<commit_before>import BaseStore from "./BaseStore";
import Immutable from "immutable";
import alt from "alt-instance";
import AccountActions from "actions/AccountActions";
import iDB from "idb-instance";
import PrivateKeyStore from "./PrivateKeyStore";
import {ChainStore, ChainValidation, FetchChain} from "bitsharesjs/es";
import {Apis} from "bitsharesjs-ws";
import AccountRefsStore from "stores/AccountRefsStore";
import AddressIndex from "stores/AddressIndex";
import SettingsStore from "stores/SettingsStore";
import ls from "common/localStorage";

let accountStorage = new ls("__graphene__");

/**
 *  This Store holds information about accounts in this wallet
 *
 */

class AccountStore extends BaseStore {
    constructor() {
        super();
        this.state = this._getInitialState();

        this.bindListeners({
            onSetCurrentAccount: AccountActions.setCurrentAccount,
            onCreateAccount: AccountActions.createAccount,
            onLinkAccount: AccountActions.linkAccount,
            onUnlinkAccount: AccountActions.unlinkAccount,
            onAccountSearch: AccountActions.accountSearch,
            // onNewPrivateKeys: [ PrivateKeyActions.loadDbData, PrivateKeyActions.addKey ]
        });
        this._export(
            "loadDbData",
            "tryToSetCurrentAccount",
            "onCreateAccount",
            "getMyAccounts",
            "isMyAccount",
            "getMyAuthorityForAccount",
            "isMyKey"
        );

        this.getMyAccounts = this.getMyAccounts.bind(this);
    }

    _getInitialState() {
        this.account_refs = null;
        this.initial_account_refs_load = true; // true until all undefined accounts are found

        return {
            update: false,
            subbed: false,
            accountsLoaded: false,
            refsLoaded: false,
            currentAccount: null,
            linkedAccounts: Immutable.Set(),
            myIgnoredAccounts: Immutable.Set(),
            unFollowedAccounts: Immutable.Set(accountStorage.get("unfollowed_accounts", [])),
            searchAccounts: Immutable.Map(),
            searchTerm: ""
        };
    }

    _addIgnoredAccount(name) {
        if (this.state.unFollowedAccounts.includes(name) && !this.state.myIgnoredAccounts.has(name)) {
            this.state.myIgnoredAccounts = this.state.myIgnoredAccounts.add(name);
        }
    }

    loadDbData() {
        let linkedAccounts = Immutable.Set().asMutable();
        let chainId = Apis.instance().chain_id;
        return new Promise((resolve, reject) => {
            iDB.load_data("linked_accounts")
            .then(data => {
                let accountPromises = data.filter(a => {
                    if (a.chainId) {
                        return a.chainId === chainId;
                    } else {
                        return true;
                    }
                }).map(a => {
                    linkedAccounts.add(a.name);
                    this._addIgnoredAccount(a.name);
                    return FetchChain("getAccount", a.name);
                });

                this.setState({
                    linkedAccounts: linkedAccounts.asImmutable(),
                    accountsLoaded: true
                });
                Promise.all(accountPromises).then(() => {
                    ChainStore.subscribe(this.chainStoreUpdate.bind(this));

                    this.setState({
                        subbed: true
                    });
                    resolve();
                }).catch(err => {
                    ChainStore.subscribe(this.chainStoreUpdate.bind(this));
                    this.setState({
                        subbed: true
                    });
                    reject(err);
                });
            }).catch(err => {
                reject(err);
            });
        });

    }

    chainStoreUpdate() {
        if(this.state.update) {
            this.setState({update: false});
        }
        this.addAccountRefs();
    }

    addAccountRefs() {
        //  Simply add them to the linkedAccounts list (no need to persist them)
        let account_refs = AccountRefsStore.getState().account_refs;
        if( ! this.initial_account_refs_load && this.account_refs === account_refs) {
            return this.setState({refsLoaded: true});
        };
        this.account_refs = account_refs;
        let pending = false;
        this.state.linkedAccounts = this.state.linkedAccounts.withMutations(linkedAccounts => {
            account_refs.forEach(id => {
                let account = ChainStore.getAccount(id);
                if (account === undefined) {
                    pending = true;
                    return;
                }
                if (account && !this.state.unFollowedAccounts.includes(account.get("name"))) {
                    linkedAccounts.add(account.get("name"));
                } else {
                    this._addIgnoredAccount(account.get("name"));
                }
            });
        });
        // console.log("AccountStore addAccountRefs linkedAccounts",this.state.linkedAccounts.size);
        this.setState({ linkedAccounts: this.state.linkedAccounts });
        this.initial_account_refs_load = pending;
        this.tryToSetCurrentAccount();
    }

    getMyAccounts() {
        if (!this.state.subbed) {
            return [];
        }

        let accounts = [];
        let needsUpdate = false;
        for(let account_name of this.state.linkedAccounts) {
            let account = ChainStore.getAccount(account_name);
            if(account === undefined) {
                // console.log(account_name, "account undefined");
                needsUpdate = true;
                continue;
            }
            if(account == null) {
                console.log("WARN: non-chain account name in linkedAccounts", account_name);
                continue;
            }
            let auth = this.getMyAuthorityForAccount(account);

            if(auth === undefined) {
                // console.log(account_name, "auth undefined");
                needsUpdate = true;
                continue;
            }

            if(auth === "full") {
                accounts.push(account_name);
            }

            // console.log("account:", account_name, "auth:", auth);

        }
        if (needsUpdate) this.state.update = true;
        // console.log("accounts:", accounts, "linkedAccounts:", this.state.linkedAccounts);
        return accounts.sort();
    }

    /**
        @todo "partial"
        @return string "none", "full", "partial" or undefined (pending a chain store lookup)
    */
    getMyAuthorityForAccount(account, recursion_count = 1) {
        if (! account) return undefined;

        let owner_authority = account.get("owner");
        let active_authority = account.get("active");

        let owner_pubkey_threshold = pubkeyThreshold(owner_authority);
        if(owner_pubkey_threshold == "full") return "full";
        let active_pubkey_threshold = pubkeyThreshold(active_authority);
        if(active_pubkey_threshold == "full") return "full";

        let owner_address_threshold = addressThreshold(owner_authority);
        if(owner_address_threshold == "full") return "full";
        let active_address_threshold = addressThreshold(active_authority);
        if(active_address_threshold == "full") return "full";

        let owner_account_threshold, active_account_threshold;

        // if (account.get("name") === "secured-x") {
        //     debugger;
        // }
        if(recursion_count < 3) {
            owner_account_threshold = this._accountThreshold(owner_authority, recursion_count);
            if ( owner_account_threshold === undefined ) return undefined;
            if(owner_account_threshold == "full") return "full";

            active_account_threshold = this._accountThreshold(active_authority, recursion_count);
            if ( active_account_threshold === undefined ) return undefined;
            if(active_account_threshold == "full") return "full";
        }

        if(
            owner_pubkey_threshold === "partial" || active_pubkey_threshold === "partial" ||
            owner_address_threshold === "partial" || active_address_threshold === "partial" ||
            owner_account_threshold === "partial" || active_account_threshold === "partial"
        ) return "partial";
        return "none";
    }

    _accountThreshold(authority, recursion_count) {
        let account_auths = authority.get("account_auths");
        if( ! account_auths.size ) return "none";

        let auths = account_auths.map(auth => {
            let account = ChainStore.getAccount(auth.get(0));
            if(account === undefined) return undefined;
            return this.getMyAuthorityForAccount(account, ++recursion_count);
        });

        let final = auths.reduce((map, auth) => {
            return map.set(auth, true);
        }, Immutable.Map());

        return final.get("full") && final.size === 1 ? "full" :
               final.get("partial") && final.size === 1 ? "partial" :
               final.get("none") && final.size === 1 ? "none" :
               final.get("full") || final.get("partial") ? "partial" :
               undefined;
    }

    isMyAccount(account) {
        let authority = this.getMyAuthorityForAccount(account);
        if( authority === undefined ) return undefined;
        return authority === "partial" || authority === "full";
    }

    onAccountSearch(payload) {
        this.state.searchTerm = payload.searchTerm;
        this.state.searchAccounts = this.state.searchAccounts.clear();
        payload.accounts.forEach(account => {
            this.state.searchAccounts = this.state.searchAccounts.withMutations(map => {
                map.set(account[1], account[0]);
            });
        });
    }

    _getCurrentAccountKey() {
        const chainId = Apis.instance().chain_id;
        return "currentAccount" + (chainId ? `_${chainId.substr(0, 8)}` : "");
    }

    tryToSetCurrentAccount() {
        const key = this._getCurrentAccountKey();
        if (accountStorage.has(key)) {
            return this.setCurrentAccount(accountStorage.get(key, null));
        }

        let {starredAccounts} = SettingsStore.getState();
        if (starredAccounts.size) {
            return this.setCurrentAccount(starredAccounts.first().name);
        }
        if (this.state.linkedAccounts.size) {
            return this.setCurrentAccount(this.state.linkedAccounts.first());
        }
    }

    setCurrentAccount(name) {
        const key = this._getCurrentAccountKey();
        if (!name) {
            this.state.currentAccount = null;
        } else {
            this.state.currentAccount = name;
        }

        accountStorage.set(key, this.state.currentAccount);
    }

    onSetCurrentAccount(name) {
        this.setCurrentAccount(name);
    }

    onCreateAccount(name_or_account) {
        let account = name_or_account;
        if (typeof account === "string") {
            account = {
                name: account
            };
        }

        if(account["toJS"])
            account = account.toJS();

        if(account.name == "" || this.state.linkedAccounts.get(account.name))
            return Promise.resolve();

        if( ! ChainValidation.is_account_name(account.name))
            throw new Error("Invalid account name: " + account.name);

        return iDB.add_to_store("linked_accounts", {
            name: account.name,
            chainId: Apis.instance().chain_id
        }).then(() => {
            console.log("[AccountStore.js] ----- Added account to store: ----->", account.name);
            this.state.linkedAccounts = this.state.linkedAccounts.add(account.name);
            if (this.state.linkedAccounts.size === 1) {
                this.setCurrentAccount(account.name);
            }
        });
    }

    onLinkAccount(name) {
        if( ! ChainValidation.is_account_name(name, true))
            throw new Error("Invalid account name: " + name);

        // Link
        iDB.add_to_store("linked_accounts", {
            name,
            chainId: Apis.instance().chain_id
        });
        this.state.linkedAccounts = this.state.linkedAccounts.add(name);

        // remove from unFollow
        this.state.unFollowedAccounts = this.state.unFollowedAccounts.delete(name);
        this.state.myIgnoredAccounts = this.state.myIgnoredAccounts.delete(name);
        accountStorage.set("unfollowed_accounts", this.state.unFollowedAccounts);

        // Update current account if only one account is linked
        if (this.state.linkedAccounts.size === 1) {
            this.setCurrentAccount(name);
        }
    }

    onUnlinkAccount(name) {
        if( ! ChainValidation.is_account_name(name, true))
            throw new Error("Invalid account name: " + name);

        // Unlink
        iDB.remove_from_store("linked_accounts", name);
        this.state.linkedAccounts = this.state.linkedAccounts.delete(name);

        // Add to unFollow
        this.state.unFollowedAccounts = this.state.unFollowedAccounts.add(name);
        this.checkAccountRefs();
        // Limit to maxEntries accounts
        let maxEntries = 50;
        if (this.state.unFollowedAccounts.size > maxEntries) {
            this.state.unFollowedAccounts = this.state.unFollowedAccounts.takeLast(maxEntries);
        }

        accountStorage.set("unfollowed_accounts", this.state.unFollowedAccounts);

        // Update current account if no accounts are linked
        if (this.state.linkedAccounts.size === 0) {
            this.setCurrentAccount(null);
        }

    }

