<commit_before>import {
    saveAccounts
} from '../js/redux/services';

import {
    updateGroupFromChildren
} from '../js/redux/utilities';

import {
    clone,
    safeAccess
} from '../js/react/utilities';

// Reducer
const statToNumber = {
    due: 1,
    pending: 2,
    paid: 3
};

function fixTotals(accounts) {
    var u = clone(accounts);
    (u.liabilities || []).forEach(x => {
        if (x.hidden === 'false') {
            x.hidden = false;
        }
    });

    u.totals = u.totals || {};

    var pending = (u.liabilities || [])
        .filter(function (a) {
            return a.status && a.status.toLowerCase() === 'pending';
        }).sort(function (a, b) {
            return new Date(a.date) - new Date(b.date);
        });
    // var paid = (u.liabilities||[])
    //     .filter(function (a) {
    //         return a.status && a.status.toLowerCase() === 'paid';
    //     }).sort(function (a, b) {
    //         return new Date(a.date) - new Date(b.date);
    //     });
    var due = (u.liabilities || [])
        .filter(function (a) {
            return a.status && a.status.toLowerCase() === 'due';
        }).sort(function (a, b) {
            return new Date(a.date) - new Date(b.date);
        });

    u.totals.pendingTotal = pending
        .filter(item => !(item.hidden || item.type === 'group'))
        .reduce((all, one) => all + Number(one.amount), 0)
        .toFixed(2);

    u.totals.dueTotal = due
        .filter(item => !(item.hidden || item.type === 'group'))
        .reduce((all, one) => all + Number(one.amount), 0)
        .toFixed(2);

    u.totals.assetsTotal = (u.assets || [])
        .filter(item => !JSON.parse(item.hidden))
        .reduce((all, one) => all + Number(one.amount), 0)
        .toFixed(2);

    u.totals.debts = (u.liabilities || [])
        .filter(item => !(item.hidden || item.type === 'group'))
        .reduce((all, one) => all + Number(one.amount), 0)
        .toFixed(2);

    u.totals.debtsTotal = (u.liabilities || [])
        .filter(item => !(item.hidden || item.type === 'group'))
        .reduce((all, one) => all + Number(one.total_owed), 0)
        .toFixed(2);

    return u;
}

function markGroupedItems(accounts) {
    const newAccounts = clone(accounts);
    const groupedItems = Object.keys(
        newAccounts.liabilities
            .reduce((all, x) => {
                if (x.items) {
                    x.items.forEach(y => {
                        if (!all[y.title.toLowerCase()]) {
                            all[y.title.toLowerCase()] = y;
                        }
                    });
                }
                return all;
            }, {})
    );
    newAccounts.liabilities.forEach(x => x.type !== 'group' ? delete x.type : undefined);
    newAccounts.liabilities.forEach(x =>
        x.type !== 'group' && groupedItems.includes(x.title.toLowerCase())
            ? x.type = 'grouped'
            : undefined
    );
    return newAccounts;
}

function openGroupedAccounts(initialState, viewState) {
    const outputState = clone(viewState);

    // stupid "false"
    if(!outputState.accounts){
        console.log(JSON.stringify({outputState, initialState, viewState}, null, '  '));
        console.trace();
    }
    outputState.accounts.liabilities.forEach(
        x => x.hidden === "false" ? x.hidden = false : undefined
    );

    // remove grouped items
    outputState.accounts.liabilities = outputState.accounts.liabilities.filter(x => x.type !== 'grouped');

    // remove hidden items
    outputState.accounts.liabilities = outputState.accounts.liabilities.filter(x => !x.hidden);

    // add grouped items back if group open
    //console.log(initialState)
    var newLiabs = [];
    outputState.accounts.liabilities.forEach(group => {
        newLiabs.push(group);
        if (!group.open || group.type !== 'group') return;

        const groupedItems = group.items
            .map(item => (initialState.accounts.liabilities.filter(x => x.title === item.title) || [])[0])
            .sort(function (a, b) {
                var statCompare = 0;
                if (statToNumber[a.status.toLowerCase()] > statToNumber[b.status.toLowerCase()]) statCompare = 1;
                if (statToNumber[a.status.toLowerCase()] < statToNumber[b.status.toLowerCase()]) statCompare = -1;

                return statCompare || new Date(a.date) - new Date(b.date);
            });

        newLiabs = newLiabs.concat(groupedItems);
    });

    outputState.accounts.liabilities = newLiabs;
    return outputState;
}

function bumpDateOneMonth(date) {
    var day = Number(date.replace(/.*-/g, ''));
    var month = Number(date.replace(/-..$/g, '').replace(/.*-/g, ''));
    var year = Number(date.replace(/-.*/g, ''));
    if (month === 12) {
        year += 1;
        month = 1;
    } else {
        month += 1;
    }
    day = (day < 10) ? '0' + day : day;
    month = (month < 10) ? '0' + month : month;
    return year + '-' + month + '-' + day;
}

function bumpDateOneMonthBack(date) {
    var day = Number(date.replace(/.*-/g, ''));
    var month = Number(date.replace(/-..$/g, '').replace(/.*-/g, ''));
    var year = Number(date.replace(/-.*/g, ''));
    if (month === 1) {
        year -= 1;
        month = 12;
    } else {
        month -= 1;
    }
    day = (day < 10) ? '0' + day : day;
    month = (month < 10) ? '0' + month : month;
    return year + '-' + month + '-' + day;
}

/*

   _  _  _       _  _  _  _    _  _  _  _
  (_)(_)(_) _   (_)(_)(_)(_)_ (_)(_)(_)(_)_
   _  _  _ (_)  (_)        (_)(_)        (_)
 _(_)(_)(_)(_)  (_)        (_)(_)        (_)
(_)_  _  _ (_)_ (_) _  _  _(_)(_) _  _  _(_)
  (_)(_)(_)  (_)(_)(_)(_)(_)  (_)(_)(_)(_)
                (_)           (_)
                (_)           (_)

*/

function receiveAccounts(state, action, root) {
    var newState = {};
    if (action.payload.error) {
        newState = Object.assign({}, state, action.payload);
        return newState;
    }
    newState.accounts = clone(action.payload) || {};
    (newState.accounts.liabilities || []).forEach(x => {
        if (x.hidden === 'false') {
            x.hidden = false;
        }
    });
    newState = updateGroupFromChildren(newState);
    newState.totals = safeAccess(() => state.totals) || {};
    newState.totals.balance = safeAccess(() => state.totals.balance) || 0;
    newState.totals.updating = true;
    newState = fixTotals(newState);
    newState = openGroupedAccounts(newState, newState);

    if (state && typeof state.selectedMenuIndex === "undefined") {
        newState.selectedMenuIndex = window && window.localStorage
            ? Number(localStorage.getItem('selectedTab'))
            : 0;
    } else {
        newState.selectedMenuIndex = state ? state.selectedMenuIndex : 0;
    }
    //newState.accounts = action.payload;
    debugger
    return newState;
}

function receiveAccountsData(state, action, root) {
    var newState;
    if (action.payload.error) {
        newState = Object.assign({}, state, action.payload);
        return newState;
    }
    newState = clone(state);
    const balance = safeAccess(() => action.payload.data.accounts[0].balance);
    newState.totals = newState.totals || {};
    newState.totals.balance = Number(balance || 0);
    newState.totals.updating = false;

    return newState;
}

function receiveAccountsSave(state, action, root) {
    var newState;
    if (action.payload.error) {
        newState = Object.assign({}, state, action.payload);
        return newState;
    }
    // console.log('got accounts save, notify if an error');
    newState = clone(state);
    newState.error = false;
    return newState;
}

function menuSelect(state, action, root) {
    var newState;
    localStorage.setItem('selectedTab', action.payload);
    const selectedMenuIndex = action.payload;
    newState = clone(state);
    newState.selectedMenuIndex = selectedMenuIndex;
    //newState.liabilities.forEach(x => x.selected = false);
    return newState;
}

function selectAccountClick(state, action, { selected = [] }) {
    const newState = clone(state);
    newState.accounts.liabilities.forEach(l => {
        l.selected = selected.map(x => x.title).includes(l.title);
    });
    //newState.selected = selected.map(x => x.title);
    return newState;
}

function groupClick(state, action, root) {
    //console.log({state, action, root});
    var newState;
    const groupTitle = action.payload.title;
    //const group = (state.liabilities.filter(x => x.title === groupTitle) || [])[0];

    newState = clone(state);
    newState.accounts.liabilities = newState.liabilities.map(x => {
        if (x.title.toLowerCase() === groupTitle.toLowerCase()) {
            x.open = typeof x.open !== 'undefined' ? !x.open : true;
        }
        return x;
    });
    //console.log(root)
    newState = openGroupedAccounts(newState, newState);

    // toggle open/closed
    //newState = switchGroup(group, state, accounts, !group.open)
    return newState;
}

function groupRemove(state, action, root) {
    var newState = clone(state);

    newState.accounts = clone(root.accounts);

    //TODO: should do this in root reducer
    saveAccounts({
        assets: newState.accounts.assets,
        liabilities: newState.accounts.liabilities,
        balance: newState.accounts.balance
    });
    newState.accounts.assets = root
        ? clone(safeAccess(() => root.accounts.assets) || [])
        : [];
    newState.accounts.liabilities = root
        ? clone(safeAccess(() => root.accounts.liabilities) || [])
        : [];
    newState.account = undefined;

    newState = markGroupedItems(newState.accounts);
    newState = openGroupedAccounts(newState, newState);

    return newState;
}

function accountSave(state, action, root) {
    if (!action.payload.account) {
        return state;
    }

    var newState = clone(state);

    // rehydrate grouped items, maybe not needed
    newState.accounts = clone(root.accounts);
    newState.accounts.liabilities.forEach(liab => {
        if (liab.type !== 'group') return;
        liab.items = liab.items.map(i => newState.accounts.liabilities
            .find(l => l.title === i)
        );
    });

    newState = updateGroupFromChildren(newState);
    newState = openGroupedAccounts(newState, newState);

    newState.totals = newState.accounts.totals;
    delete newState.accounts.totals;

    return newState;
}

function app(state, action, root) {
    //console.log(`--- app runs: ${action.type}`);
    var newState = undefined;
    //var groupedItems = undefined;
    switch (action.type) {
        case 'RECEIVE_ACCOUNTS':
            newState = receiveAccounts(state, action, root);
            break;
        case 'RECEIVE_ACCOUNTS_DATA':
            newState = receiveAccountsData(state, action, root);
            break;
        case 'RECEIVE_ACCOUNTS_SAVE':
            newState = receiveAccountsSave(state, action, root);
            break;
        case 'MENU_SELECT':
            newState = menuSelect(state, action, root);
            break;
        case 'SELECT_ACCOUNT_CLICK':
            newState = selectAccountClick(state, action, root);
            break;
        case 'GROUP_CLICK':
            newState = groupClick(state, action, root);
            break;
        case 'GROUP_REMOVE':
            newState = groupRemove(state, action, root);
            break;
        case 'ACCOUNT_SAVE':
            newState = accountSave(state, action, root);
            break;
        default:
            newState = clone(state || {});
            //(newState.accounts.liabilities || []).forEach(x => x.selected = false);
    }

    return newState || state || {};
}

export default app;
export {
    receiveAccounts, receiveAccountsData, receiveAccountsSave, menuSelect,
    selectAccountClick, groupClick, groupRemove, accountSave
};
<commit_msg>remove debugger<commit_after>import {
    saveAccounts
} from '../js/redux/services';

import {
    updateGroupFromChildren
} from '../js/redux/utilities';

import {
    clone,
    safeAccess
} from '../js/react/utilities';