    checkAccountRefs() {
        //  Simply add them to the linkedAccounts list (no need to persist them)
        let account_refs = AccountRefsStore.getState().account_refs;

        account_refs.forEach(id => {
            let account = ChainStore.getAccount(id);
            if (account === undefined) {
                return;
            }
            if (account) {
                this._addIgnoredAccount(account.get("name"));
            }
        });
    }

    isMyKey(key) {
        return PrivateKeyStore.hasKey(key);
    }
}

export default alt.createStore(AccountStore, "AccountStore");

// @return 3 full, 2 partial, 0 none
function pubkeyThreshold(authority) {
    let available = 0;
    let required = authority.get("weight_threshold");
    let key_auths = authority.get("key_auths");
    for (let k of key_auths) {
        if (PrivateKeyStore.hasKey(k.get(0))) {
            available += k.get(1);
        }
        if(available >= required) break;
    }
    return available >= required ? "full" : available > 0 ? "partial" : "none";
}

// @return 3 full, 2 partial, 0 none
function addressThreshold(authority) {
    let available = 0;
    let required = authority.get("weight_threshold");
    let address_auths = authority.get("address_auths");
    if( ! address_auths.size) return "none";
    let addresses = AddressIndex.getState().addresses;
    for (let k of address_auths) {
        let address = k.get(0);
        let pubkey = addresses.get(address);
        if (PrivateKeyStore.hasKey(pubkey)) {
            available += k.get(1);
        }
        if(available >= required) break;
    }
    return available >= required ? "full" : available > 0 ? "partial" : "none";
}
<commit_msg>Trigger account ref lookup immediately on AccountStore loadDbData, fixes slow Dashboard load<commit_after>import BaseStore from "./BaseStore";
import Immutable from "immutable";
import alt from "alt-instance";
import AccountActions from "actions/AccountActions";
import iDB from "idb-instance";
import PrivateKeyStore from "./PrivateKeyStore";
import {ChainStore, ChainValidation, FetchChain} from "bitsharesjs/es";
import {Apis} from "bitsharesjs-ws";
import AccountRefsStore from "stores/AccountRefsStore";
import AddressIndex from "stores/AddressIndex";
import SettingsStore from "stores/SettingsStore";
import ls from "common/localStorage";

let accountStorage = new ls("__graphene__");

/**
 *  This Store holds information about accounts in this wallet
 *
 */

class AccountStore extends BaseStore {
    constructor() {
        super();
        this.state = this._getInitialState();

        this.bindListeners({
            onSetCurrentAccount: AccountActions.setCurrentAccount,
            onCreateAccount: AccountActions.createAccount,
            onLinkAccount: AccountActions.linkAccount,
            onUnlinkAccount: AccountActions.unlinkAccount,
            onAccountSearch: AccountActions.accountSearch,
            // onNewPrivateKeys: [ PrivateKeyActions.loadDbData, PrivateKeyActions.addKey ]
        });
        this._export(
            "loadDbData",
            "tryToSetCurrentAccount",
            "onCreateAccount",
            "getMyAccounts",
            "isMyAccount",
            "getMyAuthorityForAccount",
            "isMyKey"
        );

        this.getMyAccounts = this.getMyAccounts.bind(this);
    }

    _getInitialState() {
        this.account_refs = null;
        this.initial_account_refs_load = true; // true until all undefined accounts are found

        return {
            update: false,
            subbed: false,
            accountsLoaded: false,
            refsLoaded: false,
            currentAccount: null,
            linkedAccounts: Immutable.Set(),
            myIgnoredAccounts: Immutable.Set(),
            unFollowedAccounts: Immutable.Set(accountStorage.get("unfollowed_accounts", [])),
            searchAccounts: Immutable.Map(),
            searchTerm: ""
        };
    }

    _addIgnoredAccount(name) {
        if (this.state.unFollowedAccounts.includes(name) && !this.state.myIgnoredAccounts.has(name)) {
            this.state.myIgnoredAccounts = this.state.myIgnoredAccounts.add(name);
        }
    }

    loadDbData() {
        let linkedAccounts = Immutable.Set().asMutable();
        let chainId = Apis.instance().chain_id;
        return new Promise((resolve, reject) => {
            iDB.load_data("linked_accounts")
            .then(data => {
                let accountPromises = data.filter(a => {
                    if (a.chainId) {
                        return a.chainId === chainId;
                    } else {
                        return true;
                    }
                }).map(a => {
                    linkedAccounts.add(a.name);
                    this._addIgnoredAccount(a.name);
                    return FetchChain("getAccount", a.name);
                });

                this.setState({
                    linkedAccounts: linkedAccounts.asImmutable(),
                    accountsLoaded: true
                });
                Promise.all(accountPromises).then(() => {
                    ChainStore.subscribe(this.chainStoreUpdate.bind(this));
                    this.chainStoreUpdate();
                    this.setState({
                        subbed: true
                    });
                    resolve();
                }).catch(err => {
                    ChainStore.subscribe(this.chainStoreUpdate.bind(this));
                    this.chainStoreUpdate();
                    this.setState({
                        subbed: true
                    });
                    reject(err);
                });
            }).catch(err => {
                reject(err);
            });
        });

    }

    chainStoreUpdate() {
        this.addAccountRefs();
    }

    addAccountRefs() {
        //  Simply add them to the linkedAccounts list (no need to persist them)
        let account_refs = AccountRefsStore.getState().account_refs;
        if( ! this.initial_account_refs_load && this.account_refs === account_refs) {
            return this.setState({refsLoaded: true});
        }
        this.account_refs = account_refs;
        let pending = false;
        this.state.linkedAccounts = this.state.linkedAccounts.withMutations(linkedAccounts => {
            account_refs.forEach(id => {
                let account = ChainStore.getAccount(id);
                if (account === undefined) {
                    pending = true;
                    return;
                }
                if (account && !this.state.unFollowedAccounts.includes(account.get("name"))) {
                    linkedAccounts.add(account.get("name"));
                } else {
                    this._addIgnoredAccount(account.get("name"));
                }
            });
        });
        // console.log("AccountStore addAccountRefs linkedAccounts",this.state.linkedAccounts.size);
        this.setState({ linkedAccounts: this.state.linkedAccounts });
        this.initial_account_refs_load = pending;
        this.tryToSetCurrentAccount();
    }

    getMyAccounts() {
        if (!this.state.subbed) {
            return [];
        }

        let accounts = [];
        for(let account_name of this.state.linkedAccounts) {
            let account = ChainStore.getAccount(account_name);
            if(account === undefined) {
                // console.log(account_name, "account undefined");
                continue;
            }
            if(account == null) {
                console.log("WARN: non-chain account name in linkedAccounts", account_name);
                continue;
            }
            let auth = this.getMyAuthorityForAccount(account);

            if(auth === undefined) {
                // console.log(account_name, "auth undefined");
                continue;
            }

            if(auth === "full") {
                accounts.push(account_name);
            }

            // console.log("account:", account_name, "auth:", auth);

        }
        // console.log("accounts:", accounts, "linkedAccounts:", this.state.linkedAccounts);
        return accounts.sort();
    }

    /**
        @todo "partial"
        @return string "none", "full", "partial" or undefined (pending a chain store lookup)
    */
    getMyAuthorityForAccount(account, recursion_count = 1) {
        if (! account) return undefined;

        let owner_authority = account.get("owner");
        let active_authority = account.get("active");

        let owner_pubkey_threshold = pubkeyThreshold(owner_authority);
        if(owner_pubkey_threshold == "full") return "full";
        let active_pubkey_threshold = pubkeyThreshold(active_authority);
        if(active_pubkey_threshold == "full") return "full";

        let owner_address_threshold = addressThreshold(owner_authority);
        if(owner_address_threshold == "full") return "full";
        let active_address_threshold = addressThreshold(active_authority);
        if(active_address_threshold == "full") return "full";

        let owner_account_threshold, active_account_threshold;

        // if (account.get("name") === "secured-x") {
        //     debugger;
        // }
        if(recursion_count < 3) {
            owner_account_threshold = this._accountThreshold(owner_authority, recursion_count);
            if ( owner_account_threshold === undefined ) return undefined;
            if(owner_account_threshold == "full") return "full";

            active_account_threshold = this._accountThreshold(active_authority, recursion_count);
            if ( active_account_threshold === undefined ) return undefined;
            if(active_account_threshold == "full") return "full";
        }

        if(
            owner_pubkey_threshold === "partial" || active_pubkey_threshold === "partial" ||
            owner_address_threshold === "partial" || active_address_threshold === "partial" ||
            owner_account_threshold === "partial" || active_account_threshold === "partial"
        ) return "partial";
        return "none";
    }

    _accountThreshold(authority, recursion_count) {
        let account_auths = authority.get("account_auths");
        if( ! account_auths.size ) return "none";

        let auths = account_auths.map(auth => {
            let account = ChainStore.getAccount(auth.get(0));
            if(account === undefined) return undefined;
            return this.getMyAuthorityForAccount(account, ++recursion_count);
        });

        let final = auths.reduce((map, auth) => {
            return map.set(auth, true);
        }, Immutable.Map());

        return final.get("full") && final.size === 1 ? "full" :
               final.get("partial") && final.size === 1 ? "partial" :
               final.get("none") && final.size === 1 ? "none" :
               final.get("full") || final.get("partial") ? "partial" :
               undefined;
    }

    isMyAccount(account) {
        let authority = this.getMyAuthorityForAccount(account);
        if( authority === undefined ) return undefined;
        return authority === "partial" || authority === "full";
    }

    onAccountSearch(payload) {
        this.state.searchTerm = payload.searchTerm;
        this.state.searchAccounts = this.state.searchAccounts.clear();
        payload.accounts.forEach(account => {
            this.state.searchAccounts = this.state.searchAccounts.withMutations(map => {
                map.set(account[1], account[0]);
            });
        });
    }

    _getCurrentAccountKey() {
        const chainId = Apis.instance().chain_id;
        return "currentAccount" + (chainId ? `_${chainId.substr(0, 8)}` : "");
    }

    tryToSetCurrentAccount() {
        const key = this._getCurrentAccountKey();
        if (accountStorage.has(key)) {
            return this.setCurrentAccount(accountStorage.get(key, null));
        }

        let {starredAccounts} = SettingsStore.getState();
        if (starredAccounts.size) {
            return this.setCurrentAccount(starredAccounts.first().name);
        }
        if (this.state.linkedAccounts.size) {
            return this.setCurrentAccount(this.state.linkedAccounts.first());
        }
    }

    setCurrentAccount(name) {
        const key = this._getCurrentAccountKey();
        if (!name) {
            this.state.currentAccount = null;
        } else {
            this.state.currentAccount = name;
        }

        accountStorage.set(key, this.state.currentAccount);
    }

    onSetCurrentAccount(name) {
        this.setCurrentAccount(name);
    }

    onCreateAccount(name_or_account) {
        let account = name_or_account;
        if (typeof account === "string") {
            account = {
                name: account
            };
        }

        if(account["toJS"])
            account = account.toJS();

        if(account.name == "" || this.state.linkedAccounts.get(account.name))
            return Promise.resolve();

        if( ! ChainValidation.is_account_name(account.name))
            throw new Error("Invalid account name: " + account.name);

        return iDB.add_to_store("linked_accounts", {
            name: account.name,
            chainId: Apis.instance().chain_id
        }).then(() => {
            console.log("[AccountStore.js] ----- Added account to store: ----->", account.name);
            this.state.linkedAccounts = this.state.linkedAccounts.add(account.name);
            if (this.state.linkedAccounts.size === 1) {
                this.setCurrentAccount(account.name);
            }
        });
    }

    onLinkAccount(name) {
        if( ! ChainValidation.is_account_name(name, true))
            throw new Error("Invalid account name: " + name);

        // Link
        iDB.add_to_store("linked_accounts", {
            name,
            chainId: Apis.instance().chain_id
        });
        this.state.linkedAccounts = this.state.linkedAccounts.add(name);

        // remove from unFollow
        this.state.unFollowedAccounts = this.state.unFollowedAccounts.delete(name);
        this.state.myIgnoredAccounts = this.state.myIgnoredAccounts.delete(name);
        accountStorage.set("unfollowed_accounts", this.state.unFollowedAccounts);

        // Update current account if only one account is linked
        if (this.state.linkedAccounts.size === 1) {
            this.setCurrentAccount(name);
        }
    }

    onUnlinkAccount(name) {
        if( ! ChainValidation.is_account_name(name, true))
            throw new Error("Invalid account name: " + name);

        // Unlink
        iDB.remove_from_store("linked_accounts", name);
        this.state.linkedAccounts = this.state.linkedAccounts.delete(name);

        // Add to unFollow
        this.state.unFollowedAccounts = this.state.unFollowedAccounts.add(name);
        this.checkAccountRefs();
        // Limit to maxEntries accounts
        let maxEntries = 50;
        if (this.state.unFollowedAccounts.size > maxEntries) {
            this.state.unFollowedAccounts = this.state.unFollowedAccounts.takeLast(maxEntries);
        }

        accountStorage.set("unfollowed_accounts", this.state.unFollowedAccounts);

        // Update current account if no accounts are linked
        if (this.state.linkedAccounts.size === 0) {
            this.setCurrentAccount(null);
        }

    }

    checkAccountRefs() {
        //  Simply add them to the linkedAccounts list (no need to persist them)
        let account_refs = AccountRefsStore.getState().account_refs;

        account_refs.forEach(id => {
            let account = ChainStore.getAccount(id);
            if (account === undefined) {
                return;
            }
            if (account) {
                this._addIgnoredAccount(account.get("name"));
            }
        });
    }

    isMyKey(key) {
        return PrivateKeyStore.hasKey(key);
    }
}

export default alt.createStore(AccountStore, "AccountStore");

// @return 3 full, 2 partial, 0 none
function pubkeyThreshold(authority) {
    let available = 0;
    let required = authority.get("weight_threshold");
    let key_auths = authority.get("key_auths");
    for (let k of key_auths) {
        if (PrivateKeyStore.hasKey(k.get(0))) {
            available += k.get(1);
        }
        if(available >= required) break;
    }
    return available >= required ? "full" : available > 0 ? "partial" : "none";
}

// @return 3 full, 2 partial, 0 none
function addressThreshold(authority) {
    let available = 0;
    let required = authority.get("weight_threshold");
    let address_auths = authority.get("address_auths");
    if( ! address_auths.size) return "none";
    let addresses = AddressIndex.getState().addresses;
    for (let k of address_auths) {
        let address = k.get(0);
        let pubkey = addresses.get(address);
        if (PrivateKeyStore.hasKey(pubkey)) {
            available += k.get(1);
        }
        if(available >= required) break;
    }
    return available >= required ? "full" : available > 0 ? "partial" : "none";
}
<|endoftext|>


<commit_before>// TODO detect AND/OR and convert to quotation
import { isLeaf, isBranch, isClose, toVNodeStream } from "l3n";
import { parse } from "./parser";
import { prefixAndName, normalizeName } from "./compiler-util";
import { ReplaySubject, isObservable } from "rxjs";
import { reduce, map, mergeMap, mergeAll } from "rxjs/operators";
import { $_, papplyAny } from "./papply";

//const compose = (...fns) => fns.reduce((f, g) => (...args) => f(g(...args)));

//const ifRe = /^(n:)?if$/;
//const andRe = /^(n:)?and$/;
//const orRe = /^(n:)?or$/;

const isCallNode = node => node && node.type == 14;
const isQuotNode = node => node && node.type == 15;
//const isSeqNode = node => isCallNode(node) && node.name === "";
const isModuleNode = node => isCallNode(node) && node.name === "$*";
const isImportNode = node => isCallNode(node) && node.name === "$<";
const isExportNode = node => isCallNode(node) && node.name === "$>";
const isVarNode = node => isCallNode(node) && node.name === "$";
const isPartialNode = node => isCallNode(node) && node.name == "$_";
//const isRecursivePartialNode = node => isCallNode(node) && node.name == "$.";
//const isIfNode = node => isCallNode(node) && ifRe.test(node.name);
//const isAndNode = node => isCallNode(node) && andRe.test(node.name);
//const isOrNode = node => isCallNode(node) && orRe.test(node.name);

const isCall = x => x && x instanceof Call;
const isQuot = x => x && x instanceof Context;
const isVar = x => x && x instanceof Var;
//const isParam = x => x && x instanceof Var && x.isParam;
const NOOP = {__noop:true};

// TODO module namespace
class Context {
	constructor(props = {}) {
		this.modules = props.modules || {};
		this.stack = [];
		this.length = 0;
		this.scope = {};
		this.refsToResolve = {};
	}
	addVar(count){
		let v;
		if(count > 1) {
			// assigment
			v = new Var(this,1,count);
		} else {
			const index = this.stack.lastItem;
			if(typeof index == "number") {
				this.length++;
				v = new Var(this,2,1);
			} else {
				v = new Var(this,3,1);
			}
		}
		this.append(v);
	}
	addModule(length) {
		const ref = prefix => {
			this.modules[prefix] = {};
			return NOOP;
		};
		this.append(new Call("module",length,ref));
	}
	addImport(length) {
		const ref = (prefix,ns,loc) => {
			if(length == 2) loc = ns;
			this.refsToResolve[prefix] = {};
			// TODO merge properly
			const self = this;
			return parse("../raddled/"+loc+".rdl").pipe(toVNodeStream,x => compile(x,this),mergeMap(cx => {
				const ret = cx.apply();
				//console.log(ret);
				return ret;
			}),map(() => {
				const module = self.modules[prefix];
				Object.entries(self.refsToResolve[prefix]).forEach(([k,v]) => {
					v.next(module[k]);
					v.complete();
				});
				return self;
			}));
		};
		this.append(new Call("import",length,ref));
	}
	addExport(length){
		const ref = (qname,type,body) => {
			const { prefix, name } = normalizeName(qname);
			const module = this.modules[prefix];
			if(!module) throw new Error(`Module "${prefix}" has not been formally declared`);
			if(isQuot(body)) {
				// add an object that serves as a proxy (i.e. can be applied)
				if(!module[name]) module[name] = {
					apply(self,args) {
						const ref = this[args.length];
						if(!ref) throw new Error(`Incorrect number of parameters for ${qname}, received ${args.length}, have ${Object.keys(this)}`);
						return ref.apply(self,args);
					}
				};
				// TODO add type
				module[name][body.length] = body;
			} else {
				module[name] = body;
			}
			return NOOP;
		};
		this.append(new Call("export",length,length == 2 ? papplyAny(ref,$_,() => {},$_) : ref));
	}
	getRef(qname) {
		const modules = this.modules;
		const { prefix, name } = normalizeName(qname,"n");
		if(modules.hasOwnProperty(prefix)) {
			return modules[prefix][name];
		} else {
			//console.log("no module found",prefix);
			// deferring module entry as a ReplaySubject
			const rts = this.refsToResolve;
			if(!rts.hasOwnProperty(prefix)) throw new Error(`Import of prefix ${prefix} not yet encountered`);
			const def = new ReplaySubject();
			if(!rts[prefix].hasOwnProperty(name)) rts[prefix][name] = def;
			return def;
		}
	}
	isBoundQname(qname) {
		const { prefix } = prefixAndName(qname);
		return this.modules.hasOwnProperty(prefix) || this.refsToResolve.hasOwnProperty(prefix);
	}
	getVarRef(qname) {
		// ignore NS to see if we have prefix
		if(this.isBoundQname(qname)) return this.getRef(qname);
		return this.scope[qname];
	}
	setVarRef(qname,type,value) {
		this.scope[qname] = value;
		return NOOP;
	}
	addCall(qname,length) {
		//if(!qname) console.trace(qname,length);
		this.append(new Call(qname,length));
	}
	addDatum(type,value) {
		if(type !== 8) this.append(value);
	}
	append(item){
		this.stack.push(item);
	}
	apply(self,args){
		// TODO first arg is external?
		// evaluation stack
		var stack = [];
		var ret = new ReplaySubject();
		const len = this.stack.length;
		//for(let i = 0, len = this.stack.length; i < len; i++) {
		const next = (i) => {
			if(i == len) {
				const last = stack.pop();
				if(last instanceof ReplaySubject) {
					last.subscribe(ret);
				} else {
					ret.next(last);
					ret.complete();
				}
				return;
			}
			const last = this.stack[i];
			if(isQuot(last)) {
				stack.push(last);
				next(i+1);
			} else if(isCall(last)) {
				const len = last.length;
				const _args = stack.splice(-len,len);
				// TODO original stack as Observable
				// if last.ref is Subject, evaluation must be deferred:
				// take next from the stack when ref is resolved
				last.apply(this,_args).subscribe(ret => {
					if(isObservable(ret)) {
						ret.subscribe({
							complete(){
								next(i+1);
							}
						});
					} else {
						if(ret !== NOOP) stack.push(ret);
						next(i+1);
					}
				});
			} else if(isVar(last)) {
				if(last.isParam) {
					// pop the index, push the arg
					const index = stack.pop();
					stack.push(args[index - 1]);
					next(i+1);
				} else {
					// treat vars as Calls
					const len = last.length;
					const _args = stack.splice(-len,len);
					const ref = last.apply(self,_args);
					if(last.isAssig) {
						next(i+1);
					} else {
						if(ref instanceof ReplaySubject) {
							ref.subscribe((x) => {
								stack.push(x);
								next(i+1);
							});
						} else {
							stack.push(ref);
							next(i+1);
						}
					}
				}
			} else {
				stack.push(last);
				next(i+1);
			}
		};
		next(0);
		return ret;
	}
}
class Var {
	constructor(cx,type,length) {
		this.cx = cx;
		// 1. assignment
		// 2. param
		// 3. var
		this.isAssig = type == 1;
		this.isParam = type == 2;
		this.length = length;
	}
	apply(self,args) {
		if(this.isAssig) {
			const hasType = args.length > 2;
			return this.cx.setVarRef(args[0], hasType ? args[1] : null, hasType ? args[2] : args[1]);
		} else {
			return this.cx.getVarRef(args[0]);
		}
	}
}

class Call {
	constructor(qname,length,ref) {
		this.qname = qname;
		this.length = length;
		this.ref = ref;
	}
	apply(cx,args) {
		const ref = this.ref || cx.getRef(this.qname,this.length);
		if(ref instanceof ReplaySubject) {
			return ref.pipe(map(ref => ref.apply(this,args)));
		} else {
			const ret = ref.apply(this,args);
			let sub = new ReplaySubject();
			sub.next(ret);
			sub.complete();
			return sub;
		}
	}
}

export const compile = cx => o => {
	cx = new Context(cx);
	const quots = [cx];
	// this is a reduction into a single result
	return o.pipe(reduce((cx,node) => {
		const type = node.type;
		if(isClose(type)) {
			const refNode = node.node;
			if(isQuotNode(refNode)) {
				const dest = quots.pop();
				quots.lastItem.append(dest);
			} else {
				const target = quots.lastItem;
				if(isVarNode(refNode)) {
					// var or param
					const count = refNode.count();
					// TODO add default prefix
					// NOTE we know the first child on the node, so we can read the name there
					// HOWEVER this goes against the pure stack-based implementation
					if(count > 1 && refNode.depth == 1 && target.isBoundQname(refNode.first())) {
						// private top-level declaration, simply add as export
						target.addExport(count);
					} else {
						target.addVar(count);
					}
				} else if(isModuleNode(refNode)) {
					// handle module insertion
					target.addModule(refNode.count());
				} else if(isImportNode(refNode)) {
					// handle import
					target.addImport(refNode.count());
				} else if(isExportNode(refNode)) {
					// handle export
					// expect type to be compiled to a single Call
					target.addExport(refNode.count());
				} else if(isPartialNode(refNode)) {
					// partial any
					target.append($_);
				} else if(isCallNode(refNode)){
					// handle call
					target.addCall(refNode.name,refNode.count());
				}
			}
		} else if(isLeaf(type)) {
			quots.lastItem.addDatum(node.type,node.value);
		} else if(isBranch(type) && isQuotNode(node)) {
			// add quot to scope stack
			quots.push(new Context(cx));
		}
		return cx;
	},cx));
};