// Reducer
const statToNumber = {
    due: 1,
    pending: 2,
    paid: 3
};

function fixTotals(accounts) {
    var u = clone(accounts);
    (u.liabilities || []).forEach(x => {
        if (x.hidden === 'false') {
            x.hidden = false;
        }
    });

    u.totals = u.totals || {};

    var pending = (u.liabilities || [])
        .filter(function (a) {
            return a.status && a.status.toLowerCase() === 'pending';
        }).sort(function (a, b) {
            return new Date(a.date) - new Date(b.date);
        });
    // var paid = (u.liabilities||[])
    //     .filter(function (a) {
    //         return a.status && a.status.toLowerCase() === 'paid';
    //     }).sort(function (a, b) {
    //         return new Date(a.date) - new Date(b.date);
    //     });
    var due = (u.liabilities || [])
        .filter(function (a) {
            return a.status && a.status.toLowerCase() === 'due';
        }).sort(function (a, b) {
            return new Date(a.date) - new Date(b.date);
        });

    u.totals.pendingTotal = pending
        .filter(item => !(item.hidden || item.type === 'group'))
        .reduce((all, one) => all + Number(one.amount), 0)
        .toFixed(2);

    u.totals.dueTotal = due
        .filter(item => !(item.hidden || item.type === 'group'))
        .reduce((all, one) => all + Number(one.amount), 0)
        .toFixed(2);

    u.totals.assetsTotal = (u.assets || [])
        .filter(item => !JSON.parse(item.hidden))
        .reduce((all, one) => all + Number(one.amount), 0)
        .toFixed(2);

    u.totals.debts = (u.liabilities || [])
        .filter(item => !(item.hidden || item.type === 'group'))
        .reduce((all, one) => all + Number(one.amount), 0)
        .toFixed(2);

    u.totals.debtsTotal = (u.liabilities || [])
        .filter(item => !(item.hidden || item.type === 'group'))
        .reduce((all, one) => all + Number(one.total_owed), 0)
        .toFixed(2);

    return u;
}

function markGroupedItems(accounts) {
    const newAccounts = clone(accounts);
    const groupedItems = Object.keys(
        newAccounts.liabilities
            .reduce((all, x) => {
                if (x.items) {
                    x.items.forEach(y => {
                        if (!all[y.title.toLowerCase()]) {
                            all[y.title.toLowerCase()] = y;
                        }
                    });
                }
                return all;
            }, {})
    );
    newAccounts.liabilities.forEach(x => x.type !== 'group' ? delete x.type : undefined);
    newAccounts.liabilities.forEach(x =>
        x.type !== 'group' && groupedItems.includes(x.title.toLowerCase())
            ? x.type = 'grouped'
            : undefined
    );
    return newAccounts;
}

function openGroupedAccounts(initialState, viewState) {
    const outputState = clone(viewState);

    // stupid "false"
    if(!outputState.accounts){
        console.log(JSON.stringify({outputState, initialState, viewState}, null, '  '));
        console.trace();
    }
    outputState.accounts.liabilities.forEach(
        x => x.hidden === "false" ? x.hidden = false : undefined
    );

    // remove grouped items
    outputState.accounts.liabilities = outputState.accounts.liabilities.filter(x => x.type !== 'grouped');

    // remove hidden items
    outputState.accounts.liabilities = outputState.accounts.liabilities.filter(x => !x.hidden);

    // add grouped items back if group open
    //console.log(initialState)
    var newLiabs = [];
    outputState.accounts.liabilities.forEach(group => {
        newLiabs.push(group);
        if (!group.open || group.type !== 'group') return;

        const groupedItems = group.items
            .map(item => (initialState.accounts.liabilities.filter(x => x.title === item.title) || [])[0])
            .sort(function (a, b) {
                var statCompare = 0;
                if (statToNumber[a.status.toLowerCase()] > statToNumber[b.status.toLowerCase()]) statCompare = 1;
                if (statToNumber[a.status.toLowerCase()] < statToNumber[b.status.toLowerCase()]) statCompare = -1;

                return statCompare || new Date(a.date) - new Date(b.date);
            });

        newLiabs = newLiabs.concat(groupedItems);
    });

    outputState.accounts.liabilities = newLiabs;
    return outputState;
}

function bumpDateOneMonth(date) {
    var day = Number(date.replace(/.*-/g, ''));
    var month = Number(date.replace(/-..$/g, '').replace(/.*-/g, ''));
    var year = Number(date.replace(/-.*/g, ''));
    if (month === 12) {
        year += 1;
        month = 1;
    } else {
        month += 1;
    }
    day = (day < 10) ? '0' + day : day;
    month = (month < 10) ? '0' + month : month;
    return year + '-' + month + '-' + day;
}

function bumpDateOneMonthBack(date) {
    var day = Number(date.replace(/.*-/g, ''));
    var month = Number(date.replace(/-..$/g, '').replace(/.*-/g, ''));
    var year = Number(date.replace(/-.*/g, ''));
    if (month === 1) {
        year -= 1;
        month = 12;
    } else {
        month -= 1;
    }
    day = (day < 10) ? '0' + day : day;
    month = (month < 10) ? '0' + month : month;
    return year + '-' + month + '-' + day;
}

/*

   _  _  _       _  _  _  _    _  _  _  _
  (_)(_)(_) _   (_)(_)(_)(_)_ (_)(_)(_)(_)_
   _  _  _ (_)  (_)        (_)(_)        (_)
 _(_)(_)(_)(_)  (_)        (_)(_)        (_)
(_)_  _  _ (_)_ (_) _  _  _(_)(_) _  _  _(_)
  (_)(_)(_)  (_)(_)(_)(_)(_)  (_)(_)(_)(_)
                (_)           (_)
                (_)           (_)

*/

function receiveAccounts(state, action, root) {
    var newState = {};
    if (action.payload.error) {
        newState = Object.assign({}, state, action.payload);
        return newState;
    }
    newState.accounts = clone(action.payload) || {};
    (newState.accounts.liabilities || []).forEach(x => {
        if (x.hidden === 'false') {
            x.hidden = false;
        }
    });
    newState = updateGroupFromChildren(newState);
    newState.totals = safeAccess(() => state.totals) || {};
    newState.totals.balance = safeAccess(() => state.totals.balance) || 0;
    newState.totals.updating = true;
    newState = fixTotals(newState);
    newState = openGroupedAccounts(newState, newState);

    if (state && typeof state.selectedMenuIndex === "undefined") {
        newState.selectedMenuIndex = window && window.localStorage
            ? Number(localStorage.getItem('selectedTab'))
            : 0;
    } else {
        newState.selectedMenuIndex = state ? state.selectedMenuIndex : 0;
    }
    //newState.accounts = action.payload;
    //debugger
    return newState;
}

function receiveAccountsData(state, action, root) {
    var newState;
    if (action.payload.error) {
        newState = Object.assign({}, state, action.payload);
        return newState;
    }
    newState = clone(state);
    const balance = safeAccess(() => action.payload.data.accounts[0].balance);
    newState.totals = newState.totals || {};
    newState.totals.balance = Number(balance || 0);
    newState.totals.updating = false;

    return newState;
}

function receiveAccountsSave(state, action, root) {
    var newState;
    if (action.payload.error) {
        newState = Object.assign({}, state, action.payload);
        return newState;
    }
    // console.log('got accounts save, notify if an error');
    newState = clone(state);
    newState.error = false;
    return newState;
}

function menuSelect(state, action, root) {
    var newState;
    localStorage.setItem('selectedTab', action.payload);
    const selectedMenuIndex = action.payload;
    newState = clone(state);
    newState.selectedMenuIndex = selectedMenuIndex;
    //newState.liabilities.forEach(x => x.selected = false);
    return newState;
}

function selectAccountClick(state, action, { selected = [] }) {
    const newState = clone(state);
    newState.accounts.liabilities.forEach(l => {
        l.selected = selected.map(x => x.title).includes(l.title);
    });
    //newState.selected = selected.map(x => x.title);
    return newState;
}

function groupClick(state, action, root) {
    //console.log({state, action, root});
    var newState;
    const groupTitle = action.payload.title;
    //const group = (state.liabilities.filter(x => x.title === groupTitle) || [])[0];

    newState = clone(state);
    newState.accounts.liabilities = newState.liabilities.map(x => {
        if (x.title.toLowerCase() === groupTitle.toLowerCase()) {
            x.open = typeof x.open !== 'undefined' ? !x.open : true;
        }
        return x;
    });
    //console.log(root)
    newState = openGroupedAccounts(newState, newState);

    // toggle open/closed
    //newState = switchGroup(group, state, accounts, !group.open)
    return newState;
}

function groupRemove(state, action, root) {
    var newState = clone(state);

    newState.accounts = clone(root.accounts);

    //TODO: should do this in root reducer
    saveAccounts({
        assets: newState.accounts.assets,
        liabilities: newState.accounts.liabilities,
        balance: newState.accounts.balance
    });
    newState.accounts.assets = root
        ? clone(safeAccess(() => root.accounts.assets) || [])
        : [];
    newState.accounts.liabilities = root
        ? clone(safeAccess(() => root.accounts.liabilities) || [])
        : [];
    newState.account = undefined;

    newState = markGroupedItems(newState.accounts);
    newState = openGroupedAccounts(newState, newState);

    return newState;
}

function accountSave(state, action, root) {
    if (!action.payload.account) {
        return state;
    }

    var newState = clone(state);

    // rehydrate grouped items, maybe not needed
    newState.accounts = clone(root.accounts);
    newState.accounts.liabilities.forEach(liab => {
        if (liab.type !== 'group') return;
        liab.items = liab.items.map(i => newState.accounts.liabilities
            .find(l => l.title === i)
        );
    });

    newState = updateGroupFromChildren(newState);
    newState = openGroupedAccounts(newState, newState);

    newState.totals = newState.accounts.totals;
    delete newState.accounts.totals;

    return newState;
}

function app(state, action, root) {
    //console.log(`--- app runs: ${action.type}`);
    var newState = undefined;
    //var groupedItems = undefined;
    switch (action.type) {
        case 'RECEIVE_ACCOUNTS':
            newState = receiveAccounts(state, action, root);
            break;
        case 'RECEIVE_ACCOUNTS_DATA':
            newState = receiveAccountsData(state, action, root);
            break;
        case 'RECEIVE_ACCOUNTS_SAVE':
            newState = receiveAccountsSave(state, action, root);
            break;
        case 'MENU_SELECT':
            newState = menuSelect(state, action, root);
            break;
        case 'SELECT_ACCOUNT_CLICK':
            newState = selectAccountClick(state, action, root);
            break;
        case 'GROUP_CLICK':
            newState = groupClick(state, action, root);
            break;
        case 'GROUP_REMOVE':
            newState = groupRemove(state, action, root);
            break;
        case 'ACCOUNT_SAVE':
            newState = accountSave(state, action, root);
            break;
        default:
            newState = clone(state || {});
            //(newState.accounts.liabilities || []).forEach(x => x.selected = false);
    }

    return newState || state || {};
}

export default app;
export {
    receiveAccounts, receiveAccountsData, receiveAccountsSave, menuSelect,
    selectAccountClick, groupClick, groupRemove, accountSave
};
<|endoftext|>


<commit_before>/* global $, twemoji */

import 'gsap/src/uncompressed/utils/Draggable';
import 'gsap/src/uncompressed/TweenLite';
import 'gsap/src/uncompressed/easing/EasePack';

import selectablePopup from './lib/emoji-selector';
import addScript from './lib/add-script';
import {getItem, init as settingsInit} from './lib/settings';
import pushNotifications from './lib/push-notifications';
import tinycam from './lib/tinycam';
import {updateMessageTextInput, combineEmojis, isCombinableEmojis, init as initTextInput} from './lib/emoji-text-input';
import {init as messages} from './lib/messages';
import {init as touchInit} from './lib/touch';
import {init as apiInit, save as apiSave, getCorrespondents} from './lib/api';
import {init as shareInit} from './lib/share';