export const run = (o,cx) => compile(o,cx).pipe(map(cx => cx.apply()),mergeAll());
<commit_msg>added typetest, fixed quot, added prepare+run<commit_after>// TODO detect AND/OR and convert to quotation
import { isLeaf, isBranch, isClose, toVNodeStream } from "l3n";
import { parse, parseString } from "./parser";
import { prefixAndName, normalizeName } from "./compiler-util";
import { Observable, isObservable, pipe } from "rxjs";
import { reduce, map, switchMap, mergeMap } from "rxjs/operators";
import { $_ /*, papplyAny*/ } from "./papply";

//const compose = (...fns) => fns.reduce((f, g) => (...args) => f(g(...args)));

//const ifRe = /^(n:)?if$/;
//const andRe = /^(n:)?and$/;
//const orRe = /^(n:)?or$/;
//const andOrRe = /^(n\.)?(and|or)$/;

const isCallNode = node => node && node.type == 14;
const isQuotNode = node => node && node.type == 15;
//const isSeqNode = node => isCallNode(node) && node.name === "";
const isModuleNode = node => isCallNode(node) && node.name === "$*";
const isImportNode = node => isCallNode(node) && node.name === "$<";
const isExportNode = node => isCallNode(node) && node.name === "$>";
const isVarNode = node => isCallNode(node) && node.name === "$";
const isPartialNode = node => isCallNode(node) && node.name == "$_";
//const isRecursivePartialNode = node => isCallNode(node) && node.name == "$.";
//const isIfNode = node => isCallNode(node) && ifRe.test(node.name);
//const isAndNode = node => isCallNode(node) && andRe.test(node.name);
//const isOrNode = node => isCallNode(node) && orRe.test(node.name);

const isCall = x => x && x instanceof Call;
const isQuot = x => x && x instanceof Quot;
const isVar = x => x && x instanceof Var;
//const isParam = x => x && x instanceof Var && x.isParam;
const NOOP = {__noop:true};

// TODO module namespace
class Context {
	constructor(props = {}) {
		this.core = props.core;
		this.modules = props.modules || {};
		this.stack = [];
		this.length = 0;
		this.scope = {};
		this.refsToResolve = {};
	}
	addVar(count){
		let v;
		if(count > 1) {
			// assigment
			v = new Var(this,1,count);
		} else {
			const index = this.stack.lastItem;
			if(typeof index == "number") {
				this.length++;
				v = new Var(this,2,1);
			} else {
				v = new Var(this,3,1);
			}
		}
		this.append(v);
	}
	addModule(length) {
		const ref = prefix => {
			this.modules[prefix] = {};
			return NOOP;
		};
		this.append(new Call("module",length,ref));
	}
	addImport(length) {
		const ref = (prefix,ns,loc) => {
			if(length == 2) loc = ns;
			this.refsToResolve[prefix] = {};
			// TODO merge properly
			const cx = this;
			return run("../raddled/"+loc+".rdl")(cx).pipe(map(() => {
				const module = cx.modules[prefix];
				Object.entries(cx.refsToResolve[prefix]).forEach(([k,v]) => {
					v.next(module[k]);
					v.complete();
				});
				return cx;
			}));
		};
		this.append(new Call("import",length,ref));
	}
	addExport(length){
		const ref = (qname,type,body) => {
			const { prefix, name } = normalizeName(qname);
			const module = this.modules[prefix];
			if(!module) throw new Error(`Module "${prefix}" has not been formally declared`);
			if(body === undefined) {
				// bind in core
				body = this.core[name];
				if(body) module[name] = type(body);
			} else if(isQuot(body)) {
				// add a function that serves as a proxy (i.e. can be applied)
				if(!module[name]) {
					module[name] = {
						apply(self,args) {
							const ref = this[args.length];
							if(!ref) throw new Error(`Incorrect number of parameters for ${qname}, received ${args.length}, have ${Object.keys(this)}`);
							return type(ref.call.bind(ref,self))(...args);
						}
					};
				}
				module[name][body.length] = body;
			} else {
				module[name] = type(body);
			}
			// perhaps we should just return the export / thing itself
			return NOOP;
		};
		this.append(new Call("export",length,ref));
	}
	getRef(qname) {
		const modules = this.modules;
		const core = this.core;
		const { prefix, name } = normalizeName(qname,"n");
		if(modules.hasOwnProperty(prefix)) {
			const ref = modules[prefix][name];
			if(ref) return ref;
			if(prefix === "n" && core[name]) return core[name];
			throw new Error(`Could not resolve ${name} in module ${prefix}`);
		} else {
			throw new Error("no module found: "+prefix);
		}
	}
	isBoundQname(qname) {
		const { prefix } = prefixAndName(qname);
		return this.modules.hasOwnProperty(prefix) || this.refsToResolve.hasOwnProperty(prefix);
	}
	getVarRef(qname) {
		// ignore NS to see if we have prefix
		if(this.isBoundQname(qname)) return this.getRef(qname);
		return this.scope[qname];
	}
	setVarRef(qname,type,value) {
		this.scope[qname] = value;
		return NOOP;
	}
	addCall(qname,length,isDef) {
		//if(!qname) console.trace(qname,length);
		this.append(new Call(qname,length,undefined,isDef));
	}
	addDatum(type,value) {
		if(type !== 8) this.append(value);
	}
	append(item){
		this.stack.push(item);
	}
	apply(self,args){
		// TODO
		// - first arg is external?
		// - prevent recursion
		// - prevent type checks: just use method for stack/next on each type
		// evaluation stack
		var stack = [];
		const len = this.stack.length;
		//for(let i = 0, len = this.stack.length; i < len; i++) {
		const next = (i,$o) => {
			if(i === len) {
				const last = stack.pop();
				if($o) {
					$o.next(last);
					$o.complete();
					return $o;
				}
				return last;
			}
			const last = this.stack[i];
			if(isQuot(last)) {
				stack.push(last.call.bind(last,this));
				return next(i+1,$o);
			} else if(isCall(last)) {
				const len = last.length;
				if(stack.length < len) throw new Error("Stack underflow");
				const _args = stack.splice(-len,len);
				const ret = last.apply(this,_args);
				if(last.qname == "import") {
					ret.subscribe({
						complete(){
							next(i+1,$o);
						}
					});
				} else {
					if(ret !== NOOP) stack.push(ret);
					return next(i+1,$o);
				}
			} else if(isVar(last)) {
				if(last.isParam) {
					// pop the index, push the arg
					const index = stack.pop();
					stack.push(args[index - 1]);
					return next(i+1,$o);
				} else {
					// treat vars as Calls
					const len = last.length;
					const _args = stack.splice(-len,len);
					const ref = last.apply(self,_args);
					if(!last.isAssig) {
						stack.push(ref);
					} else {
						stack.push(null);
					}
					return next(i+1,$o);
				}
			} else {
				stack.push(last);
				return next(i+1,$o);
			}
		};
		if(isQuot(this)) {
			return next(0);
		} else {
			return Observable.create($o => {
				next(0,$o);
			});
		}
	}
	call(self,...args) {
		return this.apply(self,args);
	}
}
class Var {
	constructor(cx,type,length) {
		this.cx = cx;
		// 1. assignment
		// 2. param
		// 3. var
		this.isAssig = type == 1;
		this.isParam = type == 2;
		this.length = length;
	}
	apply(self,args) {
		if(this.isAssig) {
			const hasType = args.length > 2;
			return this.cx.setVarRef(args[0], hasType ? args[1] : null, hasType ? args[2] : args[1]);
		} else {
			return this.cx.getVarRef(args[0]);
		}
	}
}

class Quot extends Context {
}

class Call {
	constructor(qname,length,ref,isDef) {
		this.qname = qname;
		this.length = length;
		this.ref = ref;
		this.isDef = isDef;
	}
	apply(cx,args) {
		const ref = this.ref || cx.getRef(this.qname,this.length);
		// TODO generalize...
		if(this.isDef) {
			args.unshift(this.isDef);
		}
		return ref.apply(this,args);
	}
}

export const prepare = (core,prefix="n",path="../raddled/") => {
	// pre-compile core
	core.jsArray = (...a) => a;
	const cx = new Context({core:core,modules:{local:{}}});
	return run(path+prefix+".rdl")(cx).pipe(map(() => cx));
};

export const compile = cx => o => {
	cx = new Context(cx);
	const quots = [cx];
	// this is a reduction into a single result
	return o.pipe(reduce((cx,node) => {
		const type = node.type;
		if(isClose(type)) {
			const refNode = node.node;
			if(isQuotNode(refNode)) {
				const dest = quots.pop();
				quots.lastItem.append(dest);
			} else {
				const target = quots.lastItem;
				if(isVarNode(refNode)) {
					// var or param
					const count = refNode.count();
					// TODO add default prefix
					// NOTE we know the first child on the node, so we can read the name there
					// HOWEVER this goes against the pure stack-based implementation
					if(count > 1 && refNode.depth == 1 && target.isBoundQname(refNode.first())) {
						// private top-level declaration, simply add as export
						target.addExport(count);
					} else {
						target.addVar(count);
					}
				} else if(isModuleNode(refNode)) {
					// handle module insertion
					target.addModule(refNode.count());
				} else if(isImportNode(refNode)) {
					// handle import
					target.addImport(refNode.count());
				} else if(isExportNode(refNode)) {
					// handle export
					// expect type to be compiled to a single Call
					target.addExport(refNode.count());
				} else if(isPartialNode(refNode)) {
					// partial any
					target.append($_);
				} else if(isCallNode(refNode)){
					// handle call
					let name = refNode.name;
					let isDef;
					// TODO generalize
					// Use array and seq indifferently
					// and always apply interop higher-order functions.
					// Functions from implementation provide seqs
					// while inline stuff is just arrays
					if(name == "function") {
						name = "def";
						isDef = refNode.parent.first();
						if(typeof isDef !== "string") isDef = "_";
					} else if(name == "") {
						if(refNode.parent.name == "function") {
							name = "jsArray";
						} else {
							name = "seq";
						}
					}
					target.addCall(name,refNode.count(),isDef);
				}
			}
		} else if(isLeaf(type)) {
			quots.lastItem.addDatum(node.type,node.value);
		} else if(isBranch(type) && isQuotNode(node)) {
			// add quot to scope stack
			quots.push(new Quot(cx));
		}
		return cx;
	},cx));
};

const runnable = cx => pipe(toVNodeStream,compile(cx),switchMap(cx => cx.apply()),mergeMap(x => isObservable(x) ? x : [x]));
export const run = str => cx => runnable(cx)(parse(str));
export const runString = str => cx => runnable(cx)(parseString(str));
<|endoftext|>


<commit_before>exports.Compiler = function() {
  this.interpreter;
}

exports.Compiler.prototype.compile = function(source) {
  this.interpreter = new Interpreter();
  return this.interpreter.generate(this.parse(source));
}

exports.Compiler.prototype.parse = function(source) {
  return parser.parse(source);
}<commit_msg>initialized a variable<commit_after>exports.Compiler = function() {
  this.interpreter = undefined;
}

exports.Compiler.prototype.compile = function(source) {
  this.interpreter = new Interpreter();
  return this.interpreter.generate(this.parse(source));
}