Promise.all([
	addScript('https://cdn.rawgit.com/AdaRoseEdwards/dirty-dom/v1.3.1/build/dirty-dom-lib.min.js').promise,
	addScript('https://twemoji.maxcdn.com/2/twemoji.min.js').promise,

	// some useful polyfills
	addScript('https://cdn.polyfill.io/v2/polyfill.min.js?features=fetch,default').promise,

	// patched array.from polyfill
	addScript('https://cdn.rawgit.com/Financial-Times/polyfill-service/Array.from-Handle-Iterable/polyfills/Array/from/polyfill.js').promise,
	addScript('/scripts/color-thief.js').promise
]).then(() => {

	/*
	* Set up the emoji Grid
	*/
	const subEmojis = [
		['😍', '😘', '🌈', '💕', '❤', '💔', '🌹', '👬', '👭'],       // Romance
		['😋', '😎', '😤', '😉', '😀', '😂', '😴', '😉', '😇'],        // Smiles
		['✍', '✋', '✌', '👐', '👌', '🖖', '👏', '👋', '🙋'],        // Hands
		['👜', '👒', '👘', '👖', '👞', '👠', '👢', '👕', '👚'], // Clothes
		['🍕', '☕', '🍸', '🍺', '🍷', '🍗', '🍒', '🍩', '🎂'],    // Foods
		['😐', '😑', '😶', '😣', '😥', '😮', '😪', '😫', '⬇'],   // Misc
		['😌', '🤓', '😛', '😜', '😝', '☹', '🙁', '😒', '😲'],     // Silly
		['😟', '😑', '💩', '😒', '🖕', '🤘', '👊', '🍖', '✌'],      // Angries
		['👅', '🍑', '💋', '🏩', '💩', '👉', '👌', '🍆', '💦']    // Adult
	];

	// main emojis is made out of middle emoji
	const mainEmojis = subEmojis.map(e => e[4]);

	const makeGrid = (emojis) => $(`

		${combineEmojis(emojis[0], getItem('skintone'))}
		${combineEmojis(emojis[1], getItem('skintone'))}
		${combineEmojis(emojis[2], getItem('skintone'))}
		${combineEmojis(emojis[3], getItem('skintone'))}
		${combineEmojis(emojis[4], getItem('skintone'))}
		${combineEmojis(emojis[5], getItem('skintone'))}
		${combineEmojis(emojis[6], getItem('skintone'))}
		${combineEmojis(emojis[7], getItem('skintone'))}
		${combineEmojis(emojis[8], getItem('skintone'))}

`);

	const mainGrid = makeGrid(mainEmojis);
	mainGrid.$$('.emoji__grid-item')
	.map(function (item, i) {

		const subGrid = makeGrid(subEmojis[i]);
		subGrid.dataset.emoji = subEmojis[i][4];
		subGrid.on('emojiSelect', e => {

			if (isCombinableEmojis(e.detail.emoji, getItem('skintone'))) {
				updateMessageTextInput(e.detail.emoji + getItem('skintone'));
			} else {
				updateMessageTextInput(e.detail.emoji);
			}

		});
		item.appendChild(subGrid);
		return subGrid;
	})
	.forEach(selectablePopup);

	$('#emoji__grid').appendChild(mainGrid);
	twemoji.parse(mainGrid);
	twemoji.parse($('#emoji__options-button'));

	function updateCorrespondentsList() {
		const c = getCorrespondents();
		const dom = $('#emoji_correspondents');
		dom.empty();
		for (const co of c) {
			if (co !== '@AnonymousUser') {
				dom.$(`<option selected>${co}</option>`);
			}
		}
	}

	window.addEventListener('correrspondentsUpdated', updateCorrespondentsList);

	// Set up local storage caching
	apiInit().then(updateCorrespondentsList);

	// Add button interactions
	touchInit();

	// Set up push notification service
	pushNotifications();

	// load new messages
	messages();

	// Push notification camera.
	tinycam();

	// Set up the text input
	initTextInput();

	shareInit();

	settingsInit();

	window.addEventListener('unload', apiSave);
})
.catch(e => {

	// Script loading errors
	throw e;
});
<commit_msg>Clear text input on click<commit_after>/* global $, twemoji */

import 'gsap/src/uncompressed/utils/Draggable';
import 'gsap/src/uncompressed/TweenLite';
import 'gsap/src/uncompressed/easing/EasePack';

import selectablePopup from './lib/emoji-selector';
import addScript from './lib/add-script';
import {getItem, init as settingsInit} from './lib/settings';
import pushNotifications from './lib/push-notifications';
import tinycam from './lib/tinycam';
import {updateMessageTextInput, combineEmojis, isCombinableEmojis, init as initTextInput} from './lib/emoji-text-input';
import {init as messages} from './lib/messages';
import {init as touchInit} from './lib/touch';
import {init as apiInit, save as apiSave, getCorrespondents} from './lib/api';
import {init as shareInit} from './lib/share';

Promise.all([
	addScript('https://cdn.rawgit.com/AdaRoseEdwards/dirty-dom/v1.3.1/build/dirty-dom-lib.min.js').promise,
	addScript('https://twemoji.maxcdn.com/2/twemoji.min.js').promise,

	// some useful polyfills
	addScript('https://cdn.polyfill.io/v2/polyfill.min.js?features=fetch,default').promise,

	// patched array.from polyfill
	addScript('https://cdn.rawgit.com/Financial-Times/polyfill-service/Array.from-Handle-Iterable/polyfills/Array/from/polyfill.js').promise,
	addScript('/scripts/color-thief.js').promise
]).then(() => {

	/*
	* Set up the emoji Grid
	*/
	const subEmojis = [
		['😍', '😘', '🌈', '💕', '❤', '💔', '🌹', '👬', '👭'],       // Romance
		['😋', '😎', '😤', '😉', '😀', '😂', '😴', '😉', '😇'],        // Smiles
		['✍', '✋', '✌', '👐', '👌', '🖖', '👏', '👋', '🙋'],        // Hands
		['👜', '👒', '👘', '👖', '👞', '👠', '👢', '👕', '👚'], // Clothes
		['🍕', '☕', '🍸', '🍺', '🍷', '🍗', '🍒', '🍩', '🎂'],    // Foods
		['😐', '😑', '😶', '😣', '😥', '😮', '😪', '😫', '⬇'],   // Misc
		['😌', '🤓', '😛', '😜', '😝', '☹', '🙁', '😒', '😲'],     // Silly
		['😟', '😑', '💩', '😒', '🖕', '🤘', '👊', '🍖', '✌'],      // Angries
		['👅', '🍑', '💋', '🏩', '💩', '👉', '👌', '🍆', '💦']    // Adult
	];

	// main emojis is made out of middle emoji
	const mainEmojis = subEmojis.map(e => e[4]);

	const makeGrid = (emojis) => $(`

		${combineEmojis(emojis[0], getItem('skintone'))}
		${combineEmojis(emojis[1], getItem('skintone'))}
		${combineEmojis(emojis[2], getItem('skintone'))}
		${combineEmojis(emojis[3], getItem('skintone'))}
		${combineEmojis(emojis[4], getItem('skintone'))}
		${combineEmojis(emojis[5], getItem('skintone'))}
		${combineEmojis(emojis[6], getItem('skintone'))}
		${combineEmojis(emojis[7], getItem('skintone'))}
		${combineEmojis(emojis[8], getItem('skintone'))}

`);

	const mainGrid = makeGrid(mainEmojis);
	mainGrid.$$('.emoji__grid-item')
	.map(function (item, i) {

		const subGrid = makeGrid(subEmojis[i]);
		subGrid.dataset.emoji = subEmojis[i][4];
		subGrid.on('emojiSelect', e => {

			if (isCombinableEmojis(e.detail.emoji, getItem('skintone'))) {
				updateMessageTextInput(e.detail.emoji + getItem('skintone'));
			} else {
				updateMessageTextInput(e.detail.emoji);
			}

		});
		item.appendChild(subGrid);
		return subGrid;
	})
	.forEach(selectablePopup);

	$('#emoji__grid').appendChild(mainGrid);
	twemoji.parse(mainGrid);
	twemoji.parse($('#emoji__options-button'));

	function updateCorrespondentsList() {
		const c = getCorrespondents();
		const dom = $('#emoji_correspondents');
		dom.empty();
		for (const co of c) {
			if (co !== '@AnonymousUser') {
				dom.$(`<option selected>${co}</option>`);
			}
		}
	}

	$('#emoji__recipient').on('click', function () {
		this.value = '';
	});

	window.addEventListener('correrspondentsUpdated', updateCorrespondentsList);

	// Set up local storage caching
	apiInit().then(updateCorrespondentsList);

	// Add button interactions
	touchInit();

	// Set up push notification service
	pushNotifications();

	// load new messages
	messages();

	// Push notification camera.
	tinycam();

	// Set up the text input
	initTextInput();

	shareInit();

	settingsInit();

	window.addEventListener('unload', apiSave);
})
.catch(e => {

	// Script loading errors
	throw e;
});
<|endoftext|>


<commit_before>/*
 * Copyright 2016 Austin Lehman
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

"use strict";
/*global Java, isDef, setDef */

var $global, $ic9env, $ic9;

/** Set $global function. */
$global = function () {
    return this;
};

/** Set the global $ic9env flag. */
$ic9env = true;

/** Set the global $ic9 object. */
$ic9 = {};

/**
 * Sets a default value where none is set already. This
 * is useful in functions with optional parameters.
 * @param Variable is a var to check.
 * @param Value is the default value for the variable.
 * @returns The provided var.
 */
function setDef(Variable, Value) {
    Variable = Variable !== undefined ? Variable : Value;
    if (Variable === null) { Variable = Value; }
    return Variable;
}

/**
 * Checks to see if the passed variable is defined. This
 * is useful when requiring function parameters.
 * @param Variable is a variable to check.
 * @returns A boolean with true for defined and false
 * for undefined.
 */
function isDef(Variable) {
    if (Variable === undefined) { return false; }
    return true;
}

/**
 * Wrapper to check if a var is an array.
 * @param Variable is the var to check.
 * @returns A boolean with true if it's an array
 * and false if not.
 */
function isArr(Variable) {
    if (Array.isArray(Variable)) { return true; }
    return false;
}

/**
 * Checks to see if the provided variable is an Object.
 * @param Variable is a variable to check.
 * @returns A boolean with true for an Object and false for not.
 */
function isObj(Variable) {
    return Variable instanceof Object;
}

/**
 * Checks to see if a string starts with another string.
 * @param string is a string to check.
 * @param prefix is the prefix to check for.
 * @returns A boolean with true for starts with and false for not.
 */
function startsWith(string, prefix) {
    return string.slice(0, prefix.length) === prefix;
}

/**
 * Checks to see if an array or an object contains the provided
 * string. In the event of an object it will look for the key.
 * @param Haystack is an Array or Object to search.
 * @param Needle is a String to look for.
 * @returns {Boolean} True if found or false if not.
 */
function contains(Haystack, Needle) {
    if (Array.isArray(Haystack)) { return (Haystack.indexOf(Needle) > -1); }
    return Haystack.hasOwnProperty(Needle);
}

/**
 * Checks to see if the provided value is a string object.
 * @param Value is an object to check.
 * @returns {Boolean} True for is string and false for not.
 */
function isString(Value) {
    return typeof Value === 'string' || Value instanceof String;
}