exports.Compiler.prototype.parse = function(source) {
  return parser.parse(source);
}<|endoftext|>


<commit_before>// This script should be run when the user logs in.
// E.g. Trigger on "Welcome to Genesis".

window.Tasks = function () {
  var list = [];
  this.resume = function () {
    var fn = list.pop();
    if (!fn) return false;
    fn();
    return true;
  };
  this.clear = function () {
    list = [];
  };
  this.push = function (tasks) {
    tasks = tasks || [];
    while (tasks.length > 0) {
      var fn = tasks.pop();
      if (!fn) continue;
      list.push(fn);
    }
  };
};

window.Commands = function (gwc) {
  var send = gwc.connection.send;

  // Movement
  this.north = function () { send('north'); };
  this.south = function () { send('south'); };
  this.east =  function () { send('east'); };
  this.west =  function () { send('west'); };
  this.northeast = function () { send('northeast'); };
  this.northwest = function () { send('northwest'); };
  this.southeast = function () { send('southeast'); };
  this.southwest = function () { send('southwest'); };

  // Battle
  this.kill = function (target) { return function () { send('kill ' + target); }; };
};

window.Player = function (gwc) {
  var tasks = new Tasks();
  var cmds = new Commands(gwc);

  this.resume = tasks.resume;
  this.abort = tasks.clear;
  this.todo = tasks.push;

  this.move = {
    n: cmds.north,
    s: cmds.south,
    e:  cmds.east,
    w:  cmds.west,
    ne: cmds.northeast,
    nw: cmds.northwest,
    se: cmds.southeast,
    sw: cmds.southwest
  };

  this.kill = cmds.kill;

  this.hunt = function (target) {
    var attack = this.kill(target);
    return {
      here: attack,
      n: function () { this.n(); attack(); },
      s: function () { this.s(); attack(); },
      e: function () { this.e(); attack(); },
      w: function () { this.w(); attack(); },
      ne: function () { this.ne(); attack(); },
      nw: function () { this.nw(); attack(); },
      se: function () { this.se(); attack(); },
      sw: function () { this.sw(); attack(); }
    };
  }.bind(this);
};

window.me = new Player(gwc);
console.log('Initialized');
<commit_msg>Implement Task.repeat<commit_after>// This script should be run when the user logs in.
// E.g. Trigger on "Welcome to Genesis".

var Tasks = function () {
  var list = [];
  this.resume = function () {
    var fn = list.pop();
    if (!fn) return false;
    fn.apply(null, arguments);
    return true;
  };
  this.clear = function () {
    list = [];
  };
  this.pushOne = function (task) {
    if (!task) return;
    list.push(task);
  }
  this.push = function (tasks) {
    tasks = tasks || [];
    while (tasks.length > 0) {
      this.pushOne(tasks.pop());
    }
  };
  this.repeat = function (name, task) {
    var stopHint = name? "stop " + name : "stop";
    var repeatingTask = function (hint) {
      if (stopHint === hint) return this.resume.apply(this, arguments);
      this.pushOne(repeatingTask);
      return task.apply(null, arguments);
    };
    this.pushOne(repeatingTask);
  };
};

var Commands = function (gwc) {
  var send = function(cmd) {
    gwc.connection.send(cmd);
    return true;
  };

  // Movement
  this.north = function () { return send('north'); };
  this.south = function () { return send('south'); };
  this.east =  function () { return send('east'); };
  this.west =  function () { return send('west'); };
  this.northeast = function () { return send('northeast'); };
  this.northwest = function () { return send('northwest'); };
  this.southeast = function () { return send('southeast'); };
  this.southwest = function () { return send('southwest'); };

  // Battle
  this.kill = function (target) {
    return function () { return send('kill ' + target); };
  };
};

var Player = function (gwc) {
  var tasks = new Tasks();
  var cmds = new Commands(gwc);

  this.resume = tasks.resume;
  this.abort = tasks.clear;
  this.todo = tasks.push;

  this.move = {
    n: cmds.north,
    s: cmds.south,
    e:  cmds.east,
    w:  cmds.west,
    ne: cmds.northeast,
    nw: cmds.northwest,
    se: cmds.southeast,
    sw: cmds.southwest
  };

  this.moveAnd = function (action) {
    var move = this.move;
    var executeAfter = function (movement) {
      return function () { movement(); return action(); };
    };
    return {
      here: action,
      n: executeAfter(move.n),
      s: executeAfter(move.s),
      e: executeAfter(move.e),
      w: executeAfter(move.w),
      ne: executeAfter(move.ne),
      nw: executeAfter(move.nw),
      se: executeAfter(move.se),
      sw: executeAfter(move.sw)
    };
  };

  this.kill = cmds.kill;
  this.hunt = function (target) {
    return this.moveAnd(this.kill(target));
  };
};

window.me = new Player(gwc);
console.log('Initialized');
<|endoftext|>


<commit_before>/*******
*
* Globale Variables
*
*******/
var Beagle;

(function(){

	"use strict";

	/*******
	*
	* Private Variables
	*
	*******/
	var schemas = {};

	/*******
	*
	* Error Messages
	*
	*******/

	var errorMsg = {

		nolocalstorage: "your browser doesn't support localstorage",
		schemaUndefined: "you have to defined a schema: Beagle.schema(schemaname:string, schemafields:array)",
		schemaNameEmpty: "you have to define a name for your schema",
		collectionNameUndefined: "collection name dont exist",
		collectionNotExist: "Collection dont exist",
		indexUndefined: "Index is undefinded",
		notInSchema: "attribute not in schema define",
		viewCollection: "your View need a collection",
		noObject: "your view need an object as parameter"
	}

	/*******
	*
	* Intialisierung nach dem Fassade Entwurfsmuster
	*
	*******/

	var Initialize = function(){

		if(!checkSystem()){

			throw	errorMsg.nolocalstorage;

		}

		var methods = {

			collections: function(){
				return Database().collections();
			},

			empty: function(collectionName){

				return Database().empty(collectionName);

			},

			view: function(init){

				return View(init);

			},

			schema: function(collectionName, schemaFields){

				if( (collectionName !== undefined && typeof collectionName === "string" ) && (schemaFields !== undefined &&  schemaFields[0] !== undefined ) ){

					var sn = collectionName;
					var sf = schemaFields;

					if(sn.length === 0){
						throw errorMsg.schemaNameEmpty;
					}

					sn = conformSchemaName(sn);

					return Schema().schema(sn, sf);

				}else{

					throw errorMsg.schemaUndefined;

				}

			}
		}

		methods = merge(methods, Collection());
		methods = merge(methods, Observer());

		return methods

	}

	/*******
	*
	* System check funtion
	*
	*******/

	function checkSystem(){

		if(window.localStorage === undefined){

			return false;
		}

		return true;

	}

	/*******
	*
	* Def. Collection
	*
	*******/

	var Collection = function(){

		function checkCollectionName(collectionName){

			if( (collectionName !== undefined && typeof collectionName === "string") && ( Database().existCollection(collectionName) === true ) ){

				return true;

			}else{

				return false;

			}

		}

		function isAttr(collectionName, attr){

			var _schemas = makeObject(schemas[collectionName]);

			for(var key in attr){

				if(_schemas[key] === undefined){
					return false;
				}

			}

			return true;
		}

		function pushHandler(model, modify){

			var newModel = model;

			for(var key in modify){

				if(newModel[key] instanceof Array){

					newModel[key].push(modify[key]);

				}

			}

			return newModel;

		}

		function pullHandler(model, modify){

			var newModel = model;

			for(var key in modify){

				if(newModel[key] instanceof Array){

					var index = newModel[key].indexOf(modify[key]);
					if (index > -1) {
						 newModel[key].splice(index, 1);
					}

				}

			}

			return newModel;

		}

		function setHandler(model, modify){

			var newModel = model;

			for(var mod in modify){

				newModel[mod] = modify[mod]

			}

			return newModel;

		}

		function not(a,b){
			if(a !== b){
				return true;
			}else{
				return false;
			}
		}

		function is(a,b){
			if(a === b){
				return true;
			}else{
				return false;
			}
		}

		function gt(a,b){
			if(parseInt(a) > parseInt(b)){
				return true;
			}else{
				return false;
			}
		}

		function gte(a,b){
			if(parseInt(a) >= parseInt(b)){
				return true;
			}else{
				return false;
			}
		}

		function lt(a,b){
			if(parseInt(a) < parseInt(b)){
				return true;
			}else{
				return false;
			}
		}

		function lte(a,b){
			if(parseInt(a) <= parseInt(b)){
				return true;
			}else{
				return false;
			}
		}

		function has(a, b){

			if(a instanceof Array){

				var ctl = false;

				each(a, function(c){

					if(b === c){

						ctl = true;

					};

				});

				return ctl;

			}
		}

		return {

			add: function(collectionName, model){

				if(!checkCollectionName(collectionName)){

					throw errorMsg.collectionNameUndefined;

				}

				var fields = schemas[collectionName];

				var createModel = {

					index: createId()

				}

				each(fields, function(field){

					createModel[field] = model[field]

				});

				Database().addModel(collectionName, createModel);

				observe('add', createModel);

				return createModel;

			},

			removeByIndex: function(collectionName, id){

				if(!checkCollectionName(collectionName)){

					throw errorMsg.collectionNameUndefined;

				}

				var collections = Database().getCollection(collectionName);
				var deleted;

				if(collections !== undefined){

					deleted = collections[id];

					delete collections[id];

					Database().save(collectionName, collections);
					observe('remove', deleted);

				}

			},

			findByIndex: function(collectionName, id){

				if(id === undefined){

					throw errorMsg.indexUndefined;

				}

				if(!checkCollectionName(collectionName)){

					throw errorMsg.collectionNameUndefined;

				}

				var collections = Database().getCollection(collectionName);

				return collections[id];

			},

			findIndex: function(collectionName, attr, _collection){

				var operator;
				var fieldAttr;

				for(var operator in attr){

					operator = operator;
					fieldAttr = attr[operator];

				}

				var collections = _collection !== undefined ? _collection : Database().getCollection(collectionName);

				if(!isAttr(collectionName, fieldAttr)){

					throw errorMsg.notInSchema;

				}

				var search = [];

				for(var key in fieldAttr){

					search.push(key);
					search.push(fieldAttr[key]);

				}

				var results = [];

				for(var model in collections){

					var c = false;

					switch(operator){

						case 'is':
							c = is(collections[model][search[0]], search[1]);
						break;
						case 'not':
							c = not(collections[model][search[0]], search[1]);
						break;
						case 'gt':
							c = gt(collections[model][search[0]], search[1])
						break;
						case 'gte':
							c = gte(collections[model][search[0]], search[1])
						break;
						case 'lt':
							c = lt(collections[model][search[0]], search[1])
						break;
						case 'lte':
							c = lte(collections[model][search[0]], search[1])
						break;
						case 'has':
							c = has(collections[model][search[0]], search[1])
						break;
					}

					if(c){
						results.push(model);
					}

				}

				return results;

			},

			find: function(collectionName, attr, _collection){

				var indizes = this.findIndex(collectionName, attr, _collection);
				var collections = Database().getCollection(collectionName);
				var results = [];

				each(indizes, function(index){

					results.push(collections[index]);

				});

				return Cursor(results, collectionName);

			},

			findAll: function(collectionName){

				if(!checkCollectionName(collectionName)){

					throw errorMsg.collectionNameUndefined;

				}
				var results = [];
				var collections = Database().getCollection(collectionName);
				for(var model in collections){
					results.push(collections[model]);
				}
				return Cursor(results, collectionName);
			},

			remove: function(collectionName, attr){

				var indizes = this.findIndex(collectionName, attr);
				var collections = Database().getCollection(collectionName);
				var self = this;
				var deleted = [];

				each(indizes, function(index){

					deleted.push(collections[index]);
					self.removeByIndex(collectionName, index);

				});

				return Cursor(deleted, collectionName);

			},

			update: function(collectionName, attr, modifiers){

				var indizes = this.findIndex(collectionName, attr);
				var self = this;
				var updated = []

				each(indizes, function(index){

					self.updateByIndex(collectionName, index, modifiers);

				});

				var collections = Database().getCollection(collectionName);

				each(indizes, function(index){

					updated.push(collections[index]);

				});

				return Cursor(updated, collectionName);
			},

			updateByIndex: function(collectionName, id, modifiers){

				if(!checkCollectionName(collectionName)){

					throw errorMsg.collectionNameUndefined;

				}

				if(id === undefined){

					throw errorMsg.indexUndefined;

				}

				var model = Database().getCollection(collectionName)[id];

				for(var mod in modifiers){

					var modify = modifiers[mod];

					if(!isAttr(collectionName, modifiers[mod])){

						throw errorMsg.notInSchema;

					}

					var model;

					switch(mod){

						case 'set':
							model = setHandler(model, modify);
						break;

						case 'push':
							model = pushHandler(model, modify);
						break;

						case 'pull':
							model = pullHandler(model, modify);
						break;

					}

				}

				var collections = Database().getCollection(collectionName);
				collections[model.index] = model;

				Database().save(collectionName, collections);

				observe('update', updated);

			}

		}

	}

	/*******
	*
	* Def. Cursor Object
	*
	*******/

	var Cursor = function(collection, collectionName){

		var collection = collection;
		var currSchema = collectionName;

		return {

			count: function(){

				return collection.length;

			},

			fetch: function(){

				return collection;

			},

			where: function(attr){

				var modCollection = setIndizes(collection);
				var results = Collection().find(currSchema, attr, modCollection);
				return results;

			},

			limit: function(skip,limit){

				return collection.splice(skip, limit);

			}

		}

	}

	/*******
	*
	* Def. Schema Object
	*
	*******/

	var Schema = function(){

		return {

			schema: function(collectionName, schemaFields){

				schemas[collectionName] = schemaFields;

				if(Database().createCollection(collectionName)){

					schemas[collectionName] = schemaFields;
					return true;

				};

				return false;

			}

		}

	}

	/*******
	*
	* Def. Observer Object
	*
	*******/

	var listener = {
		update: [],
		remove: [],
		add: []
	}

	function observe(listen, obj){

		each(listener[listen], function(fn){

			fn(obj);

		});

	}

	var Observer = function(){

		return {

			on: function(listener_key, callback){

				listener[listener_key].push(callback);

			}

		}

	}

	/*******
	*
	* Localstorage API
	*
	*******/

	var Database = function(){

		return {

			empty: function(collectionName){

				if( !this.existCollection(collectionName) ){
					throw errorMsg.collectionNotExist;
				}

				this.save(collectionName, {});
				return true;

			},

			collections: function(){

				var all =  {};

				for(var schema in schemas){

					all[schema] =[];

					var collection = this.getCollection(schema);

					for(var model in collection){

						all[schema].push(collection[model]);

					}

				}

				return all;

			},

			addModel: function(collectionName, model){

				if( !this.existCollection(collectionName) ){
					throw errorMsg.collectionNotExist;
				}

				var collection = this.getCollection(collectionName);
				collection[model.index] = model;
				this.save(collectionName, collection);

			},

			save: function(collectionName, db){

				localStorage.setItem(collectionName, JSON.stringify(db))

			},

			existCollection: function(collectionName){

				if(localStorage.getItem(collectionName) !== null){

					return true;

				}else{

					return false;

				}

			},

			createCollection: function(collectionName){

				if(! this.existCollection(collectionName) ){

					localStorage.setItem(collectionName, JSON.stringify({}));

					return true;

				}

				return false;

			},

			getCollection: function(collectionName){

				if(this.existCollection(collectionName) ){

					return JSON.parse(localStorage.getItem(collectionName));

				}

			}

		}

	}

	/*******
	*
	* View Object
	*
	*******/
	/*
	var View = function(init){

		var Initialize = (function(init){

			if(init === undefined){
				throw errorMsg.noObject;
			}
			if(init.collection === undefined){
				throw errorMsg.viewCollection;
			}
			if(!Database().existCollection(init.collection)){
				throw errorMsg.collectionNotExist;
			}

			return init;

		})(init);

		function createTemplate(){

			var keys = schemas[Initialize.collection];
			var collection = Database().getCollection(Initialize.collection);

			for(var model in collection){

				console.log(collection[model]);

			}

		}

		var methods = {

			render: function(){

				createTemplate();

			}

		}

		return methods;

	}
	*/

	/*******
	*
	* helper function
	*
	*******/

	function setIndizes(arr){

		var obj = {};
		each(arr, function(model){
			obj[model.index] = model;
		});
		return obj;

	}

	function conformSchemaName(_str){

		var str = _str;

		str = str.replace(/\s/g, '');
		str = str.toLowerCase();

		return str;

	}

	function makeObject(arr){

		var obj = {};

		each(arr, function(field){

				obj[field] = field;

		});

		return obj;

	}

	function merge(mainObj, toMerge){

		var obj = {};

		for (var attr in mainObj) { obj[attr] = mainObj[attr]; }
    for (var attr in toMerge) { obj[attr] = toMerge[attr]; }

		return obj;

	}

	function createId(){

		var currDate = Date.now();
		var ran = Math.floor(Math.random() * currDate - (currDate + 1000) +   (currDate + 1000));
		return ran;

	}

	function each(arr, callback){

		var i = arr.length;
		while (i--){
			callback(arr[i], i);
		}

	}

	Beagle = new Initialize();

})();<commit_msg>fix conflict<commit_after>/*******
*
* Globale Variables
*
*******/
var Beagle;