/**
 * Checks to see if the provided value is a number object.
 * @param Value is an object to check.
 * @returns {Boolean} True for is number and false for not.
 */
function isNumber(Value) {
    return typeof Value === 'number' && isFinite(Value);
}

/**
 * Checks to see if the provided value is a boolean object.
 * @param Value is an object to check.
 * @returns {Boolean} True for is boolean and false for not.
 */
function isBool(Value) {
    return typeof Value === 'boolean' || Value instanceof Boolean;
}

/**
 * Checks to see if the provided value is a function.
 * @param Value is an object to check.
 * @returns {Boolean} True for is function and false if not.
 */
function isFunct(Value) {
    return typeof Value === 'function';
}

/**
 * Base object is a minimal ic9 object with a check to ensure current scope
 * isn't global and with a toString method.
 * @constructor
 */
function BaseObj() {
    if (this === $global()) { throw ("Attepmt to instantiate in global scope."); }
}

/*
 * Assert methods for testing cases.
 */
/**
 * Assert boolean.
 * @param Value is an object to test.
 * @throws An exception if not true.
 */
function assert(Value) {
    if (Value !== true) { throw ("assert(): Assertion exception."); }
}

/**
 * Asserts the provided value is a string.
 * @param Value is an object to test.
 * @throws An exception if not a string.
 */
function assertString(Value) {
    if (!(typeof Value === "string")) { throw ("assertString(): Assertion exception, not a string."); }
}

/**
 * Asserts the provided value is a boolean.
 * @param Value is an object to test.
 * @throws An exception if not a boolean.
 */
function assertBool(Value) {
    if (!(typeof Value === "boolean")) { throw ("assertBool(): Assertion exception, not a boolean."); }
}

/**
 * Asserts the provided value is a number.
 * @param Value is an object to test.
 * @throws An exception if not a number.
 */
function assertNumber(Value) {
    if (!(typeof Value === "number")) { throw ("assertNumber(): Assertion exception, not a number."); }
}

/**
 * Asserts the provided value is an integer.
 * @param Value is an object to test.
 * @throws An exception if not an integer.
 */
function assertInt(Value) {
    if (!(Number.isInteger(Value))) { throw ("assertInt(): Assertion exception, not an integer."); }
}

/**
 * Asserts the provided value is null.
 * @param Value is an object to test.
 * @throws An exception if not null.
 */
function assertNull(Value) {
    if (Value !== null) { throw ("assertNull(): Assertion exception, not null."); }
}

/**
 * Asserts the provided value is a function.
 * @param Value is an object to test.
 * @throws An exception if not a function.
 */
function assertFunction(Value) {
    if (typeof Value !== "function") { throw ("assertFunct(): Assertion exception, not a function."); }
}

/**
 * Asserts the provided value is an object.
 * @param Value is an object to test.
 * @throws An exception if not an object.
 */
function assertObject(Value) {
    if (!(typeof Value === "object")) { throw ("assertObject(): Assertion exception, not an object."); }
}

/**
 * Asserts the provided value is an array.
 * @param Value is an object to test.
 * @throws An exception if not an array.
 */
function assertArray(Value) {
    if (!Array.isArray(Value)) { throw ("assertArray(): Assertion exception, not an array."); }
}

/**
 * Turns any object or variable into a JSON string representation.
 * @param Jobj Is a Javascript object to turn into a JSON string.
 * @param Pretty Is a boolean with true for pretty print and false
 * for not. (Optional) (Default is true.)
 * @param Indent Is an integer with the number of tab indentations
 * to start with. This only applies if Pretty === true. (Optional)
 * (Default is 0.)
 */
function jStringify(Jobj, Pretty, Indent) {
    var rstr = "", count = 0, key, tstr = "", i, tres;
    Jobj = setDef(Jobj, {});
    Pretty = setDef(Pretty, true);
    Indent = setDef(Indent, 0);
    if (isArr(Jobj)) {
      for (i = 0; i < Jobj.length; i += 1) {
        if (Jobj[i] !== undefined) {
          if (i > 0) { tstr += ","; }
          if (Pretty === true && i > 0) { tstr += "\n"; }
          if (Pretty === true && isArr(Jobj[i]) === false && isObj(Jobj[i]) === false && typeof(Jobj[i]) !== "object") { tstr += jIndent(Indent + 1); }
          tstr += jStringify(Jobj[i], Pretty, Indent + 1);
          count += 1;
        }
      }
      if (Pretty === true && count > 0) {
        rstr += jIndent(Indent) + "[\n";
        rstr += tstr;
        if (tstr.trim() !== "") { rstr += "\n"; }
        rstr += jIndent(Indent) + "]";
      } else {
        rstr += "[" + tstr + "]"
      }
    } else if (isObj(Jobj) || typeof(Jobj) === "object") {
      for (key in Jobj) {
        if (Jobj.hasOwnProperty(key) && Jobj[key] !== undefined) {
          if (count > 0) { tstr += ","; }
          if (Pretty === true && count > 0) { tstr += "\n"; }
          tstr += jIndent(Indent + 1) + "\"" + key + "\":";
          tres = jStringify(Jobj[key], Pretty, Indent + 1);
          if (Pretty === true && tres.startsWith("[]") === false && tres.startsWith("{}") === false && (isArr(Jobj[key]) || isObj(Jobj[key]) || typeof(Jobj[key]) === "object")) { tstr += "\n"; }
          tstr += tres;
          count += 1;
        }
      }
      if (Pretty === true && count > 0) {
        rstr += jIndent(Indent) + "{\n";
        rstr += tstr;
        if (tstr.trim() !== "") { rstr += "\n"; }
        rstr += jIndent(Indent) + "}";
      } else {
        rstr += "{" + tstr + "}"
      }
    } else if (isNumber(Jobj) || isBool(Jobj)) {
      if (Pretty === true) { jIndent(Indent); }
      rstr += "" + Jobj;
    } else {
      rstr += "\"" + Jobj.escapeJson() + "\"";
    }
    return rstr;
  }

/*
 * Extended native types.
 */

/**
 * Shortcut for defining new member methods for any object. This
 * will add the function to the prototype.
 *

 * Standard way:
 *

 * MyObj.prototype.someFunct = function () { console.log("something"); };
 *

 * Using shortcut:
 *

 * MyObj.p("someFunct", function () { console.log("something"); };
 */
Function.prototype.p = function (MemberName, FunctionInstance) {
    this.prototype[MemberName] = FunctionInstance;
    return this;
};

/**
 * Add object contains method. This method works for both JS objects
 * and arrays.
 * @param Needle is the item to check for.
 * @return A boolean with true for contains and false for not.
 */
Object.defineProperty(Object.prototype, 'contains', {
    value : function (Needle) {
        if (Array.isArray(this)) { return (this.indexOf(Needle) > -1); }
        return this.hasOwnProperty(Needle);
    },
    writable: true,
    enumerable: false
});

/**
 * Mixin method takes the source object and
 * combines it into the current object.
 * @param Source is the source object to mix in.
 * @return Object instance.
 */
Object.defineProperty(Object.prototype, 'mixin', {
    value : function (Source) {
        var prop;
        for (prop in Source) {
            if (Source.hasOwnProperty(prop)) {
                this[prop] = Source[prop];
            }
        }
        return this;
    },
    writable: true,
    enumerable: false
});

/*jslint forin:true */
/**
 * Creates a string with a JSON representation of the object.
 * @param Pretty is a boolean with true for pretty print and false for not.
 * @param ShowMeths is a boolean with true for show methods and false for not.
 * @return A string of the object in JSON format.
 */
Object.defineProperty(Object.prototype, 'toString', {
    value : function (Pretty, ShowMeths) {
        var ret = "", mth;
        Pretty = setDef(Pretty, true);
        ret = jStringify(this, Pretty);
        if (ShowMeths) {
            ret += "\n";
            for (mth in this) {
                if (isFunct(this[mth])) {
                    ret += "Method: " + mth + "\n";
                }
            }
        }
        return ret;
    },
    writable: true,
    enumerable: false
});
/*jslint forin:false */

/**
 * Gets the number of properties in the object. This
 * method uses hasOwnProperty to filter for just
 * the properties.
 * @return An int with the number of properties.
 */
Object.defineProperty(Object.prototype, 'length', {
    value : function () {
        var cnt = 0, key;
        for (key in this) {
            if (this.hasOwnProperty(key)) {
                cnt += 1;
            }
        }
        return cnt;
    },
    writable: true,
    enumerable: false
});

/**
 * Add contains member to Strig prototype.
 * @param Str is a String to test for.
 * @return A boolean with true for contains and false for not.
 */
Object.defineProperty(String.prototype, 'contains', {
    value : function (Str) {
        return this.indexOf(Str) !== -1;
    },
    writable: true,
    enumerable: false
});

/**
 * Escapes special chars for packaging in a JSON string.
 * @return A string with escaped characters.
 */
Object.defineProperty(String.prototype, 'escapeJson', {
    value : function () {
        return this
        .replace(/[\"]/g, '\\"')
        .replace(/[\/]/g, '\\/')
        .replace(/[\b]/g, '\\b')
        .replace(/[\f]/g, '\\f')
        .replace(/[\n]/g, '\\n')
        .replace(/[\r]/g, '\\r')
        .replace(/[\t]/g, '\\t')
      ;
    },
    writable: true,
    enumerable: false
});

/**
 * Gets the item at the specified index. This
 * can be used from Java as an accessor to get
 * an item at the index.
 * @param Index is the index to get the item at.
 * @return A Javascript object at the index.
 */
Array.prototype.get = function (Index) {
    return this[Index];
};

/**
 * Generic JS exception.
 * @param Message is a string with the exception message.
 * @param Name is a string with the exception name. (Optional)
 */
function Exception(Message, Name) {
    BaseObj.call(this);
    this.message = setDef(Message, "");
    this.name = setDef(Name, "");
}
Exception.prototype = new BaseObj();
Exception.prototype.constructor = Exception;

<commit_msg>Added missing jIndent function.<commit_after>/*
 * Copyright 2016 Austin Lehman
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

"use strict";
/*global Java, isDef, setDef */

var $global, $ic9env, $ic9;

/** Set $global function. */
$global = function () {
    return this;
};

/** Set the global $ic9env flag. */
$ic9env = true;

/** Set the global $ic9 object. */
$ic9 = {};

/**
 * Sets a default value where none is set already. This
 * is useful in functions with optional parameters.
 * @param Variable is a var to check.
 * @param Value is the default value for the variable.
 * @returns The provided var.
 */
function setDef(Variable, Value) {
    Variable = Variable !== undefined ? Variable : Value;
    if (Variable === null) { Variable = Value; }
    return Variable;
}

/**
 * Checks to see if the passed variable is defined. This
 * is useful when requiring function parameters.
 * @param Variable is a variable to check.
 * @returns A boolean with true for defined and false
 * for undefined.
 */
function isDef(Variable) {
    if (Variable === undefined) { return false; }
    return true;
}

/**
 * Wrapper to check if a var is an array.
 * @param Variable is the var to check.
 * @returns A boolean with true if it's an array
 * and false if not.
 */
function isArr(Variable) {
    if (Array.isArray(Variable)) { return true; }
    return false;
}

/**
 * Checks to see if the provided variable is an Object.
 * @param Variable is a variable to check.
 * @returns A boolean with true for an Object and false for not.
 */
function isObj(Variable) {
    return Variable instanceof Object;
}

/**
 * Checks to see if a string starts with another string.
 * @param string is a string to check.
 * @param prefix is the prefix to check for.
 * @returns A boolean with true for starts with and false for not.
 */
function startsWith(string, prefix) {
    return string.slice(0, prefix.length) === prefix;
}

/**
 * Checks to see if an array or an object contains the provided
 * string. In the event of an object it will look for the key.
 * @param Haystack is an Array or Object to search.
 * @param Needle is a String to look for.
 * @returns {Boolean} True if found or false if not.
 */
function contains(Haystack, Needle) {
    if (Array.isArray(Haystack)) { return (Haystack.indexOf(Needle) > -1); }
    return Haystack.hasOwnProperty(Needle);
}

/**
 * Checks to see if the provided value is a string object.
 * @param Value is an object to check.
 * @returns {Boolean} True for is string and false for not.
 */
function isString(Value) {
    return typeof Value === 'string' || Value instanceof String;
}

/**
 * Checks to see if the provided value is a number object.
 * @param Value is an object to check.
 * @returns {Boolean} True for is number and false for not.
 */
function isNumber(Value) {
    return typeof Value === 'number' && isFinite(Value);
}

/**
 * Checks to see if the provided value is a boolean object.
 * @param Value is an object to check.
 * @returns {Boolean} True for is boolean and false for not.
 */
function isBool(Value) {
    return typeof Value === 'boolean' || Value instanceof Boolean;
}

/**
 * Checks to see if the provided value is a function.
 * @param Value is an object to check.
 * @returns {Boolean} True for is function and false if not.
 */
function isFunct(Value) {
    return typeof Value === 'function';
}

/**
 * Base object is a minimal ic9 object with a check to ensure current scope
 * isn't global and with a toString method.
 * @constructor
 */
function BaseObj() {
    if (this === $global()) { throw ("Attepmt to instantiate in global scope."); }
}

/*
 * Assert methods for testing cases.
 */
/**
 * Assert boolean.
 * @param Value is an object to test.
 * @throws An exception if not true.
 */
function assert(Value) {
    if (Value !== true) { throw ("assert(): Assertion exception."); }
}

/**
 * Asserts the provided value is a string.
 * @param Value is an object to test.
 * @throws An exception if not a string.
 */
function assertString(Value) {
    if (!(typeof Value === "string")) { throw ("assertString(): Assertion exception, not a string."); }
}

/**
 * Asserts the provided value is a boolean.
 * @param Value is an object to test.
 * @throws An exception if not a boolean.
 */
function assertBool(Value) {
    if (!(typeof Value === "boolean")) { throw ("assertBool(): Assertion exception, not a boolean."); }
}

/**
 * Asserts the provided value is a number.
 * @param Value is an object to test.
 * @throws An exception if not a number.
 */
function assertNumber(Value) {
    if (!(typeof Value === "number")) { throw ("assertNumber(): Assertion exception, not a number."); }
}

/**
 * Asserts the provided value is an integer.
 * @param Value is an object to test.
 * @throws An exception if not an integer.
 */
function assertInt(Value) {
    if (!(Number.isInteger(Value))) { throw ("assertInt(): Assertion exception, not an integer."); }
}

/**
 * Asserts the provided value is null.
 * @param Value is an object to test.
 * @throws An exception if not null.
 */
function assertNull(Value) {
    if (Value !== null) { throw ("assertNull(): Assertion exception, not null."); }
}

/**
 * Asserts the provided value is a function.
 * @param Value is an object to test.
 * @throws An exception if not a function.
 */
function assertFunction(Value) {
    if (typeof Value !== "function") { throw ("assertFunct(): Assertion exception, not a function."); }
}

/**
 * Asserts the provided value is an object.
 * @param Value is an object to test.
 * @throws An exception if not an object.
 */
function assertObject(Value) {
    if (!(typeof Value === "object")) { throw ("assertObject(): Assertion exception, not an object."); }
}

/**
 * Asserts the provided value is an array.
 * @param Value is an object to test.
 * @throws An exception if not an array.
 */
function assertArray(Value) {
    if (!Array.isArray(Value)) { throw ("assertArray(): Assertion exception, not an array."); }
}

/**
 * Turns any object or variable into a JSON string representation.
 * @param Jobj Is a Javascript object to turn into a JSON string.
 * @param Pretty Is a boolean with true for pretty print and false
 * for not. (Optional) (Default is true.)
 * @param Indent Is an integer with the number of tab indentations
 * to start with. This only applies if Pretty === true. (Optional)
 * (Default is 0.)
 */
function jStringify(Jobj, Pretty, Indent) {
    var rstr = "", count = 0, key, tstr = "", i, tres;
    Jobj = setDef(Jobj, {});
    Pretty = setDef(Pretty, true);
    Indent = setDef(Indent, 0);
    if (isArr(Jobj)) {
      for (i = 0; i < Jobj.length; i += 1) {
        if (Jobj[i] !== undefined) {
          if (i > 0) { tstr += ","; }
          if (Pretty === true && i > 0) { tstr += "\n"; }
          if (Pretty === true && isArr(Jobj[i]) === false && isObj(Jobj[i]) === false && typeof(Jobj[i]) !== "object") { tstr += jIndent(Indent + 1); }
          tstr += jStringify(Jobj[i], Pretty, Indent + 1);
          count += 1;
        }
      }
      if (Pretty === true && count > 0) {
        rstr += jIndent(Indent) + "[\n";
        rstr += tstr;
        if (tstr.trim() !== "") { rstr += "\n"; }
        rstr += jIndent(Indent) + "]";
      } else {
        rstr += "[" + tstr + "]"
      }
    } else if (isObj(Jobj) || typeof(Jobj) === "object") {
      for (key in Jobj) {
        if (Jobj.hasOwnProperty(key) && Jobj[key] !== undefined) {
          if (count > 0) { tstr += ","; }
          if (Pretty === true && count > 0) { tstr += "\n"; }
          tstr += jIndent(Indent + 1) + "\"" + key + "\":";
          tres = jStringify(Jobj[key], Pretty, Indent + 1);
          if (Pretty === true && tres.startsWith("[]") === false && tres.startsWith("{}") === false && (isArr(Jobj[key]) || isObj(Jobj[key]) || typeof(Jobj[key]) === "object")) { tstr += "\n"; }
          tstr += tres;
          count += 1;
        }
      }
      if (Pretty === true && count > 0) {
        rstr += jIndent(Indent) + "{\n";
        rstr += tstr;
        if (tstr.trim() !== "") { rstr += "\n"; }
        rstr += jIndent(Indent) + "}";
      } else {
        rstr += "{" + tstr + "}"
      }
    } else if (isNumber(Jobj) || isBool(Jobj)) {
      if (Pretty === true) { jIndent(Indent); }
      rstr += "" + Jobj;
    } else {
      rstr += "\"" + Jobj.escapeJson() + "\"";
    }
    return rstr;
}

/**
 * Returns a string of tabs with the number equal to the integer
 * provided as an argument.
 * @param Indent Is an integer with the number of tab spaces to indent.
 * @returns A string with tab characters.
 */
function jIndent(Indent) {
    Indent = setDef(Indent, 0);
    var istr = "";
    for (var i = 0; i < Indent; i += 1) { istr += "\t"; }
    return istr;
}

/*
 * Extended native types.
 */

/**
 * Shortcut for defining new member methods for any object. This
 * will add the function to the prototype.
 *

 * Standard way:
 *

 * MyObj.prototype.someFunct = function () { console.log("something"); };
 *

 * Using shortcut:
 *

 * MyObj.p("someFunct", function () { console.log("something"); };
 */
Function.prototype.p = function (MemberName, FunctionInstance) {
    this.prototype[MemberName] = FunctionInstance;
    return this;
};

/**
 * Add object contains method. This method works for both JS objects
 * and arrays.
 * @param Needle is the item to check for.
 * @return A boolean with true for contains and false for not.
 */
Object.defineProperty(Object.prototype, 'contains', {
    value : function (Needle) {
        if (Array.isArray(this)) { return (this.indexOf(Needle) > -1); }
        return this.hasOwnProperty(Needle);
    },
    writable: true,
    enumerable: false
});

/**
 * Mixin method takes the source object and
 * combines it into the current object.
 * @param Source is the source object to mix in.
 * @return Object instance.
 */
Object.defineProperty(Object.prototype, 'mixin', {
    value : function (Source) {
        var prop;
        for (prop in Source) {
            if (Source.hasOwnProperty(prop)) {
                this[prop] = Source[prop];
            }
        }
        return this;
    },
    writable: true,
    enumerable: false
});

/*jslint forin:true */
/**
 * Creates a string with a JSON representation of the object.
 * @param Pretty is a boolean with true for pretty print and false for not.
 * @param ShowMeths is a boolean with true for show methods and false for not.
 * @return A string of the object in JSON format.
 */
Object.defineProperty(Object.prototype, 'toString', {
    value : function (Pretty, ShowMeths) {
        var ret = "", mth;
        Pretty = setDef(Pretty, true);
        ret = jStringify(this, Pretty);
        if (ShowMeths) {
            ret += "\n";
            for (mth in this) {
                if (isFunct(this[mth])) {
                    ret += "Method: " + mth + "\n";
                }
            }
        }
        return ret;
    },
    writable: true,
    enumerable: false
});
/*jslint forin:false */

/**
 * Gets the number of properties in the object. This
 * method uses hasOwnProperty to filter for just
 * the properties.
 * @return An int with the number of properties.
 */
Object.defineProperty(Object.prototype, 'length', {
    value : function () {
        var cnt = 0, key;
        for (key in this) {
            if (this.hasOwnProperty(key)) {
                cnt += 1;
            }
        }
        return cnt;
    },
    writable: true,
    enumerable: false
});

/**
 * Add contains member to Strig prototype.
 * @param Str is a String to test for.
 * @return A boolean with true for contains and false for not.
 */
Object.defineProperty(String.prototype, 'contains', {
    value : function (Str) {
        return this.indexOf(Str) !== -1;
    },
    writable: true,
    enumerable: false
});

/**
 * Escapes special chars for packaging in a JSON string.
 * @return A string with escaped characters.
 */
Object.defineProperty(String.prototype, 'escapeJson', {
    value : function () {
        return this
        .replace(/[\"]/g, '\\"')
        .replace(/[\/]/g, '\\/')
        .replace(/[\b]/g, '\\b')
        .replace(/[\f]/g, '\\f')
        .replace(/[\n]/g, '\\n')
        .replace(/[\r]/g, '\\r')
        .replace(/[\t]/g, '\\t')
      ;
    },
    writable: true,
    enumerable: false
});

/**
 * Gets the item at the specified index. This
 * can be used from Java as an accessor to get
 * an item at the index.
 * @param Index is the index to get the item at.
 * @return A Javascript object at the index.
 */
Array.prototype.get = function (Index) {
    return this[Index];
};

/**
 * Generic JS exception.
 * @param Message is a string with the exception message.
 * @param Name is a string with the exception name. (Optional)
 */
function Exception(Message, Name) {
    BaseObj.call(this);
    this.message = setDef(Message, "");
    this.name = setDef(Name, "");
}
Exception.prototype = new BaseObj();
Exception.prototype.constructor = Exception;

<|endoftext|>


<commit_before>const { merge } = require('webpack-merge');
const HardSourceWebpackPlugin = require('hard-source-webpack-plugin');

const common = require('./webpack.common');

module.exports = merge(common, {
  mode: 'production',
  output: {
    filename: '[name]-[contenthash].js',
    publicPath: '/webpack/',
  },
  plugins: [
    new HardSourceWebpackPlugin({
      cacheDirectory: join(__dirname, 'hard-source-cache/[confighash]'),
    }),
  ],
});
<commit_msg>chore(webpack): add tree-shaking to prod builds<commit_after>const { merge } = require('webpack-merge');
const HardSourceWebpackPlugin = require('hard-source-webpack-plugin');

const common = require('./webpack.common');

module.exports = merge(common, {
  mode: 'production',
  output: {
    filename: '[name]-[contenthash].js',
    publicPath: '/webpack/',
  },
  plugins: [
    new HardSourceWebpackPlugin({
      cacheDirectory: join(__dirname, 'hard-source-cache/[confighash]'),
    }),
  ],
  optimization: {
    usedExports: true,
  },
});
<|endoftext|>


<commit_before>var timelineApp = angular.module('timelineApp', []);

timelineApp.controller('TimelineCtrl', function ($scope) {
    $scope.show = 10;
    var maxScrollTop = 0;

    var refresh = function () {
        $.get('../utilities/get_timeline.php', function (d) {
            var evts = JSON.parse(d);
            $scope.events = evts.reverse();
            $scope.$apply()
        });
    };

    $(window).scroll(function() {
        if($(window).scrollTop() + $(window).height() > $(document).height() - 100) {
            console.log('loading more');
            if($scope.events.length >= $scope.show){
                if($(window).scrollTop() > maxScrollTop) {
                    maxScrollTop = $(window).scrollTop();
                    $scope.show += 10;
                }
            }
            else{
                console.log('ignoring')
            }

            $scope.$apply();
        }
    });

    refresh();
    setInterval(refresh, 2000);
});<commit_msg>fixes<commit_after>var timelineApp = angular.module('timelineApp', []);

timelineApp.controller('TimelineCtrl', function ($scope) {
    $scope.show = 10;
    var maxScrollTop = 0;

    var refresh = function () {
        $.get('../utilities/get_timeline.php', function (d) {
            var evts = JSON.parse(d);
            $scope.events = evts.reverse();
            $scope.$apply()
        });
    };

    $(window).scroll(function() {
        if($(window).scrollTop() + $(window).height() > $(document).height() - 100) {
            if($scope.events.length >= $scope.show){
                if($(window).scrollTop() > maxScrollTop) {
                    maxScrollTop = $(window).scrollTop();
                    $scope.show += 10;
                    $scope.$apply();
                    console.log('loading more');
                }
            }
            else{
                console.log('ignoring')
            }
        }
    });

    refresh();
    setInterval(refresh, 2000);
});<|endoftext|>


<commit_before>$(function(){
	r();
	$("a.group").fancybox({
			'transitionIn'	:	'elastic',
			'transitionOut'	:	'elastic',
			'speedIn'		:	600,
			'speedOut'		:	200,
			'overlayShow'	:	true
		});
	$("a.iframe").fancybox({
			'transitionIn'	:	'elastic',
			'transitionOut'	:	'elastic',
			'speedIn'		:	600,
			'speedOut'		:	200,
			'width'			:   842,
			'height'		:   '85%',
			'overlayShow'	:	true
		});

	$('a.iframe').bind("mouseover", function(){
		$('div#metadata')
						.show()
						.load('/interface/show/description/' + $(this).attr('id') + "/0/")
						.css({'top': ($(this).offset().top+16) + 'px', 'left': $(this).offset().left + 'px'});
	});
	$('a.iframe').bind("mouseout", function(){$('div#metadata').hide();});

	// menu item hiding
	$('div.menu-passive').hide();
	$('div#menuContent > div.menu-passive').show();
	var folder_id = location.pathname.replace(/^\/+|\/+$/g, '').replace('gui/folder/', '').replace('/', '_');
	if (folder_id){
    	var selector = 'div#' + folder_id + ', div#' + folder_id + ' > div.menu-passive';
        // $('#debug').text(selector + ' | ' + $(selector).length + ' | ' + $('div#' + folder_id).length);
    	$(selector).show().siblings().show();
    	$('div#' + folder_id + ' a').addClass('active');
	}

	// change sorting

    $('div#sorting').click(function(){
        $.get('/toggle/sorting/');
        setTimeout(function(){location.reload();}, 500);
    });
    $('ul#pagesize-container').hide();
    $('#page_size').click(function(){
        var position = $(this).position();
        $('ul#pagesize-container').css({'top': (position.top + 14) + "px", 'left': (position.left) + "px"}).toggle();
        setTimeout(function(){$('ul#pagesize-container').fadeOut();}, 5000 );
    });
    $('ul#pagesize-container li').click(function(){
        $.get('/change/pagesize/' + $(this).attr('title') + '/');
        setTimeout(function(){location.reload();}, 500);
    });

});

function r(){
	// render cart
	 $('div#cart').hide().load('/cart/show/').show('fast');
}
<commit_msg>change url for test<commit_after>$(function(){
	r();
	$("a.group").fancybox({
			'transitionIn'	:	'elastic',
			'transitionOut'	:	'elastic',
			'speedIn'		:	600,
			'speedOut'		:	200,
			'overlayShow'	:	true
		});
	$("a.iframe").fancybox({
			'transitionIn'	:	'elastic',
			'transitionOut'	:	'elastic',
			'speedIn'		:	600,
			'speedOut'		:	200,
			'width'			:   842,
			'height'		:   '85%',
			'overlayShow'	:	true
		});

	$('a.iframe').bind("mouseover", function(){
		$('div#metadata')
						.show()
						.load('/interface/show/description/' + $(this).attr('id') + "/0/")
						.css({'top': ($(this).offset().top+16) + 'px', 'left': $(this).offset().left + 'px'});
	});
	$('a.iframe').bind("mouseout", function(){$('div#metadata').hide();});

	// menu item hiding
	$('div.menu-passive').hide();
	$('div#menuContent > div.menu-passive').show();
	var folder_id = location.pathname.replace(/^\/+|\/+$/g, '').replace('gui/folder/', '').replace('/', '_');
	if (folder_id){
    	var selector = 'div#' + folder_id + ', div#' + folder_id + ' > div.menu-passive';
        // $('#debug').text(selector + ' | ' + $(selector).length + ' | ' + $('div#' + folder_id).length);
    	$(selector).show().siblings().show();
    	$('div#' + folder_id + ' a').addClass('active');
	}

	// change sorting

    $('div#sorting').click(function(){
        $.get('/gui/toggle/sorting/');
        setTimeout(function(){location.reload();}, 500);
    });
    $('ul#pagesize-container').hide();
    $('#page_size').click(function(){
        var position = $(this).position();
        $('ul#pagesize-container').css({'top': (position.top + 14) + "px", 'left': (position.left) + "px"}).toggle();
        setTimeout(function(){$('ul#pagesize-container').fadeOut();}, 5000 );
    });
    $('ul#pagesize-container li').click(function(){
        $.get('/gui/change/pagesize/' + $(this).attr('title') + '/');
        setTimeout(function(){location.reload();}, 500);
    });

});

function r(){
	// render cart
	 $('div#cart').hide().load('/cart/show/').show('fast');
}
<|endoftext|>


<commit_before>import BootstrapAlpha from './bootstrapAlpha.js'

$(document).ready(() => {
  BootstrapAlpha.langToogle($)
  BootstrapAlpha.logoSize($)

  $(window).resize(function() {
    BootstrapAlpha.logoSize()
  })
})
<commit_msg>Corrige estilo<commit_after>import BootstrapAlpha from './bootstrapAlpha.js'

$(document).ready(() => {
  BootstrapAlpha.langToogle($)
  BootstrapAlpha.logoSize($)

  $(window).resize(function () {
    BootstrapAlpha.logoSize()
  })
})
<|endoftext|>


<commit_before>var fs = require('fs');
var util = require('util');
var libpb = require('libprofitbricks');
var SSH = require('simple-ssh');
var format = util.format;

var dummyCb = function() {};
// 5 seconds.
var SERVER_QUERY_INTERVAL = 5000;
// 3 minutes total.
var MAX_QUERY_ATTEMPTS = 36;

var PbServer = function(pb, ssh, logger) {
  this.pb = pb;
  this.ssh = ssh;
  if (logger) {
    this.logger = logger;
  }
  else {
    this.logger = console;
    this.logger.debug = this.logger.log;
  }
  this.stateChangeQueryInterval = pb.stateChangeQueryInterval ? pb.stateChangeQueryInterval : SERVER_QUERY_INTERVAL;
  this.maxStateChangeQueryAttempts = pb.maxStateChangeQueryAttempts ? pb.maxStateChangeQueryAttempts : MAX_QUERY_ATTEMPTS;
  this.sshKey = ssh.key ? fs.readFileSync(ssh.key) : null;

  libpb.setauth(this.pb.username, this.pb.password);
  libpb.setdepth(this.pb.depth);

  var self = this;
  var _tracked = function(command, serverToState, vmToState, message, cb) {
    cb = cb ? cb : dummyCb;
    var postCommand = function(err) {
      if (err) {
        self.logger.error('%s command returned error: %s', command, err);
        cb(err);
      }
      else {
        var stateChangeCallback = function(err, data) {
          if (err) {
            self.logger.error('State change returned error: %s', err);
            cb(err);
          }
          else {
            self.logger.info(message);
            cb(null, data);
          }
        }
        self.serverStateChange(serverToState, vmToState, stateChangeCallback);
      }
    }
    self[command](postCommand);
  }

  this.startServerTracked = function(cb) {
    _tracked('startServer', 'AVAILABLE', 'RUNNING', 'Server started!', cb);
  }

  this.shutdownServerTracked = function(cb) {
    _tracked('shutdownServer', 'AVAILABLE', 'SHUTOFF', 'Server shut down!', cb);
  }

  this.stopServerTracked = function(cb) {
    _tracked('stopServer', 'INACTIVE', 'SHUTOFF', 'Server stopped!', cb);
  }
}

PbServer.prototype.listDatacenters = function(cb) {
  var self = this;
  cb = cb ? cb : dummyCb;
  var apiCallback = function(err, resp, body) {
    if (err) {
      self.logger.error(format("ERROR: %s", err));
      cb(err);
    }
    else {
      var data = JSON.parse(body);
      cb(null, data);
    }
  }
  this.logger.info("Getting datacenter info...");
  libpb.listDatacenters(apiCallback);
}

PbServer.prototype.listServers = function(cb) {
  var self = this;
  cb = cb ? cb : dummyCb;
  var apiCallback = function(err, resp, body) {
    if (err) {
      self.logger.error(format("ERROR: %s", err));
      cb(err);
    }
    else {
      var data = JSON.parse(body);
      cb(null, data);
    }
  }
  this.logger.info("Listing servers...");
  libpb.listServers(this.pb.datacenterId, apiCallback)
}

PbServer.prototype.getServer = function(cb) {
  var self = this;
  cb = cb ? cb : dummyCb;
  var apiCallback = function(err, resp, body) {
    if (err) {
      self.logger.error(format("ERROR: %s", err));
      cb(err);
    }
    else {
      var data = JSON.parse(body);
      cb(null, data);
    }
  }
  this.logger.info("Getting server status...");
  libpb.getServer(this.pb.datacenterId, this.pb.serverId, apiCallback)
}

PbServer.prototype.startServer = function(cb) {
  this.logger.info("Starting server...");
  libpb.startServer(this.pb.datacenterId, this.pb.serverId, cb)
}

PbServer.prototype.stopServer = function(cb) {
  this.logger.info("Powering off server...");
  libpb.stopServer(this.pb.datacenterId, this.pb.serverId, cb)
}

PbServer.prototype.shutdownServer = function(cb) {
  var self = this;
  cb = cb ? cb : dummyCb;
  this.logger.info("Shutting down server...");
  var ssh = new SSH({
    host: this.ssh.host,
    port: this.ssh.port,
    user: this.ssh.user,
    key: this.sshKey,
  });
  var exit = function(code, stdout, stderr) {
    self.logger.debug(format("SSH command exit code: %s", code));
    if (code === 0) {
      self.logger.debug("Shutdown command execution succeeded...");
      cb(null, code);
    }
    else {
      var message = format('SSH command returned with error code: %d, %s', code, stderr);
      self.logger.error(message);
      cb(message);
    }
  }
  var execConfig = {
    exit: exit,
  };
  var startConfig = {
    success: function() {
      self.logger.debug("SSH connection successful...");
    },
    fail: function(err) {
      self.logger.debug(format("SSH connection failed: %s", err));
      cb(err);
    },
  }
  // Executing shutdown without backgrounding hangs, backgrounding the command
  // allows the shutdown to proceed, and our SSH command to get a return value.
  ssh.exec('shutdown -P now shutdown-now&', execConfig).start(startConfig);
}

PbServer.prototype.serverStateChange = function(serverToState, vmToState, cb) {
  var self = this;
  cb = cb ? cb : dummyCb;
  var count = 1;
  var checkState = function(err, data) {
    if (err) {
      self.logger.error(format("ERROR: %s", err));
      cb(err);
    }
    else {
      if (count > self.maxStateChangeQueryAttempts) {
        clearInterval(serverStateChange);
        var message = "Max attempts exceeded.";
        self.logger.error(message);
        cb(message);
      }
      else {
        var serverState = data.metadata.state;
        var vmState = data.properties.vmState;
        self.logger.debug(format("Attempt #%d", count));
        self.logger.debug("-------------------------------------");
        self.logger.debug(format("Server state: %s", serverState));
        self.logger.debug(format("VM state: %s", vmState));
        self.logger.debug("-------------------------------------");
        if (serverState == serverToState && vmState == vmToState) {
          self.logger.info(format("State change to (%s, %s) complete", serverToState , vmToState));
          clearInterval(serverStateChange);
          cb(null, data);
        }
        count++;
      }
    }
  }
  var get = function() {
    self.getServer(checkState);
  }
  this.logger.info(format("Waiting for server state to change to (%s, %s)", serverToState , vmToState));
  get();
  var serverStateChange = setInterval(get, this.stateChangeQueryInterval);
}

PbServer.prototype.checkCommand = function(command, cb) {
  var self = this;
  cb = cb ? cb : dummyCb;
  var count = 1;
  // This prevents overlapping checks and messages.
  var timeout = this.stateChangeQueryInterval - 1000;
  this.logger.debug(format("Checking command: %s", command));
  this.logger.debug(format("SSH connection timeout set to %d milliseconds", timeout));
  var exit = function(code, stdout, stderr) {
    if (code === 0) {
      clearInterval(checkCommand);
      self.logger.info("Command succeeded");
      cb(null, code);
    }
    else {
      self.logger.debug(format('Command returned with error code: %d, %s', code, stderr));
    }
  }
  var execConfig = {
    exit: exit,
  };
  var startConfig = {
    success: function() {
      self.logger.debug("SSH connection successful...");
    },
    fail: function(err) {
      self.logger.debug(format("SSH connection failed: %s", err));
    },
  }
  var check = function() {
    if (count > self.maxStateChangeQueryAttempts) {
      clearInterval(checkCommand);
      var message = "Max attempts exceeded.";
      self.logger.error(message);
      cb(message);
    }
    else {
      self.logger.debug(format("Attempt #%d", count));
      var ssh = new SSH({
        host: self.ssh.host,
        port: self.ssh.port,
        user: self.ssh.user,
        key: self.sshKey,
        timeout: timeout,
      });
      ssh.exec(command, execConfig).start(startConfig);
      count++;
    }
  }
  check();
  var checkCommand = setInterval(check, this.stateChangeQueryInterval);
}

PbServer.prototype.updateServer = function(profile, cb) {
  var self = this;
  cb = cb ? cb : dummyCb;
  var apiCallback = function(err, resp, body) {
    if (err) {
      self.logger.error(format("ERROR: %s", err));
      cb(err);
    }
    else {
      var data = JSON.parse(body);
      cb(null, data);
    }
  }
  this.logger.info(format("Updating server to profile: %s", profile));
  var data = this.pb.profiles[profile];
  if (data) {
    var updateData = {
      properties: data,
    }
    libpb.updateServer(this.pb.datacenterId, this.pb.serverId, updateData, apiCallback);
  }
  else {
    this.logger.error(format("ERROR: profile '%s' does not exist", profile));
  }
}

if (module.exports) {
  module.exports = PbServer;
}
<commit_msg>variable renaming for parity with cli.<commit_after>var fs = require('fs');
var util = require('util');
var libpb = require('libprofitbricks');
var SSH = require('simple-ssh');
var format = util.format;

var dummyCb = function() {};
// 5 seconds.
var SERVER_QUERY_INTERVAL = 5000;
// 3 minutes total.
var MAX_QUERY_ATTEMPTS = 36;

var PbServer = function(pb, ssh, logger) {
  this.pb = pb;
  this.ssh = ssh;
  if (logger) {
    this.logger = logger;
  }
  else {
    this.logger = console;
    this.logger.debug = this.logger.log;
  }
  this.stateChangeQueryInterval = pb.stateChangeQueryInterval ? pb.stateChangeQueryInterval : SERVER_QUERY_INTERVAL;
  this.maxStateChangeQueryAttempts = pb.maxStateChangeQueryAttempts ? pb.maxStateChangeQueryAttempts : MAX_QUERY_ATTEMPTS;
  this.sshKey = ssh.key ? fs.readFileSync(ssh.key) : null;

  libpb.setauth(this.pb.username, this.pb.password);
  libpb.setdepth(this.pb.depth);

  var self = this;
  var _tracked = function(command, machineToState, serverToState, message, cb) {
    cb = cb ? cb : dummyCb;
    var postCommand = function(err) {
      if (err) {
        self.logger.error('%s command returned error: %s', command, err);
        cb(err);
      }
      else {
        var stateChangeCallback = function(err, data) {
          if (err) {
            self.logger.error('State change returned error: %s', err);
            cb(err);
          }
          else {
            self.logger.info(message);
            cb(null, data);
          }
        }
        self.serverStateChange(machineToState, serverToState, stateChangeCallback);
      }
    }
    self[command](postCommand);
  }

  this.startServerTracked = function(cb) {
    _tracked('startServer', 'AVAILABLE', 'RUNNING', 'Server started!', cb);
  }

  this.shutdownServerTracked = function(cb) {
    _tracked('shutdownServer', 'AVAILABLE', 'SHUTOFF', 'Server shut down!', cb);
  }

  this.stopServerTracked = function(cb) {
    _tracked('stopServer', 'INACTIVE', 'SHUTOFF', 'Server stopped!', cb);
  }
}

PbServer.prototype.listDatacenters = function(cb) {
  var self = this;
  cb = cb ? cb : dummyCb;
  var apiCallback = function(err, resp, body) {
    if (err) {
      self.logger.error(format("ERROR: %s", err));
      cb(err);
    }
    else {
      var data = JSON.parse(body);
      cb(null, data);
    }
  }
  this.logger.info("Getting datacenter info...");
  libpb.listDatacenters(apiCallback);
}

PbServer.prototype.listServers = function(cb) {
  var self = this;
  cb = cb ? cb : dummyCb;
  var apiCallback = function(err, resp, body) {
    if (err) {
      self.logger.error(format("ERROR: %s", err));
      cb(err);
    }
    else {
      var data = JSON.parse(body);
      cb(null, data);
    }
  }
  this.logger.info("Listing servers...");
  libpb.listServers(this.pb.datacenterId, apiCallback)
}

PbServer.prototype.getServer = function(cb) {
  var self = this;
  cb = cb ? cb : dummyCb;
  var apiCallback = function(err, resp, body) {
    if (err) {
      self.logger.error(format("ERROR: %s", err));
      cb(err);
    }
    else {
      var data = JSON.parse(body);
      cb(null, data);
    }
  }
  this.logger.info("Getting server status...");
  libpb.getServer(this.pb.datacenterId, this.pb.serverId, apiCallback)
}

PbServer.prototype.startServer = function(cb) {
  this.logger.info("Starting server...");
  libpb.startServer(this.pb.datacenterId, this.pb.serverId, cb)
}

PbServer.prototype.stopServer = function(cb) {
  this.logger.info("Powering off server...");
  libpb.stopServer(this.pb.datacenterId, this.pb.serverId, cb)
}

PbServer.prototype.shutdownServer = function(cb) {
  var self = this;
  cb = cb ? cb : dummyCb;
  this.logger.info("Shutting down server...");
  var ssh = new SSH({
    host: this.ssh.host,
    port: this.ssh.port,
    user: this.ssh.user,
    key: this.sshKey,
  });
  var exit = function(code, stdout, stderr) {
    self.logger.debug(format("SSH command exit code: %s", code));
    if (code === 0) {
      self.logger.debug("Shutdown command execution succeeded...");
      cb(null, code);
    }
    else {
      var message = format('SSH command returned with error code: %d, %s', code, stderr);
      self.logger.error(message);
      cb(message);
    }
  }
  var execConfig = {
    exit: exit,
  };
  var startConfig = {
    success: function() {
      self.logger.debug("SSH connection successful...");
    },
    fail: function(err) {
      self.logger.debug(format("SSH connection failed: %s", err));
      cb(err);
    },
  }
  // Executing shutdown without backgrounding hangs, backgrounding the command
  // allows the shutdown to proceed, and our SSH command to get a return value.
  ssh.exec('shutdown -P now shutdown-now&', execConfig).start(startConfig);
}

PbServer.prototype.serverStateChange = function(machineToState, serverToState, cb) {
  var self = this;
  cb = cb ? cb : dummyCb;
  var count = 1;
  var checkState = function(err, data) {
    if (err) {
      self.logger.error(format("ERROR: %s", err));
      cb(err);
    }
    else {
      if (count > self.maxStateChangeQueryAttempts) {
        clearInterval(serverStateChange);
        var message = "Max attempts exceeded.";
        self.logger.error(message);
        cb(message);
      }
      else {
        var machineState = data.metadata.state;
        var serverState = data.properties.vmState;
        self.logger.debug(format("Attempt #%d", count));
        self.logger.debug("-------------------------------------");
        self.logger.debug(format("Machine state: %s", machineState));
        self.logger.debug(format("Server state: %s", serverState));
        self.logger.debug("-------------------------------------");
        if (machineState == machineToState && serverState == serverToState) {
          self.logger.info(format("State change to (%s, %s) complete", machineToState , serverToState));
          clearInterval(serverStateChange);
          cb(null, data);
        }
        count++;
      }
    }
  }
  var get = function() {
    self.getServer(checkState);
  }
  this.logger.info(format("Waiting for server state to change to (%s, %s)", machineToState , serverToState));
  get();
  var serverStateChange = setInterval(get, this.stateChangeQueryInterval);
}

PbServer.prototype.checkCommand = function(command, cb) {
  var self = this;
  cb = cb ? cb : dummyCb;
  var count = 1;
  // This prevents overlapping checks and messages.
  var timeout = this.stateChangeQueryInterval - 1000;
  this.logger.debug(format("Checking command: %s", command));
  this.logger.debug(format("SSH connection timeout set to %d milliseconds", timeout));
  var exit = function(code, stdout, stderr) {
    if (code === 0) {
      clearInterval(checkCommand);
      self.logger.info("Command succeeded");
      cb(null, code);
    }
    else {
      self.logger.debug(format('Command returned with error code: %d, %s', code, stderr));
    }
  }
  var execConfig = {
    exit: exit,
  };
  var startConfig = {
    success: function() {
      self.logger.debug("SSH connection successful...");
    },
    fail: function(err) {
      self.logger.debug(format("SSH connection failed: %s", err));
    },
  }
  var check = function() {
    if (count > self.maxStateChangeQueryAttempts) {
      clearInterval(checkCommand);
      var message = "Max attempts exceeded.";
      self.logger.error(message);
      cb(message);
    }
    else {
      self.logger.debug(format("Attempt #%d", count));
      var ssh = new SSH({
        host: self.ssh.host,
        port: self.ssh.port,
        user: self.ssh.user,
        key: self.sshKey,
        timeout: timeout,
      });
      ssh.exec(command, execConfig).start(startConfig);
      count++;
    }
  }
  check();
  var checkCommand = setInterval(check, this.stateChangeQueryInterval);
}

PbServer.prototype.updateServer = function(profile, cb) {
  var self = this;
  cb = cb ? cb : dummyCb;
  var apiCallback = function(err, resp, body) {
    if (err) {
      self.logger.error(format("ERROR: %s", err));
      cb(err);
    }
    else {
      var data = JSON.parse(body);
      cb(null, data);
    }
  }
  this.logger.info(format("Updating server to profile: %s", profile));
  var data = this.pb.profiles[profile];
  if (data) {
    var updateData = {
      properties: data,
    }
    libpb.updateServer(this.pb.datacenterId, this.pb.serverId, updateData, apiCallback);
  }
  else {
    this.logger.error(format("ERROR: profile '%s' does not exist", profile));
  }
}

if (module.exports) {
  module.exports = PbServer;
}
<|endoftext|>


<commit_before>// stringtemplate.js

// Sept 4-5, 2014 ~ start the zero-recursion version
// Sept 6 - still in progress but most array drilldown is done
// Sept 9 ~ cleaned up array v placeholder detection and drilldown

// TODO ~ BUILD REPO WITH TESTS, DOCUMENTATION, EXAMPLES

// 21 August 2014 1:04 AM PDT
/*
// test

'
notes & quotes
\n\r<q>$who$ said, "I\'ll be back"</q>'.escapeHtml();
// => "<p>notes & quotes</p>\n\r<q>$who$ said, "I\'ll be back"</q>"
*/
var escapes = {"\\": "\\\\", "\n": "\\n", "\r": "\\r", "'": "\\'"},
    entities = {'&': '&', '"': '"', '<': '<', '>': '>'};

typeof String.prototype.escapeHtml == 'function' ||
(String.prototype.escapeHtml = function escapeHtml() {
  return this
  .replace(/[\\\n\r']/g, function(char) {
    return escapes[char];
  })
  .replace(/[&\"<>]/g, function(char) {
    return entities[char];
  });
});

/////////////////////////////////////////////

// string#trim polyfill
typeof String.prototype.trim == 'function' ||
(String.prototype.trim = function trim() {
  return this.replace(/^\s+|\s+$/gm, '');
});

// string#template
// a batch string#replace, using $token$ placeholders for values/objects
// and @arrayName@ + @/@ tokens for indexed data (arrays)
// requires string#trim
typeof String.prototype.template == 'function' ||
(String.prototype.template = function template(data) {

  if (!this.match(/\$[^\$]+\$/g) || !data) {
    return this;
  }

  var rows = this.split('\n'),
      parts = [],     // output collector
      i, j, k, p, t,  // loop indexes
      row,            // current row under process
      placeholder,    // match for $name$ or $data.name$:
      arrayStart,     // match for @array@ or @data.array@
      arrayEnd,       // match for @/@
      pathname, tokens, block, array, item;

  for (i = 0; i < rows.length; i += 1) {

    row = rows[i];

    placeholder = row.match(/\$[^\$]+\$/g);
    arrayStart = row.match(/\@[^\@^\/]+\@/g);
    //arrayEnd = row.match(/\@\/\@/g);

    // @array@ || @data.array@:
    if (arrayStart) {

      array = data;
      pathname = arrayStart[0].replace(/\@/g, ''); // console.log('pathname: ' + pathname);

      if (pathname != '.') {

        // drill-down name tokens
        tokens = pathname.split('.');
        p = 0;

        while (p < tokens.length) {
          array = array[tokens[p]];
          p += 1;
        }
      }

      block = [];

      // add rows to block until next array-end token, @/@
      while (rows[i += 1] && !rows[i].match(/\@\/\@/)) {

        if (i ===  rows.length - 1) {

          // rather than throw an error, return this message as the result

          return 'Error: closing @/@ tag for ' + arrayStart[0] + ' array not found';
        }

        block.push(rows[i].trim());

      } // console.log(block);

      // process copies of each row in block
      for (j = 0; j < array.length; j += 1) {

        row = block.join('\n');
        item = array[j];

        if (~row.indexOf('$.$')) {
          // solve array index value $.$
          row = row.replace('$.$', item);
        } else if (typeof item == 'object') {
          // solve item as key:value object
          for (var k in item) {
            row = row.replace('$'+k+'$', item[k]);
          }
        }

        parts.push(row.trim());
      }

    } else {

      // $name$ || $data.name$:
      if (placeholder) {

        // iterate placeholder in current row // console.log('placeholder: ' + placeholder)
        for (t = 0; t < placeholder.length; t += 1) {

          item = data;

          // drill-down name tokens
          tokens = placeholder[t].replace(/\$/g, '').split('.');
          p = 0;

          while (p < tokens.length) {
            item = item[tokens[p]];
            p += 1;
          }

          row = row.replace(placeholder[t], item);
        }
      }

      // avoid array start/end, and whitespace-only
      //if (!row.match(/\@/g) && !row.match(/^\s*$/)) {
        parts.push(row.trim());
      //}
    }
  }

  return parts.join('\n').trim();
});

// function#template
// heredoc/multiline string polyfill
// originally inspired by @rjrodger's mstring project
// requires string#trim and string#template
typeof Function.prototype.template == 'function' ||
(Function.prototype.template = function template(data) {

  var fs = String(this);

  // splitting logic taken from where.js
  var fnBody = fs.replace(/\s*function[^\(]*[\(][^\)]*[\)][^\{]*{/,'')
                 .replace(/[\}]$/, '');
  var table = fnBody.match(/\/(\*){3,3}[^\*]+(\*){3,3}\//);
  var rows = (table && table[0] || fnBody)
              .replace(/\/\/[^\r]*/g, '') // remove line comments...
              .replace(/(\/\*+)*[\r]*(\*+\/)*/g, '') // ...block comments
              .split('\n'); // and split by newline

  var r = [];

  for (var i = 0; i < rows.length; i++) {
    if (!rows[i].match(/^\s*$/)) {
      r.push( rows[i].trim() );
    }
  }

  r = r.join('\n').trim();

  return (data && typeof data == 'object') ? r.template(data) : r;
});
<commit_msg>boundary token for line comment removal regex<commit_after>// stringtemplate.js

// Sept 4-5, 2014 ~ start the zero-recursion version
// Sept 6 - still in progress but most array drilldown is done
// Sept 9 ~ cleaned up array v placeholder detection and drilldown

// TODO ~ BUILD REPO WITH TESTS, DOCUMENTATION, EXAMPLES

// 21 August 2014 1:04 AM PDT
/*
// test

'
notes & quotes
\n\r<q>$who$ said, "I\'ll be back"</q>'.escapeHtml();
// => "<p>notes & quotes</p>\n\r<q>$who$ said, "I\'ll be back"</q>"
*/
var escapes = {"\\": "\\\\", "\n": "\\n", "\r": "\\r", "'": "\\'"},
    entities = {'&': '&', '"': '"', '<': '<', '>': '>'};

typeof String.prototype.escapeHtml == 'function' ||
(String.prototype.escapeHtml = function escapeHtml() {
  return this
  .replace(/[\\\n\r']/g, function(char) {
    return escapes[char];
  })
  .replace(/[&\"<>]/g, function(char) {
    return entities[char];
  });
});

/////////////////////////////////////////////

// string#trim polyfill
typeof String.prototype.trim == 'function' ||
(String.prototype.trim = function trim() {
  return this.replace(/^\s+|\s+$/gm, '');
});

// string#template
// a batch string#replace, using $token$ placeholders for values/objects
// and @arrayName@ + @/@ tokens for indexed data (arrays)
// requires string#trim
typeof String.prototype.template == 'function' ||
(String.prototype.template = function template(data) {

  if (!this.match(/\$[^\$]+\$/g) || !data) {
    return this;
  }

  var rows = this.split('\n'),
      parts = [],     // output collector
      i, j, k, p, t,  // loop indexes
      row,            // current row under process
      placeholder,    // match for $name$ or $data.name$:
      arrayStart,     // match for @array@ or @data.array@
      arrayEnd,       // match for @/@
      pathname, tokens, block, array, item;

  for (i = 0; i < rows.length; i += 1) {

    row = rows[i];

    placeholder = row.match(/\$[^\$]+\$/g);
    arrayStart = row.match(/\@[^\@^\/]+\@/g);
    //arrayEnd = row.match(/\@\/\@/g);

    // @array@ || @data.array@:
    if (arrayStart) {

      array = data;
      pathname = arrayStart[0].replace(/\@/g, ''); // console.log('pathname: ' + pathname);

      if (pathname != '.') {

        // drill-down name tokens
        tokens = pathname.split('.');
        p = 0;

        while (p < tokens.length) {
          array = array[tokens[p]];
          p += 1;
        }
      }

      block = [];

      // add rows to block until next array-end token, @/@
      while (rows[i += 1] && !rows[i].match(/\@\/\@/)) {

        if (i ===  rows.length - 1) {

          // rather than throw an error, return this message as the result

          return 'Error: closing @/@ tag for ' + arrayStart[0] + ' array not found';
        }

        block.push(rows[i].trim());

      } // console.log(block);

      // process copies of each row in block
      for (j = 0; j < array.length; j += 1) {

        row = block.join('\n');
        item = array[j];

        if (~row.indexOf('$.$')) {
          // solve array index value $.$
          row = row.replace('$.$', item);
        } else if (typeof item == 'object') {
          // solve item as key:value object
          for (var k in item) {
            row = row.replace('$'+k+'$', item[k]);
          }
        }

        parts.push(row.trim());
      }

    } else {

      // $name$ || $data.name$:
      if (placeholder) {

        // iterate placeholder in current row // console.log('placeholder: ' + placeholder)
        for (t = 0; t < placeholder.length; t += 1) {

          item = data;

          // drill-down name tokens
          tokens = placeholder[t].replace(/\$/g, '').split('.');
          p = 0;

          while (p < tokens.length) {
            item = item[tokens[p]];
            p += 1;
          }

          row = row.replace(placeholder[t], item);
        }
      }

      // avoid array start/end, and whitespace-only
      //if (!row.match(/\@/g) && !row.match(/^\s*$/)) {
        parts.push(row.trim());
      //}
    }
  }

  return parts.join('\n').trim();
});

// function#template
// heredoc/multiline string polyfill
// originally inspired by @rjrodger's mstring project
// requires string#trim and string#template
typeof Function.prototype.template == 'function' ||
(Function.prototype.template = function template(data) {

  var fs = String(this);

  // splitting logic taken from where.js
  var fnBody = fs.replace(/\s*function[^\(]*[\(][^\)]*[\)][^\{]*{/,'')
                 .replace(/[\}]$/, '');
  var table = fnBody.match(/\/(\*){3,3}[^\*]+(\*){3,3}\//);
  var rows = (table && table[0] || fnBody)
              .replace(/\b\/\/[^\r]*/g, '') // remove line comments...
              .replace(/(\/\*+)*[\r]*(\*+\/)*/g, '') // ...block comments
              .split('\n'); // and split by newline

  var r = [];

  for (var i = 0; i < rows.length; i++) {
    if (!rows[i].match(/^\s*$/)) {
      r.push( rows[i].trim() );
    }
  }

  r = r.join('\n').trim();

  return (data && typeof data == 'object') ? r.template(data) : r;
});
<|endoftext|>