(function(){

	"use strict";

	/*******
	*
	* Private Variables
	*
	*******/
	var schemas = {};

	/*******
	*
	* Error Messages
	*
	*******/

	var errorMsg = {

		nolocalstorage: "your browser doesn't support localstorage",
		schemaUndefined: "you have to defined a schema: Beagle.schema(schemaname:string, schemafields:array)",
		schemaNameEmpty: "you have to define a name for your schema",
		collectionNameUndefined: "collection name dont exist",
		collectionNotExist: "Collection dont exist",
		indexUndefined: "Index is undefinded",
		notInSchema: "attribute not in schema define",
		viewCollection: "your View need a collection",
		noObject: "your view need an object as parameter"
	}

	/*******
	*
	* Intialisierung nach dem Fassade Entwurfsmuster
	*
	*******/

	var Initialize = function(){

		if(!checkSystem()){

			throw	errorMsg.nolocalstorage;

		}

		var methods = {

			collections: function(){
				return Database().collections();
			},

			empty: function(collectionName){

				return Database().empty(collectionName);

			},

			view: function(init){

				//return View(init);

			},

			schema: function(collectionName, schemaFields){

				if( (collectionName !== undefined && typeof collectionName === "string" ) && (schemaFields !== undefined &&  schemaFields[0] !== undefined ) ){

					var sn = collectionName;
					var sf = schemaFields;

					if(sn.length === 0){
						throw errorMsg.schemaNameEmpty;
					}

					sn = conformSchemaName(sn);

					return Schema().schema(sn, sf);

				}else{

					throw errorMsg.schemaUndefined;

				}

			}
		}

		methods = merge(methods, Collection());
		methods = merge(methods, Observer());

		return methods

	}

	/*******
	*
	* System check funtion
	*
	*******/

	function checkSystem(){

		if(window.localStorage === undefined){

			return false;
		}

		return true;

	}

	/*******
	*
	* Def. Collection
	*
	*******/

	var Collection = function(){

		function checkCollectionName(collectionName){

			if( (collectionName !== undefined && typeof collectionName === "string") && ( Database().existCollection(collectionName) === true ) ){

				return true;

			}else{

				return false;

			}

		}

		function isAttr(collectionName, attr){

			var _schemas = makeObject(schemas[collectionName]);

			for(var key in attr){

				if(_schemas[key] === undefined){
					return false;
				}

			}

			return true;
		}

		function pushHandler(model, modify){

			var newModel = model;

			for(var key in modify){

				if(newModel[key] instanceof Array){

					newModel[key].push(modify[key]);

				}

			}

			return newModel;

		}

		function pullHandler(model, modify){

			var newModel = model;

			for(var key in modify){

				if(newModel[key] instanceof Array){

					var index = newModel[key].indexOf(modify[key]);
					if (index > -1) {
						 newModel[key].splice(index, 1);
					}

				}

			}

			return newModel;

		}

		function setHandler(model, modify){

			var newModel = model;

			for(var mod in modify){

				newModel[mod] = modify[mod]

			}

			return newModel;

		}

		function not(a,b){
			if(a !== b){
				return true;
			}else{
				return false;
			}
		}

		function is(a,b){
			if(a === b){
				return true;
			}else{
				return false;
			}
		}

		function gt(a,b){
			if(parseInt(a) > parseInt(b)){
				return true;
			}else{
				return false;
			}
		}

		function gte(a,b){
			if(parseInt(a) >= parseInt(b)){
				return true;
			}else{
				return false;
			}
		}

		function lt(a,b){
			if(parseInt(a) < parseInt(b)){
				return true;
			}else{
				return false;
			}
		}

		function lte(a,b){
			if(parseInt(a) <= parseInt(b)){
				return true;
			}else{
				return false;
			}
		}

		function has(a, b){

			if(a instanceof Array){

				var ctl = false;

				each(a, function(c){

					if(b === c){

						ctl = true;

					};

				});

				return ctl;

			}
		}

		return {

			add: function(collectionName, model){

				if(!checkCollectionName(collectionName)){

					throw errorMsg.collectionNameUndefined;

				}

				var fields = schemas[collectionName];

				var createModel = {

					index: createId()

				}

				each(fields, function(field){

					createModel[field] = model[field]

				});

				Database().addModel(collectionName, createModel);

				observe('add', createModel);

				return createModel;

			},

			removeByIndex: function(collectionName, id){

				if(!checkCollectionName(collectionName)){

					throw errorMsg.collectionNameUndefined;

				}

				var collections = Database().getCollection(collectionName);
				var deleted;

				if(collections !== undefined){

					deleted = collections[id];

					delete collections[id];

					Database().save(collectionName, collections);
					observe('remove', deleted);

				}

			},

			findByIndex: function(collectionName, id){

				if(id === undefined){

					throw errorMsg.indexUndefined;

				}

				if(!checkCollectionName(collectionName)){

					throw errorMsg.collectionNameUndefined;

				}

				var collections = Database().getCollection(collectionName);

				return collections[id];

			},

			findIndex: function(collectionName, attr, _collection){

				var operator;
				var fieldAttr;

				for(var operator in attr){

					operator = operator;
					fieldAttr = attr[operator];

				}

				var collections = _collection !== undefined ? _collection : Database().getCollection(collectionName);

				if(!isAttr(collectionName, fieldAttr)){

					throw errorMsg.notInSchema;

				}

				var search = [];

				for(var key in fieldAttr){

					search.push(key);
					search.push(fieldAttr[key]);

				}

				var results = [];

				for(var model in collections){

					var c = false;

					switch(operator){

						case 'is':
							c = is(collections[model][search[0]], search[1]);
						break;
						case 'not':
							c = not(collections[model][search[0]], search[1]);
						break;
						case 'gt':
							c = gt(collections[model][search[0]], search[1])
						break;
						case 'gte':
							c = gte(collections[model][search[0]], search[1])
						break;
						case 'lt':
							c = lt(collections[model][search[0]], search[1])
						break;
						case 'lte':
							c = lte(collections[model][search[0]], search[1])
						break;
						case 'has':
							c = has(collections[model][search[0]], search[1])
						break;
					}

					if(c){
						results.push(model);
					}

				}

				return results;

			},

			find: function(collectionName, attr, _collection){

				var indizes = this.findIndex(collectionName, attr, _collection);
				var collections = Database().getCollection(collectionName);
				var results = [];

				each(indizes, function(index){

					results.push(collections[index]);

				});

				return Cursor(results, collectionName);

			},

			findAll: function(collectionName){

				if(!checkCollectionName(collectionName)){

					throw errorMsg.collectionNameUndefined;

				}
				var results = [];
				var collections = Database().getCollection(collectionName);
				for(var model in collections){
					results.push(collections[model]);
				}
				return Cursor(results, collectionName);
			},

			remove: function(collectionName, attr){

				var indizes = this.findIndex(collectionName, attr);
				var collections = Database().getCollection(collectionName);
				var self = this;
				var deleted = [];

				each(indizes, function(index){

					deleted.push(collections[index]);
					self.removeByIndex(collectionName, index);

				});

				return Cursor(deleted, collectionName);

			},

			update: function(collectionName, attr, modifiers){

				var indizes = this.findIndex(collectionName, attr);
				var self = this;
				var updated = []

				each(indizes, function(index){

					self.updateByIndex(collectionName, index, modifiers);

				});

				var collections = Database().getCollection(collectionName);

				each(indizes, function(index){

					updated.push(collections[index]);

				});

				return Cursor(updated, collectionName);
			},

			updateByIndex: function(collectionName, id, modifiers){

				if(!checkCollectionName(collectionName)){

					throw errorMsg.collectionNameUndefined;

				}

				if(id === undefined){

					throw errorMsg.indexUndefined;

				}

				var model = Database().getCollection(collectionName)[id];

				for(var mod in modifiers){

					var modify = modifiers[mod];

					if(!isAttr(collectionName, modifiers[mod])){

						throw errorMsg.notInSchema;

					}

					var model;

					switch(mod){

						case 'set':
							model = setHandler(model, modify);
						break;

						case 'push':
							model = pushHandler(model, modify);
						break;

						case 'pull':
							model = pullHandler(model, modify);
						break;

					}

				}

				var collections = Database().getCollection(collectionName);
				collections[model.index] = model;

				Database().save(collectionName, collections);

				observe('update', updated);

			}

		}

	}

	/*******
	*
	* Def. Cursor Object
	*
	*******/

	var Cursor = function(collection, collectionName){

		var collection = collection;
		var currSchema = collectionName;

		return {

			count: function(){

				return collection.length;

			},

			fetch: function(){

				return collection;

			},

			where: function(attr){

				var modCollection = setIndizes(collection);
				var results = Collection().find(currSchema, attr, modCollection);
				return results;

			},

			limit: function(skip,limit){

				return collection.splice(skip, limit);

			}

		}

	}

	/*******
	*
	* Def. Schema Object
	*
	*******/

	var Schema = function(){

		return {

			schema: function(collectionName, schemaFields){

				schemas[collectionName] = schemaFields;

				if(Database().createCollection(collectionName)){

					schemas[collectionName] = schemaFields;
					return true;

				};

				return false;

			}

		}

	}

	/*******
	*
	* Def. Observer Object
	*
	*******/

	var listener = {
		update: [],
		remove: [],
		add: []
	}

	function observe(listen, obj){

		each(listener[listen], function(fn){

			fn(obj);

		});

	}

	var Observer = function(){

		return {

			on: function(listener_key, callback){

				listener[listener_key].push(callback);

			}

		}

	}

	/*******
	*
	* Localstorage API
	*
	*******/

	var Database = function(){

		return {

			empty: function(collectionName){

				if( !this.existCollection(collectionName) ){
					throw errorMsg.collectionNotExist;
				}

				this.save(collectionName, {});
				return true;

			},

			collections: function(){

				var all =  {};

				for(var schema in schemas){

					all[schema] =[];

					var collection = this.getCollection(schema);

					for(var model in collection){

						all[schema].push(collection[model]);

					}

				}

				return all;

			},

			addModel: function(collectionName, model){

				if( !this.existCollection(collectionName) ){
					throw errorMsg.collectionNotExist;
				}

				var collection = this.getCollection(collectionName);
				collection[model.index] = model;
				this.save(collectionName, collection);

			},

			save: function(collectionName, db){

				localStorage.setItem(collectionName, JSON.stringify(db))

			},

			existCollection: function(collectionName){

				if(localStorage.getItem(collectionName) !== null){

					return true;

				}else{

					return false;

				}

			},

			createCollection: function(collectionName){

				if(! this.existCollection(collectionName) ){

					localStorage.setItem(collectionName, JSON.stringify({}));

					return true;

				}

				return false;

			},

			getCollection: function(collectionName){

				if(this.existCollection(collectionName) ){

					return JSON.parse(localStorage.getItem(collectionName));

				}

			}

		}

	}

	/*******
	*
	* View Object
	*
	*******/
	/*
	var View = function(init){

		var Initialize = (function(init){

			if(init === undefined){
				throw errorMsg.noObject;
			}
			if(init.collection === undefined){
				throw errorMsg.viewCollection;
			}
			if(!Database().existCollection(init.collection)){
				throw errorMsg.collectionNotExist;
			}

			return init;

		})(init);

		function createTemplate(){

			var keys = schemas[Initialize.collection];
			var collection = Database().getCollection(Initialize.collection);

			for(var model in collection){

				console.log(collection[model]);

			}

		}

		var methods = {

			render: function(){

				createTemplate();

			}

		}

		return methods;

	}
	*/

	/*******
	*
	* helper function
	*
	*******/

	function setIndizes(arr){

		var obj = {};
		each(arr, function(model){
			obj[model.index] = model;
		});
		return obj;

	}

	function conformSchemaName(_str){

		var str = _str;

		str = str.replace(/\s/g, '');
		str = str.toLowerCase();

		return str;

	}

	function makeObject(arr){

		var obj = {};

		each(arr, function(field){

				obj[field] = field;

		});

		return obj;

	}

	function merge(mainObj, toMerge){

		var obj = {};

		for (var attr in mainObj) { obj[attr] = mainObj[attr]; }
    for (var attr in toMerge) { obj[attr] = toMerge[attr]; }

		return obj;

	}

	function createId(){

		var currDate = Date.now();
		var ran = Math.floor(Math.random() * currDate - (currDate + 1000) +   (currDate + 1000));
		return ran;

	}

	function each(arr, callback){

		var i = arr.length;
		while (i--){
			callback(arr[i], i);
		}

	}

	Beagle = new Initialize();

})();<|endoftext|>


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

  var CanvasRenderer = $$('renderer', 'canvas');
  var CRp = CanvasRenderer.prototype;

  // Draw node
  CRp.drawNode = function(context, node, drawOverlayInstead) {

    var r = this;
    var nodeWidth, nodeHeight;
    var style = node._private.style;
    var rs = node._private.rscratch;
    var _p = node._private;
    var pos = _p.position;

    if( pos.x === undefined || pos.y === undefined ){
      return; // can't draw node with undefined position
    }

    var usePaths = this.usePaths();
    var canvasContext = context;
    var path;
    var pathCacheHit = false;

    var overlayPadding = style['overlay-padding'].pxValue;
    var overlayOpacity = style['overlay-opacity'].value;
    var overlayColor = style['overlay-color'].value;

    if( drawOverlayInstead && overlayOpacity === 0 ){ // exit early if drawing overlay but none to draw
      return;
    }

    var parentOpacity = node.effectiveOpacity();
    if( parentOpacity === 0 ){ return; }

    nodeWidth = node.width() + style['padding-left'].pxValue + style['padding-right'].pxValue;
    nodeHeight = node.height() + style['padding-top'].pxValue + + style['padding-bottom'].pxValue;

    context.lineWidth = style['border-width'].pxValue;

    if( drawOverlayInstead === undefined || !drawOverlayInstead ){

      var url = style['background-image'].value[2] ||
        style['background-image'].value[1];
      var image;

      if (url !== undefined) {

        // get image, and if not loaded then ask to redraw when later loaded
        image = this.getCachedImage(url, function(){
          r.data.canvasNeedsRedraw[CanvasRenderer.NODE] = true;
          r.data.canvasNeedsRedraw[CanvasRenderer.DRAG] = true;

          r.drawingImage = true;

          r.redraw();
        });

        var prevBging = _p.backgrounding;
        _p.backgrounding = !image.complete;

        if( prevBging !== _p.backgrounding ){ // update style b/c :backgrounding state changed
          node.updateStyle( false );
        }
      }

      // Node color & opacity

      var bgColor = style['background-color'].value;
      var borderColor = style['border-color'].value;
      var borderStyle = style['border-style'].value;

      this.fillStyle(context, bgColor[0], bgColor[1], bgColor[2], style['background-opacity'].value * parentOpacity);

      this.strokeStyle(context, borderColor[0], borderColor[1], borderColor[2], style['border-opacity'].value * parentOpacity);

      var shadowBlur = style['shadow-blur'].pxValue;
      var shadowOpacity = style['shadow-opacity'].value;
      var shadowColor = style['shadow-color'].value;
      var shadowOffsetX = style['shadow-offset-x'].pxValue;
      var shadowOffsetY = style['shadow-offset-y'].pxValue;

      this.shadowStyle(context, shadowColor, shadowOpacity, shadowBlur, shadowOffsetX, shadowOffsetY);

      context.lineJoin = 'miter'; // so borders are square with the node shape

      if( context.setLineDash ){ // for very outofdate browsers
        switch( borderStyle ){
          case 'dotted':
            context.setLineDash([ 1, 1 ]);
            break;

          case 'dashed':
            context.setLineDash([ 4, 2 ]);
            break;

          case 'solid':
          case 'double':
            context.setLineDash([ ]);
            break;
        }
      }

      var styleShape = style['shape'].strValue;

      if( usePaths ){
        var pathCacheKey = styleShape + '$' + nodeWidth +'$' + nodeHeight;

        context.translate( pos.x, pos.y );

        if( rs.pathCacheKey === pathCacheKey ){
          path = context = rs.pathCache;
          pathCacheHit = true;
        } else {
          path = context = new Path2D();
          rs.pathCacheKey = pathCacheKey;
          rs.pathCache = path;
        }
      }

      if( !pathCacheHit ){

        var npos = pos;

        if( usePaths ){
          npos = {
            x: 0,
            y: 0
          };
        }

        CanvasRenderer.nodeShapes[this.getNodeShape(node)].drawPath(
              context,
              npos.x,
              npos.y,
              nodeWidth,
              nodeHeight);
      }

      context = canvasContext;

      if( usePaths ){
        context.fill( path );
      } else {
        context.fill();
      }

      this.shadowStyle(context, 'transparent', 0); // reset for next guy

      if (url !== undefined) {
        if( image.complete ){
          this.drawInscribedImage(context, image, node);
        }
      }

      var darkness = style['background-blacken'].value;
      var borderWidth = style['border-width'].pxValue;

      if( this.hasPie(node) ){
        this.drawPie( context, node, parentOpacity );

        // redraw path for blacken and border
        if( darkness !== 0 || borderWidth !== 0 ){

          if( !usePaths ){
            CanvasRenderer.nodeShapes[this.getNodeShape(node)].drawPath(
                context,
                pos.x,
                pos.y,
                nodeWidth,
                nodeHeight);
          }
        }
      }

      if( darkness > 0 ){
        this.fillStyle(context, 0, 0, 0, darkness);

        if( usePaths ){
          context.fill( path );
        } else {
          context.fill();
        }

      } else if( darkness < 0 ){
        this.fillStyle(context, 255, 255, 255, -darkness);

        if( usePaths ){
          context.fill( path );
        } else {
          context.fill();
        }
      }

      // Border width, draw border
      if (borderWidth > 0) {

        if( usePaths ){
          context.stroke( path );
        } else {
          context.stroke();
        }

        if( borderStyle === 'double' ){
          context.lineWidth = style['border-width'].pxValue/3;

          var gco = context.globalCompositeOperation;
          context.globalCompositeOperation = 'destination-out';

          if( usePaths ){
            context.stroke( path );
          } else {
            context.stroke();
          }

          context.globalCompositeOperation = gco;
        }

      }

      if( usePaths ){
        context.translate( -pos.x, -pos.y );
      }

      // reset in case we changed the border style
      if( context.setLineDash ){ // for very outofdate browsers
        context.setLineDash([ ]);
      }

    // draw the overlay
    } else {

      if( overlayOpacity > 0 ){
        this.fillStyle(context, overlayColor[0], overlayColor[1], overlayColor[2], overlayOpacity);

        CanvasRenderer.nodeShapes['roundrectangle'].drawPath(
          context,
          node._private.position.x,
          node._private.position.y,
          nodeWidth + overlayPadding * 2,
          nodeHeight + overlayPadding * 2
        );

        context.fill();
      }
    }

  };

  // does the node have at least one pie piece?
  CRp.hasPie = function(node){
    node = node[0]; // ensure ele ref

    return node._private.hasPie;
  };

  CRp.drawPie = function( context, node, nodeOpacity ){
    node = node[0]; // ensure ele ref

    var _p = node._private;
    var style = _p.style;
    var pieSize = style['pie-size'];
    var nodeW = node.width();
    var nodeH = node.height();
    var x = _p.position.x;
    var y = _p.position.y;
    var radius = Math.min( nodeW, nodeH ) / 2; // must fit in node
    var lastPercent = 0; // what % to continue drawing pie slices from on [0, 1]
    var usePaths = this.usePaths();

    if( usePaths ){
      x = 0;
      y = 0;
    }

    if( pieSize.units === '%' ){
      radius = radius * pieSize.value / 100;
    } else if( pieSize.pxValue !== undefined ){
      radius = pieSize.pxValue / 2;
    }

    for( var i = 1; i <= $$.style.pieBackgroundN; i++ ){ // 1..N
      var size = style['pie-' + i + '-background-size'].value;
      var color = style['pie-' + i + '-background-color'].value;
      var opacity = style['pie-' + i + '-background-opacity'].value * nodeOpacity;
      var percent = size / 100; // map integer range [0, 100] to [0, 1]

      // percent can't push beyond 1
      if( percent + lastPercent > 1 ){
        percent = 1 - lastPercent;
      }

      var angleStart = 1.5 * Math.PI + 2 * Math.PI * lastPercent; // start at 12 o'clock and go clockwise
      var angleDelta = 2 * Math.PI * percent;
      var angleEnd = angleStart + angleDelta;

      // ignore if
      // - zero size
      // - we're already beyond the full circle
      // - adding the current slice would go beyond the full circle
      if( size === 0 || lastPercent >= 1 || lastPercent + percent > 1 ){
        continue;
      }

      context.beginPath();
      context.moveTo(x, y);
      context.arc( x, y, radius, angleStart, angleEnd );
      context.closePath();

      this.fillStyle(context, color[0], color[1], color[2], opacity);

      context.fill();

      lastPercent += percent;
    }

  };

})( cytoscape );
<commit_msg>more robust position check for drawing nodes<commit_after>;(function($$){ 'use strict';

  var CanvasRenderer = $$('renderer', 'canvas');
  var CRp = CanvasRenderer.prototype;

  // Draw node
  CRp.drawNode = function(context, node, drawOverlayInstead) {

    var r = this;
    var nodeWidth, nodeHeight;
    var style = node._private.style;
    var rs = node._private.rscratch;
    var _p = node._private;
    var pos = _p.position;

    if( !$$.is.number(pos.x) || !$$.is.number(pos.y) ){
      return; // can't draw node with undefined position
    }

    var usePaths = this.usePaths();
    var canvasContext = context;
    var path;
    var pathCacheHit = false;

    var overlayPadding = style['overlay-padding'].pxValue;
    var overlayOpacity = style['overlay-opacity'].value;
    var overlayColor = style['overlay-color'].value;

    if( drawOverlayInstead && overlayOpacity === 0 ){ // exit early if drawing overlay but none to draw
      return;
    }

    var parentOpacity = node.effectiveOpacity();
    if( parentOpacity === 0 ){ return; }

    nodeWidth = node.width() + style['padding-left'].pxValue + style['padding-right'].pxValue;
    nodeHeight = node.height() + style['padding-top'].pxValue + + style['padding-bottom'].pxValue;

    context.lineWidth = style['border-width'].pxValue;

    if( drawOverlayInstead === undefined || !drawOverlayInstead ){

      var url = style['background-image'].value[2] ||
        style['background-image'].value[1];
      var image;

      if (url !== undefined) {

        // get image, and if not loaded then ask to redraw when later loaded
        image = this.getCachedImage(url, function(){
          r.data.canvasNeedsRedraw[CanvasRenderer.NODE] = true;
          r.data.canvasNeedsRedraw[CanvasRenderer.DRAG] = true;

          r.drawingImage = true;

          r.redraw();
        });

        var prevBging = _p.backgrounding;
        _p.backgrounding = !image.complete;

        if( prevBging !== _p.backgrounding ){ // update style b/c :backgrounding state changed
          node.updateStyle( false );
        }
      }

      // Node color & opacity

      var bgColor = style['background-color'].value;
      var borderColor = style['border-color'].value;
      var borderStyle = style['border-style'].value;

      this.fillStyle(context, bgColor[0], bgColor[1], bgColor[2], style['background-opacity'].value * parentOpacity);

      this.strokeStyle(context, borderColor[0], borderColor[1], borderColor[2], style['border-opacity'].value * parentOpacity);

      var shadowBlur = style['shadow-blur'].pxValue;
      var shadowOpacity = style['shadow-opacity'].value;
      var shadowColor = style['shadow-color'].value;
      var shadowOffsetX = style['shadow-offset-x'].pxValue;
      var shadowOffsetY = style['shadow-offset-y'].pxValue;

      this.shadowStyle(context, shadowColor, shadowOpacity, shadowBlur, shadowOffsetX, shadowOffsetY);

      context.lineJoin = 'miter'; // so borders are square with the node shape

      if( context.setLineDash ){ // for very outofdate browsers
        switch( borderStyle ){
          case 'dotted':
            context.setLineDash([ 1, 1 ]);
            break;

          case 'dashed':
            context.setLineDash([ 4, 2 ]);
            break;

          case 'solid':
          case 'double':
            context.setLineDash([ ]);
            break;
        }
      }

      var styleShape = style['shape'].strValue;

      if( usePaths ){
        var pathCacheKey = styleShape + '$' + nodeWidth +'$' + nodeHeight;

        context.translate( pos.x, pos.y );

        if( rs.pathCacheKey === pathCacheKey ){
          path = context = rs.pathCache;
          pathCacheHit = true;
        } else {
          path = context = new Path2D();
          rs.pathCacheKey = pathCacheKey;
          rs.pathCache = path;
        }
      }

      if( !pathCacheHit ){

        var npos = pos;

        if( usePaths ){
          npos = {
            x: 0,
            y: 0
          };
        }

        CanvasRenderer.nodeShapes[this.getNodeShape(node)].drawPath(
              context,
              npos.x,
              npos.y,
              nodeWidth,
              nodeHeight);
      }

      context = canvasContext;

      if( usePaths ){
        context.fill( path );
      } else {
        context.fill();
      }

      this.shadowStyle(context, 'transparent', 0); // reset for next guy

      if (url !== undefined) {
        if( image.complete ){
          this.drawInscribedImage(context, image, node);
        }
      }

      var darkness = style['background-blacken'].value;
      var borderWidth = style['border-width'].pxValue;

      if( this.hasPie(node) ){
        this.drawPie( context, node, parentOpacity );

        // redraw path for blacken and border
        if( darkness !== 0 || borderWidth !== 0 ){

          if( !usePaths ){
            CanvasRenderer.nodeShapes[this.getNodeShape(node)].drawPath(
                context,
                pos.x,
                pos.y,
                nodeWidth,
                nodeHeight);
          }
        }
      }

      if( darkness > 0 ){
        this.fillStyle(context, 0, 0, 0, darkness);

        if( usePaths ){
          context.fill( path );
        } else {
          context.fill();
        }

      } else if( darkness < 0 ){
        this.fillStyle(context, 255, 255, 255, -darkness);

        if( usePaths ){
          context.fill( path );
        } else {
          context.fill();
        }
      }

      // Border width, draw border
      if (borderWidth > 0) {

        if( usePaths ){
          context.stroke( path );
        } else {
          context.stroke();
        }

        if( borderStyle === 'double' ){
          context.lineWidth = style['border-width'].pxValue/3;

          var gco = context.globalCompositeOperation;
          context.globalCompositeOperation = 'destination-out';

          if( usePaths ){
            context.stroke( path );
          } else {
            context.stroke();
          }

          context.globalCompositeOperation = gco;
        }

      }

      if( usePaths ){
        context.translate( -pos.x, -pos.y );
      }

      // reset in case we changed the border style
      if( context.setLineDash ){ // for very outofdate browsers
        context.setLineDash([ ]);
      }

    // draw the overlay
    } else {

      if( overlayOpacity > 0 ){
        this.fillStyle(context, overlayColor[0], overlayColor[1], overlayColor[2], overlayOpacity);

        CanvasRenderer.nodeShapes['roundrectangle'].drawPath(
          context,
          node._private.position.x,
          node._private.position.y,
          nodeWidth + overlayPadding * 2,
          nodeHeight + overlayPadding * 2
        );

        context.fill();
      }
    }

  };

  // does the node have at least one pie piece?
  CRp.hasPie = function(node){
    node = node[0]; // ensure ele ref

    return node._private.hasPie;
  };

  CRp.drawPie = function( context, node, nodeOpacity ){
    node = node[0]; // ensure ele ref

    var _p = node._private;
    var style = _p.style;
    var pieSize = style['pie-size'];
    var nodeW = node.width();
    var nodeH = node.height();
    var x = _p.position.x;
    var y = _p.position.y;
    var radius = Math.min( nodeW, nodeH ) / 2; // must fit in node
    var lastPercent = 0; // what % to continue drawing pie slices from on [0, 1]
    var usePaths = this.usePaths();

    if( usePaths ){
      x = 0;
      y = 0;
    }

    if( pieSize.units === '%' ){
      radius = radius * pieSize.value / 100;
    } else if( pieSize.pxValue !== undefined ){
      radius = pieSize.pxValue / 2;
    }

    for( var i = 1; i <= $$.style.pieBackgroundN; i++ ){ // 1..N
      var size = style['pie-' + i + '-background-size'].value;
      var color = style['pie-' + i + '-background-color'].value;
      var opacity = style['pie-' + i + '-background-opacity'].value * nodeOpacity;
      var percent = size / 100; // map integer range [0, 100] to [0, 1]

      // percent can't push beyond 1
      if( percent + lastPercent > 1 ){
        percent = 1 - lastPercent;
      }

      var angleStart = 1.5 * Math.PI + 2 * Math.PI * lastPercent; // start at 12 o'clock and go clockwise
      var angleDelta = 2 * Math.PI * percent;
      var angleEnd = angleStart + angleDelta;

      // ignore if
      // - zero size
      // - we're already beyond the full circle
      // - adding the current slice would go beyond the full circle
      if( size === 0 || lastPercent >= 1 || lastPercent + percent > 1 ){
        continue;
      }

      context.beginPath();
      context.moveTo(x, y);
      context.arc( x, y, radius, angleStart, angleEnd );
      context.closePath();

      this.fillStyle(context, color[0], color[1], color[2], opacity);

      context.fill();

      lastPercent += percent;
    }

  };

})( cytoscape );
<|endoftext|>