<commit_before>import './customBlockly';
import blocks from './blocks';
import {
    isMainBlock,
    save,
    disable,
    deleteBlocksLoadedBy,
    addLoadersFirst,
    cleanUpOnLoad,
    addDomAsBlock,
    backwardCompatibility,
    fixCollapsedBlocks,
    fixArgumentAttribute,
    removeUnavailableMarkets,
    strategyHasValidTradeTypeCategory,
    cleanBeforeExport,
} from './utils';
import Interpreter from '../../bot/Interpreter';
import { createErrorAndEmit } from '../../common/error';
import { translate, xml as translateXml } from '../../../common/i18n';
import { getLanguage } from '../../../common/lang';
import { observer as globalObserver } from '../../../common/utils/observer';
import { showDialog } from '../../bot/tools';
import GTM from '../../../common/gtm';
import { parseQueryString } from '../../../common/utils/tools';

const setBeforeUnload = off => {
    if (off) {
        window.onbeforeunload = null;
    } else {
        window.onbeforeunload = () => 'You have some unsaved blocks, do you want to save them before you exit?';
    }
};

const disableStrayBlocks = () => {
    const topBlocks = Blockly.mainWorkspace.getTopBlocks();
    topBlocks.forEach(block => {
        if (
            !isMainBlock(block.type) &&
            ['block_holder', 'tick_analysis', 'loader', 'procedures_defreturn', 'procedures_defnoreturn'].indexOf(
                block.type
            ) < 0 &&
            !block.disabled
        ) {
            disable(block, translate('Blocks must be inside block holders, main blocks or functions'));
        }
    });
};
const disposeBlocksWithLoaders = () => {
    Blockly.mainWorkspace.addChangeListener(ev => {
        setBeforeUnload();
        if (ev.type === 'delete' && ev.oldXml.getAttribute('type') === 'loader' && ev.group !== 'undo') {
            deleteBlocksLoadedBy(ev.blockId, ev.group);
        }
    });
};
const marketsWereRemoved = xml => {
    if (!Array.from(xml.children).every(block => !removeUnavailableMarkets(block))) {
        if (window.trackJs) {
            trackJs.track('Invalid financial market');
        }
        showDialog({
            title  : translate('Warning'),
            text   : [translate('This strategy is not available in your country.')],
            buttons: [
                {
                    text : translate('OK'),
                    class: 'button-primary',
                    click() {
                        $(this).dialog('close');
                    },
                },
            ],
        })
            .then(() => {})
            .catch(() => {});
        return true;
    }
    return false;
};
export const loadWorkspace = xml => {
    if (!strategyHasValidTradeTypeCategory(xml)) return;
    if (marketsWereRemoved(xml)) return;

    Blockly.Events.setGroup('load');
    Blockly.mainWorkspace.clear();

    Array.from(xml.children).forEach(block => {
        backwardCompatibility(block);
    });

    fixArgumentAttribute(xml);
    Blockly.Xml.domToWorkspace(xml, Blockly.mainWorkspace);
    addLoadersFirst(xml).then(
        () => {
            fixCollapsedBlocks();
            Blockly.Events.setGroup(false);
            globalObserver.emit('ui.log.success', translate('Blocks are loaded successfully'));
        },
        e => {
            Blockly.Events.setGroup(false);
            throw e;
        }
    );
};

export const loadBlocks = (xml, dropEvent = {}) => {
    if (!strategyHasValidTradeTypeCategory(xml)) return;
    if (marketsWereRemoved(xml)) return;

    const variables = xml.getElementsByTagName('variables');
    if (variables.length > 0) {
        Blockly.Xml.domToVariables(variables[0], Blockly.mainWorkspace);
    }
    Blockly.Events.setGroup('load');
    addLoadersFirst(xml).then(
        loaders => {
            const addedBlocks = [
                ...loaders,
                ...Array.from(xml.children)
                    .map(block => addDomAsBlock(block))
                    .filter(b => b),
            ];
            cleanUpOnLoad(addedBlocks, dropEvent);
            fixCollapsedBlocks();
            globalObserver.emit('ui.log.success', translate('Blocks are loaded successfully'));
        },
        e => {
            throw e;
        }
    );
};
const xmlToStr = xml => {
    const serializer = new XMLSerializer();
    return serializer.serializeToString(xml);
};
const addBlocklyTranslation = () => {
    $.ajaxPrefilter(options => {
        options.async = true; // eslint-disable-line no-param-reassign
    });
    let lang = getLanguage();
    if (lang === 'ach') {
        lang = 'en';
    } else if (lang === 'zh_cn') {
        lang = 'zh-hans';
    } else if (lang === 'zh_tw') {
        lang = 'zh-hant';
    }
    return new Promise(resolve => {
        $.getScript(`translations/${lang}.js`, resolve);
    });
};
const onresize = () => {
    let element = document.getElementById('blocklyArea');
    const blocklyArea = element;
    const blocklyDiv = document.getElementById('blocklyDiv');
    let x = 0;
    let y = 0;
    do {
        x += element.offsetLeft;
        y += element.offsetTop;
        element = element.offsetParent;
    } while (element);
    // Position blocklyDiv over blocklyArea.
    blocklyDiv.style.left = `${x}px`;
    blocklyDiv.style.top = `${y}px`;
    blocklyDiv.style.width = `${blocklyArea.offsetWidth}px`;
    blocklyDiv.style.height = `${blocklyArea.offsetHeight}px`;
};
const render = workspace => () => {
    onresize();
    Blockly.svgResize(workspace);
};
const overrideBlocklyDefaultShape = () => {
    const addDownloadToMenu = block => {
        if (block instanceof Object) {
            // eslint-disable-next-line no-param-reassign, max-len
            block.customContextMenu = function customContextMenu(options) {
                options.push({
                    text    : translate('Download'),
                    enabled : true,
                    callback: () => {
                        const xml = Blockly.Xml.textToDom(
                            '<xml xmlns="http://www.w3.org/1999/xhtml" collection="false"></xml>'
                        );
                        xml.appendChild(Blockly.Xml.blockToDom(this));
                        save('binary-bot-block', true, xml);
                    },
                });
            };
        }
    };
    Object.keys(Blockly.Blocks).forEach(blockName => {
        const downloadDisabledBlocks = ['controls_forEach', 'controls_for', 'variables_get', 'variables_set'];
        if (!downloadDisabledBlocks.includes(blockName)) {
            addDownloadToMenu(Blockly.Blocks[blockName]);
        }
    });
};
const repaintDefaultColours = () => {
    Blockly.Msg.LOGIC_HUE = '#DEDEDE';
    Blockly.Msg.LOOPS_HUE = '#DEDEDE';
    Blockly.Msg.MATH_HUE = '#DEDEDE';
    Blockly.Msg.TEXTS_HUE = '#DEDEDE';
    Blockly.Msg.LISTS_HUE = '#DEDEDE';
    Blockly.Msg.COLOUR_HUE = '#DEDEDE';
    Blockly.Msg.VARIABLES_HUE = '#DEDEDE';
    Blockly.Msg.VARIABLES_DYNAMIC_HUE = '#DEDEDE';
    Blockly.Msg.PROCEDURES_HUE = '#DEDEDE';

    Blockly.Blocks.logic.HUE = '#DEDEDE';
    Blockly.Blocks.loops.HUE = '#DEDEDE';
    Blockly.Blocks.math.HUE = '#DEDEDE';
    Blockly.Blocks.texts.HUE = '#DEDEDE';
    Blockly.Blocks.lists.HUE = '#DEDEDE';
    Blockly.Blocks.colour.HUE = '#DEDEDE';
    Blockly.Blocks.variables.HUE = '#DEDEDE';
    Blockly.Blocks.procedures.HUE = '#DEDEDE';
};

export default class _Blockly {
    constructor() {
        this.blocksXmlStr = '';
        this.generatedJs = '';
        // eslint-disable-next-line no-underscore-dangle
        Blockly.WorkspaceSvg.prototype.preloadAudio_ = () => {}; // https://github.com/google/blockly/issues/299
        this.initPromise = new Promise(resolve => {
            $.get('xml/toolbox.xml', toolboxXml => {
                blocks();
                const workspace = Blockly.inject('blocklyDiv', {
                    toolbox: xmlToStr(translateXml(toolboxXml.getElementsByTagName('xml')[0])),
                    zoom   : {
                        wheel: false,
                    },
                    trashcan: false,
                });
                workspace.addChangeListener(event => {
                    if (event.type === Blockly.Events.BLOCK_CREATE) {
                        event.ids.forEach(id => {
                            const block = workspace.getBlockById(id);
                            if (block) {
                                GTM.pushDataLayer({
                                    event     : 'Block Event',
                                    blockEvent: event.type,
                                    blockType : block.type,
                                });
                            }
                        });
                    } else if (event.type === Blockly.Events.BLOCK_DELETE) {
                        const dom = Blockly.Xml.textToDom(`<xml>${event.oldXml.outerHTML}</xml>`);
                        const blockNodes = dom.getElementsByTagName('block');
                        Array.from(blockNodes).forEach(blockNode => {
                            GTM.pushDataLayer({
                                event     : 'Block Event',
                                blockEvent: event.type,
                                blockType : blockNode.getAttribute('type'),
                            });
                        });
                    }
                });

                const renderInstance = render(workspace);
                window.addEventListener('resize', renderInstance, false);
                renderInstance();
                addBlocklyTranslation().then(() => {
                    const defaultStrat = parseQueryString().strategy;
                    const xmlFile = defaultStrat ? `xml/${defaultStrat}.xml` : 'xml/main.xml';

                    $.get(xmlFile, main => {
                        repaintDefaultColours();
                        overrideBlocklyDefaultShape();
                        this.blocksXmlStr = Blockly.Xml.domToPrettyText(main);
                        Blockly.Xml.domToWorkspace(main.getElementsByTagName('xml')[0], workspace);
                        this.zoomOnPlusMinus();
                        disposeBlocksWithLoaders();
                        setTimeout(() => {
                            setBeforeUnload(true);
                            Blockly.mainWorkspace.cleanUp();
                            Blockly.mainWorkspace.clearUndo();
                        }, 0);
                        resolve();
                    });
                });
            });
        });
    }
    /* eslint-disable class-methods-use-this */
    zoomOnPlusMinus(zoomIn) {
        const metrics = Blockly.mainWorkspace.getMetrics();
        if (zoomIn) {
            Blockly.mainWorkspace.zoom(metrics.viewWidth / 2, metrics.viewHeight / 2, 1);
        } else {
            Blockly.mainWorkspace.zoom(metrics.viewWidth / 2, metrics.viewHeight / 2, -1);
        }
    }
    resetWorkspace() {
        Blockly.Events.setGroup('reset');
        Blockly.mainWorkspace.clear();
        Blockly.Xml.domToWorkspace(Blockly.Xml.textToDom(this.blocksXmlStr), Blockly.mainWorkspace);
        Blockly.Events.setGroup(false);
    }
    /* eslint-disable class-methods-use-this */
    cleanUp() {
        Blockly.Events.setGroup(true);
        const topBlocks = Blockly.mainWorkspace.getTopBlocks(true);
        let cursorY = 0;
        topBlocks.forEach(block => {
            if (block.getSvgRoot().style.display !== 'none') {
                const xy = block.getRelativeToSurfaceXY();
                block.moveBy(-xy.x, cursorY - xy.y);
                block.snapToGrid();
                cursorY =
                    block.getRelativeToSurfaceXY().y + block.getHeightWidth().height + Blockly.BlockSvg.MIN_BLOCK_Y;
            }
        });
        Blockly.Events.setGroup(false);
        // Fire an event to allow scrollbars to resize.
        Blockly.mainWorkspace.resizeContents();
    }
    /* eslint-disable class-methods-use-this */
    load(blockStr = '', dropEvent = {}) {
        const unrecognisedMsg = () => translate('Unrecognized file format');

        try {
            const xmlDoc = new DOMParser().parseFromString(blockStr, 'application/xml');

            if (xmlDoc.getElementsByTagName('parsererror').length) {
                throw new Error();
            }
        } catch (err) {
            throw createErrorAndEmit('FileLoad', unrecognisedMsg());
        }

        let xml;
        try {
            xml = Blockly.Xml.textToDom(blockStr);
        } catch (e) {
            throw createErrorAndEmit('FileLoad', unrecognisedMsg());
        }

        const blocklyXml = xml.querySelectorAll('block');

        if (!blocklyXml.length) {
            throw createErrorAndEmit(
                'FileLoad',
                'XML file contains unsupported elements. Please check or modify file.'
            );
        }

        blocklyXml.forEach(block => {
            const blockType = block.getAttribute('type');

            if (!Object.keys(Blockly.Blocks).includes(blockType)) {
                throw createErrorAndEmit(
                    'FileLoad',
                    'XML file contains unsupported elements. Please check or modify file'
                );
            }
        });

        try {
            if (xml.hasAttribute('collection') && xml.getAttribute('collection') === 'true') {
                loadBlocks(xml, dropEvent);
            } else {
                loadWorkspace(xml);
            }
        } catch (e) {
            throw createErrorAndEmit('FileLoad', translate('Unable to load the block file'));
        }
    }
    /* eslint-disable class-methods-use-this */
    save(arg) {
        const { filename, collection } = arg;

        setBeforeUnload(true);

        const xml = Blockly.Xml.workspaceToDom(Blockly.mainWorkspace);
        cleanBeforeExport(xml);

        save(filename, collection, xml);
    }
    run(limitations = {}) {
        disableStrayBlocks();
        let code;
        try {
            code = `
var BinaryBotPrivateInit, BinaryBotPrivateStart, BinaryBotPrivateBeforePurchase, BinaryBotPrivateDuringPurchase, BinaryBotPrivateAfterPurchase;

var BinaryBotPrivateLastTickTime
var BinaryBotPrivateTickAnalysisList = [];

function BinaryBotPrivateRun(f, arg) {
 if (f) return f(arg);
 return false;
}

function BinaryBotPrivateTickAnalysis() {
 var currentTickTime = Bot.getLastTick(true).epoch
 if (currentTickTime === BinaryBotPrivateLastTickTime) {
   return
 }
 BinaryBotPrivateLastTickTime = currentTickTime
 for (var BinaryBotPrivateI = 0; BinaryBotPrivateI < BinaryBotPrivateTickAnalysisList.length; BinaryBotPrivateI++) {
   BinaryBotPrivateRun(BinaryBotPrivateTickAnalysisList[BinaryBotPrivateI]);
 }
}

var BinaryBotPrivateLimitations = ${JSON.stringify(limitations)};

${Blockly.JavaScript.workspaceToCode(Blockly.mainWorkspace)}

BinaryBotPrivateRun(BinaryBotPrivateInit);

while(true) {
 BinaryBotPrivateTickAnalysis();
 BinaryBotPrivateRun(BinaryBotPrivateStart)
 while(watch('before')) {
   BinaryBotPrivateTickAnalysis();
   BinaryBotPrivateRun(BinaryBotPrivateBeforePurchase);
 }
 while(watch('during')) {
   BinaryBotPrivateTickAnalysis();
   BinaryBotPrivateRun(BinaryBotPrivateDuringPurchase);
 }
 BinaryBotPrivateTickAnalysis();
 if(!BinaryBotPrivateRun(BinaryBotPrivateAfterPurchase)) {
   break;
 }
}
       `;
            this.generatedJs = code;
            if (code) {
                this.stop(true);
                this.interpreter = new Interpreter();
                this.interpreter.run(code).catch(e => {
                    globalObserver.emit('Error', e);
                    this.stop();
                });
            }
        } catch (e) {
            globalObserver.emit('Error', e);
            this.stop();
        }
    }
    stop(stopBeforeStart) {
        if (!stopBeforeStart) {
            const elRunButtons = document.querySelectorAll('#runButton, #summaryRunButton');
            const elStopButtons = document.querySelectorAll('#stopButton, #summaryStopButton');

            elRunButtons.forEach(el => {
                const elRunButton = el;
                elRunButton.style.display = 'initial';
            });
            elStopButtons.forEach(el => {
                const elStopButton = el;
                elStopButton.style.display = null;
            });
        }
        if (this.interpreter) {
            this.interpreter.stop();
            this.interpreter = null;
        }
    }
    /* eslint-disable class-methods-use-this */
    undo() {
        Blockly.Events.setGroup('undo');
        Blockly.mainWorkspace.undo();
        Blockly.Events.setGroup(false);
    }
    /* eslint-disable class-methods-use-this */
    redo() {
        Blockly.mainWorkspace.undo(true);
    }
    /* eslint-disable class-methods-use-this */
    hasStarted() {
        return this.interpreter && this.interpreter.hasStarted();
    }
    /* eslint-enable */
}
<commit_msg>fix page not load when random text pass to strategy parameter<commit_after>import './customBlockly';
import blocks from './blocks';
import {
    isMainBlock,
    save,
    disable,
    deleteBlocksLoadedBy,
    addLoadersFirst,
    cleanUpOnLoad,
    addDomAsBlock,
    backwardCompatibility,
    fixCollapsedBlocks,
    fixArgumentAttribute,
    removeUnavailableMarkets,
    strategyHasValidTradeTypeCategory,
    cleanBeforeExport,
} from './utils';
import Interpreter from '../../bot/Interpreter';
import { createErrorAndEmit } from '../../common/error';
import { translate, xml as translateXml } from '../../../common/i18n';
import { getLanguage } from '../../../common/lang';
import { observer as globalObserver } from '../../../common/utils/observer';
import { showDialog } from '../../bot/tools';
import GTM from '../../../common/gtm';
import { parseQueryString } from '../../../common/utils/tools';

const setBeforeUnload = off => {
    if (off) {
        window.onbeforeunload = null;
    } else {
        window.onbeforeunload = () => 'You have some unsaved blocks, do you want to save them before you exit?';
    }
};

const disableStrayBlocks = () => {
    const topBlocks = Blockly.mainWorkspace.getTopBlocks();
    topBlocks.forEach(block => {
        if (
            !isMainBlock(block.type) &&
            ['block_holder', 'tick_analysis', 'loader', 'procedures_defreturn', 'procedures_defnoreturn'].indexOf(
                block.type
            ) < 0 &&
            !block.disabled
        ) {
            disable(block, translate('Blocks must be inside block holders, main blocks or functions'));
        }
    });
};
const disposeBlocksWithLoaders = () => {
    Blockly.mainWorkspace.addChangeListener(ev => {
        setBeforeUnload();
        if (ev.type === 'delete' && ev.oldXml.getAttribute('type') === 'loader' && ev.group !== 'undo') {
            deleteBlocksLoadedBy(ev.blockId, ev.group);
        }
    });
};
const marketsWereRemoved = xml => {
    if (!Array.from(xml.children).every(block => !removeUnavailableMarkets(block))) {
        if (window.trackJs) {
            trackJs.track('Invalid financial market');
        }
        showDialog({
            title  : translate('Warning'),
            text   : [translate('This strategy is not available in your country.')],
            buttons: [
                {
                    text : translate('OK'),
                    class: 'button-primary',
                    click() {
                        $(this).dialog('close');
                    },
                },
            ],
        })
            .then(() => {})
            .catch(() => {});
        return true;
    }
    return false;
};
export const loadWorkspace = xml => {
    if (!strategyHasValidTradeTypeCategory(xml)) return;
    if (marketsWereRemoved(xml)) return;

    Blockly.Events.setGroup('load');
    Blockly.mainWorkspace.clear();

    Array.from(xml.children).forEach(block => {
        backwardCompatibility(block);
    });

    fixArgumentAttribute(xml);
    Blockly.Xml.domToWorkspace(xml, Blockly.mainWorkspace);
    addLoadersFirst(xml).then(
        () => {
            fixCollapsedBlocks();
            Blockly.Events.setGroup(false);
            globalObserver.emit('ui.log.success', translate('Blocks are loaded successfully'));
        },
        e => {
            Blockly.Events.setGroup(false);
            throw e;
        }
    );
};

export const loadBlocks = (xml, dropEvent = {}) => {
    if (!strategyHasValidTradeTypeCategory(xml)) return;
    if (marketsWereRemoved(xml)) return;

    const variables = xml.getElementsByTagName('variables');
    if (variables.length > 0) {
        Blockly.Xml.domToVariables(variables[0], Blockly.mainWorkspace);
    }
    Blockly.Events.setGroup('load');
    addLoadersFirst(xml).then(
        loaders => {
            const addedBlocks = [
                ...loaders,
                ...Array.from(xml.children)
                    .map(block => addDomAsBlock(block))
                    .filter(b => b),
            ];
            cleanUpOnLoad(addedBlocks, dropEvent);
            fixCollapsedBlocks();
            globalObserver.emit('ui.log.success', translate('Blocks are loaded successfully'));
        },
        e => {
            throw e;
        }
    );
};
const xmlToStr = xml => {
    const serializer = new XMLSerializer();
    return serializer.serializeToString(xml);
};
const addBlocklyTranslation = () => {
    $.ajaxPrefilter(options => {
        options.async = true; // eslint-disable-line no-param-reassign
    });
    let lang = getLanguage();
    if (lang === 'ach') {
        lang = 'en';
    } else if (lang === 'zh_cn') {
        lang = 'zh-hans';
    } else if (lang === 'zh_tw') {
        lang = 'zh-hant';
    }
    return new Promise(resolve => {
        $.getScript(`translations/${lang}.js`, resolve);
    });
};
const onresize = () => {
    let element = document.getElementById('blocklyArea');
    const blocklyArea = element;
    const blocklyDiv = document.getElementById('blocklyDiv');
    let x = 0;
    let y = 0;
    do {
        x += element.offsetLeft;
        y += element.offsetTop;
        element = element.offsetParent;
    } while (element);
    // Position blocklyDiv over blocklyArea.
    blocklyDiv.style.left = `${x}px`;
    blocklyDiv.style.top = `${y}px`;
    blocklyDiv.style.width = `${blocklyArea.offsetWidth}px`;
    blocklyDiv.style.height = `${blocklyArea.offsetHeight}px`;
};
const render = workspace => () => {
    onresize();
    Blockly.svgResize(workspace);
};
const overrideBlocklyDefaultShape = () => {
    const addDownloadToMenu = block => {
        if (block instanceof Object) {
            // eslint-disable-next-line no-param-reassign, max-len
            block.customContextMenu = function customContextMenu(options) {
                options.push({
                    text    : translate('Download'),
                    enabled : true,
                    callback: () => {
                        const xml = Blockly.Xml.textToDom(
                            '<xml xmlns="http://www.w3.org/1999/xhtml" collection="false"></xml>'
                        );
                        xml.appendChild(Blockly.Xml.blockToDom(this));
                        save('binary-bot-block', true, xml);
                    },
                });
            };
        }
    };
    Object.keys(Blockly.Blocks).forEach(blockName => {
        const downloadDisabledBlocks = ['controls_forEach', 'controls_for', 'variables_get', 'variables_set'];
        if (!downloadDisabledBlocks.includes(blockName)) {
            addDownloadToMenu(Blockly.Blocks[blockName]);
        }
    });
};
const repaintDefaultColours = () => {
    Blockly.Msg.LOGIC_HUE = '#DEDEDE';
    Blockly.Msg.LOOPS_HUE = '#DEDEDE';
    Blockly.Msg.MATH_HUE = '#DEDEDE';
    Blockly.Msg.TEXTS_HUE = '#DEDEDE';
    Blockly.Msg.LISTS_HUE = '#DEDEDE';
    Blockly.Msg.COLOUR_HUE = '#DEDEDE';
    Blockly.Msg.VARIABLES_HUE = '#DEDEDE';
    Blockly.Msg.VARIABLES_DYNAMIC_HUE = '#DEDEDE';
    Blockly.Msg.PROCEDURES_HUE = '#DEDEDE';

    Blockly.Blocks.logic.HUE = '#DEDEDE';
    Blockly.Blocks.loops.HUE = '#DEDEDE';
    Blockly.Blocks.math.HUE = '#DEDEDE';
    Blockly.Blocks.texts.HUE = '#DEDEDE';
    Blockly.Blocks.lists.HUE = '#DEDEDE';
    Blockly.Blocks.colour.HUE = '#DEDEDE';
    Blockly.Blocks.variables.HUE = '#DEDEDE';
    Blockly.Blocks.procedures.HUE = '#DEDEDE';
};

export default class _Blockly {
    constructor() {
        this.blocksXmlStr = '';
        this.generatedJs = '';
        // eslint-disable-next-line no-underscore-dangle
        Blockly.WorkspaceSvg.prototype.preloadAudio_ = () => {}; // https://github.com/google/blockly/issues/299
        this.initPromise = new Promise(resolve => {
            $.get('xml/toolbox.xml', toolboxXml => {
                blocks();
                const workspace = Blockly.inject('blocklyDiv', {
                    toolbox: xmlToStr(translateXml(toolboxXml.getElementsByTagName('xml')[0])),
                    zoom   : {
                        wheel: false,
                    },
                    trashcan: false,
                });
                workspace.addChangeListener(event => {
                    if (event.type === Blockly.Events.BLOCK_CREATE) {
                        event.ids.forEach(id => {
                            const block = workspace.getBlockById(id);
                            if (block) {
                                GTM.pushDataLayer({
                                    event     : 'Block Event',
                                    blockEvent: event.type,
                                    blockType : block.type,
                                });
                            }
                        });
                    } else if (event.type === Blockly.Events.BLOCK_DELETE) {
                        const dom = Blockly.Xml.textToDom(`<xml>${event.oldXml.outerHTML}</xml>`);
                        const blockNodes = dom.getElementsByTagName('block');
                        Array.from(blockNodes).forEach(blockNode => {
                            GTM.pushDataLayer({
                                event     : 'Block Event',
                                blockEvent: event.type,
                                blockType : blockNode.getAttribute('type'),
                            });
                        });
                    }
                });

                const renderInstance = render(workspace);
                window.addEventListener('resize', renderInstance, false);
                renderInstance();
                addBlocklyTranslation().then(() => {
                    const defaultStrat = parseQueryString().strategy;
                    const xmlFile = defaultStrat ? `xml/${defaultStrat}.xml` : 'xml/main.xml';

                    const getFile = xml => {
                        $.get(xml, main => {
                            repaintDefaultColours();
                            overrideBlocklyDefaultShape();
                            this.blocksXmlStr = Blockly.Xml.domToPrettyText(main);
                            Blockly.Xml.domToWorkspace(main.getElementsByTagName('xml')[0], workspace);
                            this.zoomOnPlusMinus();
                            disposeBlocksWithLoaders();
                            setTimeout(() => {
                                setBeforeUnload(true);
                                Blockly.mainWorkspace.cleanUp();
                                Blockly.mainWorkspace.clearUndo();
                            }, 0);
                            resolve();
                        }).catch(() => {
                            getFile('xml/main.xml');
                        });
                    };

                    getFile(xmlFile);
                });
            });
        });
    }
    /* eslint-disable class-methods-use-this */
    zoomOnPlusMinus(zoomIn) {
        const metrics = Blockly.mainWorkspace.getMetrics();
        if (zoomIn) {
            Blockly.mainWorkspace.zoom(metrics.viewWidth / 2, metrics.viewHeight / 2, 1);
        } else {
            Blockly.mainWorkspace.zoom(metrics.viewWidth / 2, metrics.viewHeight / 2, -1);
        }
    }
    resetWorkspace() {
        Blockly.Events.setGroup('reset');
        Blockly.mainWorkspace.clear();
        Blockly.Xml.domToWorkspace(Blockly.Xml.textToDom(this.blocksXmlStr), Blockly.mainWorkspace);
        Blockly.Events.setGroup(false);
    }
    /* eslint-disable class-methods-use-this */
    cleanUp() {
        Blockly.Events.setGroup(true);
        const topBlocks = Blockly.mainWorkspace.getTopBlocks(true);
        let cursorY = 0;
        topBlocks.forEach(block => {
            if (block.getSvgRoot().style.display !== 'none') {
                const xy = block.getRelativeToSurfaceXY();
                block.moveBy(-xy.x, cursorY - xy.y);
                block.snapToGrid();
                cursorY =
                    block.getRelativeToSurfaceXY().y + block.getHeightWidth().height + Blockly.BlockSvg.MIN_BLOCK_Y;
            }
        });
        Blockly.Events.setGroup(false);
        // Fire an event to allow scrollbars to resize.
        Blockly.mainWorkspace.resizeContents();
    }
    /* eslint-disable class-methods-use-this */
    load(blockStr = '', dropEvent = {}) {
        const unrecognisedMsg = () => translate('Unrecognized file format');

        try {
            const xmlDoc = new DOMParser().parseFromString(blockStr, 'application/xml');

            if (xmlDoc.getElementsByTagName('parsererror').length) {
                throw new Error();
            }
        } catch (err) {
            throw createErrorAndEmit('FileLoad', unrecognisedMsg());
        }

        let xml;
        try {
            xml = Blockly.Xml.textToDom(blockStr);
        } catch (e) {
            throw createErrorAndEmit('FileLoad', unrecognisedMsg());
        }

        const blocklyXml = xml.querySelectorAll('block');

        if (!blocklyXml.length) {
            throw createErrorAndEmit(
                'FileLoad',
                'XML file contains unsupported elements. Please check or modify file.'
            );
        }

        blocklyXml.forEach(block => {
            const blockType = block.getAttribute('type');

            if (!Object.keys(Blockly.Blocks).includes(blockType)) {
                throw createErrorAndEmit(
                    'FileLoad',
                    'XML file contains unsupported elements. Please check or modify file'
                );
            }
        });

        try {
            if (xml.hasAttribute('collection') && xml.getAttribute('collection') === 'true') {
                loadBlocks(xml, dropEvent);
            } else {
                loadWorkspace(xml);
            }
        } catch (e) {
            throw createErrorAndEmit('FileLoad', translate('Unable to load the block file'));
        }
    }
    /* eslint-disable class-methods-use-this */
    save(arg) {
        const { filename, collection } = arg;

        setBeforeUnload(true);

        const xml = Blockly.Xml.workspaceToDom(Blockly.mainWorkspace);
        cleanBeforeExport(xml);

        save(filename, collection, xml);
    }
    run(limitations = {}) {
        disableStrayBlocks();
        let code;
        try {
            code = `
var BinaryBotPrivateInit, BinaryBotPrivateStart, BinaryBotPrivateBeforePurchase, BinaryBotPrivateDuringPurchase, BinaryBotPrivateAfterPurchase;

var BinaryBotPrivateLastTickTime
var BinaryBotPrivateTickAnalysisList = [];

function BinaryBotPrivateRun(f, arg) {
 if (f) return f(arg);
 return false;
}

function BinaryBotPrivateTickAnalysis() {
 var currentTickTime = Bot.getLastTick(true).epoch
 if (currentTickTime === BinaryBotPrivateLastTickTime) {
   return
 }
 BinaryBotPrivateLastTickTime = currentTickTime
 for (var BinaryBotPrivateI = 0; BinaryBotPrivateI < BinaryBotPrivateTickAnalysisList.length; BinaryBotPrivateI++) {
   BinaryBotPrivateRun(BinaryBotPrivateTickAnalysisList[BinaryBotPrivateI]);
 }
}

var BinaryBotPrivateLimitations = ${JSON.stringify(limitations)};

${Blockly.JavaScript.workspaceToCode(Blockly.mainWorkspace)}

BinaryBotPrivateRun(BinaryBotPrivateInit);

while(true) {
 BinaryBotPrivateTickAnalysis();
 BinaryBotPrivateRun(BinaryBotPrivateStart)
 while(watch('before')) {
   BinaryBotPrivateTickAnalysis();
   BinaryBotPrivateRun(BinaryBotPrivateBeforePurchase);
 }
 while(watch('during')) {
   BinaryBotPrivateTickAnalysis();
   BinaryBotPrivateRun(BinaryBotPrivateDuringPurchase);
 }
 BinaryBotPrivateTickAnalysis();
 if(!BinaryBotPrivateRun(BinaryBotPrivateAfterPurchase)) {
   break;
 }
}
       `;
            this.generatedJs = code;
            if (code) {
                this.stop(true);
                this.interpreter = new Interpreter();
                this.interpreter.run(code).catch(e => {
                    globalObserver.emit('Error', e);
                    this.stop();
                });
            }
        } catch (e) {
            globalObserver.emit('Error', e);
            this.stop();
        }
    }
    stop(stopBeforeStart) {
        if (!stopBeforeStart) {
            const elRunButtons = document.querySelectorAll('#runButton, #summaryRunButton');
            const elStopButtons = document.querySelectorAll('#stopButton, #summaryStopButton');

            elRunButtons.forEach(el => {
                const elRunButton = el;
                elRunButton.style.display = 'initial';
            });
            elStopButtons.forEach(el => {
                const elStopButton = el;
                elStopButton.style.display = null;
            });
        }
        if (this.interpreter) {
            this.interpreter.stop();
            this.interpreter = null;
        }
    }
    /* eslint-disable class-methods-use-this */
    undo() {
        Blockly.Events.setGroup('undo');
        Blockly.mainWorkspace.undo();
        Blockly.Events.setGroup(false);
    }
    /* eslint-disable class-methods-use-this */
    redo() {
        Blockly.mainWorkspace.undo(true);
    }
    /* eslint-disable class-methods-use-this */
    hasStarted() {
        return this.interpreter && this.interpreter.hasStarted();
    }
    /* eslint-enable */
}
<|endoftext|>


<commit_before>// Copyright 2014 Globo.com Player authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

/**
 * The Core is responsible to manage Containers, the mediator, MediaControl
 * and the player state.
 */

var assign = require('lodash.assign')
var $ = require('clappr-zepto')

var UIObject = require('../../base/ui_object')
var ContainerFactory = require('../container_factory')
var Fullscreen = require('../../base/utils').Fullscreen
var Styler = require('../../base/styler')
var MediaControl = require('../media_control')
var PlayerInfo = require('../player_info')
var Mediator = require('../mediator')
var Events = require('../../base/events')

var find = require('lodash.find')
var isNumber = require('../../base/utils').isNumber

class Core extends UIObject {
  get events() {
    return {
      'webkitfullscreenchange': 'exit',
      'mousemove': 'showMediaControl',
      'mouseleave': 'hideMediaControl'
    }
  }

  get attributes() {
    return {
      'data-player': ''
    }
  }

  constructor(options) {
    super(options)
    PlayerInfo.options = options
    this.options = options
    this.plugins = []
    this.containers = []
    this.createContainers(options)
    //FIXME fullscreen api sucks
    $(document).bind('fullscreenchange', () => this.exit())
    $(document).bind('MSFullscreenChange', () => this.exit())
    $(document).bind('mozfullscreenchange', () => this.exit())
  }

  createContainers(options) {
    this.defer = $.Deferred()
    this.defer.promise(this)
    this.containerFactory = new ContainerFactory(options, options.loader)
    this.containerFactory
      .createContainers()
      .then((containers) => this.setupContainers(containers))
      .then((containers) => this.resolveOnContainersReady(containers))
  }

  updateSize() {
    if (Fullscreen.isFullscreen()) {
      this.setFullscreen()
    } else {
      this.setPlayerSize()
    }
    Mediator.trigger(Events.PLAYER_RESIZE)
  }

  setFullscreen() {
    this.$el.addClass('fullscreen')
    this.$el.removeAttr('style')
    PlayerInfo.previousSize = PlayerInfo.currentSize
    PlayerInfo.currentSize = { width: $(window).width(), height: $(window).height() }
  }

  setPlayerSize() {
    this.$el.removeClass('fullscreen')
    PlayerInfo.currentSize = PlayerInfo.previousSize
    PlayerInfo.previousSize = { width: $(window).width(), height: $(window).height() }
    this.resize(PlayerInfo.currentSize)
  }

  resize(options) {
    if (!isNumber(options.height) && !isNumber(options.width))  {
      this.el.style.height = `${options.height}`;
      this.el.style.width = `${options.width}`;
    } else {
      this.el.style.height = `${options.height}px`;
      this.el.style.width = `${options.width}px`;
    }
    PlayerInfo.previousSize = PlayerInfo.currentSize
    PlayerInfo.currentSize = options
    Mediator.trigger(Events.PLAYER_RESIZE)
  }

  resolveOnContainersReady(containers) {
    $.when.apply($, containers).done(() =>this.defer.resolve(this))
  }

  addPlugin(plugin) {
    this.plugins.push(plugin)
  }

  hasPlugin(name) {
    return !!this.getPlugin(name)
  }

  getPlugin(name) {
    return find(this.plugins, (plugin) => plugin.name === name)
  }

  load(sources) {
    sources = sources && sources.constructor === Array ? sources : [sources.toString()];
    this.containers.forEach((container) => container.destroy())
    this.containerFactory.options = assign(this.options, {sources})
    this.containerFactory.createContainers().then((containers) => {
      this.setupContainers(containers)
    })
  }

  destroy() {
    this.containers.forEach((container) => container.destroy())
    this.plugins.forEach((plugin) => plugin.destroy())
    this.$el.remove()
    this.mediaControl.destroy()
    $(document).unbind('fullscreenchange')
    $(document).unbind('MSFullscreenChange')
    $(document).unbind('mozfullscreenchange')
}

  exit() {
    this.updateSize()
    this.mediaControl.show()
  }

  setMediaControlContainer(container) {
    this.mediaControl.setContainer(container)
    this.mediaControl.render()
  }

  disableMediaControl() {
    this.mediaControl.disable()
    this.$el.removeClass('nocursor')
  }

  enableMediaControl() {
    this.mediaControl.enable()
  }

  removeContainer(container) {
    this.stopListening(container)
    this.containers = this.containers.filter((c) => c !== container)
  }

  appendContainer(container) {
    this.listenTo(container, Events.CONTAINER_DESTROYED, this.removeContainer)
    this.el.appendChild(container.render().el)
    this.containers.push(container)
  }

  setupContainers(containers) {
    containers.map(this.appendContainer.bind(this))
    this.setupMediaControl(this.getCurrentContainer())
    this.render()
    this.$el.appendTo(this.options.parentElement)
    return containers
  }

  createContainer(source, options) {
    var container = this.containerFactory.createContainer(source, options)
    this.appendContainer(container)
    return container
  }

  setupMediaControl(container) {
    if (this.mediaControl) {
      this.mediaControl.setContainer(container)
    } else {
      this.mediaControl = this.createMediaControl(assign({container: container}, this.options))
      this.listenTo(this.mediaControl, Events.MEDIACONTROL_FULLSCREEN, this.toggleFullscreen)
      this.listenTo(this.mediaControl, Events.MEDIACONTROL_SHOW, this.onMediaControlShow.bind(this, true))
      this.listenTo(this.mediaControl, Events.MEDIACONTROL_HIDE, this.onMediaControlShow.bind(this, false))
    }
  }

  createMediaControl(options) {
    if(options.mediacontrol && options.mediacontrol.external) {
      return new options.mediacontrol.external(options);
    } else {
      return new MediaControl(options);
    }
  }

  getCurrentContainer() {
    return this.containers[0]
  }

  toggleFullscreen() {
    if (!Fullscreen.isFullscreen()) {
      Fullscreen.requestFullscreen(this.el)
      this.$el.addClass('fullscreen')
    } else {
      Fullscreen.cancelFullscreen()
      this.$el.removeClass('fullscreen nocursor')
    }
    this.mediaControl.show()
  }

  showMediaControl(event) {
    this.mediaControl.show(event)
  }

  hideMediaControl(event) {
    this.mediaControl.hide(event)
  }

  onMediaControlShow(showing) {
    if (showing)
      this.$el.removeClass('nocursor')
    else if (Fullscreen.isFullscreen())
      this.$el.addClass('nocursor')
  }

  render() {
    var style = Styler.getStyleFor('core')
    //FIXME
    //this.$el.empty()
    this.$el.append(style)
    this.$el.append(this.mediaControl.render().el)

    this.options.width = this.options.width || this.$el.width()
    this.options.height = this.options.height || this.$el.height()
    var size = {width: this.options.width, height: this.options.height}
    PlayerInfo.previousSize = PlayerInfo.currentSize = size
    this.updateSize()

    return this
  }
}

module.exports = Core
<commit_msg>core: add resize detection to fully support percentage sizes<commit_after>// Copyright 2014 Globo.com Player authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

/**
 * The Core is responsible to manage Containers, the mediator, MediaControl
 * and the player state.
 */

var assign = require('lodash.assign')
var $ = require('clappr-zepto')

var UIObject = require('../../base/ui_object')
var ContainerFactory = require('../container_factory')
var Fullscreen = require('../../base/utils').Fullscreen
var Styler = require('../../base/styler')
var MediaControl = require('../media_control')
var PlayerInfo = require('../player_info')
var Mediator = require('../mediator')
var Events = require('../../base/events')

var find = require('lodash.find')
var isNumber = require('../../base/utils').isNumber
var requestAnimationFrame = require('../../base/utils').requestAnimationFrame
var cancelAnimationFrame = require('../../base/utils').cancelAnimationFrame

class Core extends UIObject {
  get events() {
    return {
      'webkitfullscreenchange': 'exit',
      'mousemove': 'showMediaControl',
      'mouseleave': 'hideMediaControl'
    }
  }

  get attributes() {
    return {
      'data-player': ''
    }
  }

  constructor(options) {
    super(options)
    PlayerInfo.options = options
    this.options = options
    this.plugins = []
    this.containers = []
    this.createContainers(options)
    //FIXME fullscreen api sucks
    $(document).bind('fullscreenchange', () => this.exit())
    $(document).bind('MSFullscreenChange', () => this.exit())
    $(document).bind('mozfullscreenchange', () => this.exit())
  }

  createContainers(options) {
    this.defer = $.Deferred()
    this.defer.promise(this)
    this.containerFactory = new ContainerFactory(options, options.loader)
    this.containerFactory
      .createContainers()
      .then((containers) => this.setupContainers(containers))
      .then((containers) => this.resolveOnContainersReady(containers))
  }

  updateSize() {
    if (Fullscreen.isFullscreen()) {
      this.setFullscreen()
    } else {
      this.setPlayerSize()
    }
    Mediator.trigger(Events.PLAYER_RESIZE)
  }

  setFullscreen() {
    this.$el.addClass('fullscreen')
    this.$el.removeAttr('style')
    PlayerInfo.previousSize = PlayerInfo.currentSize
    PlayerInfo.currentSize = { width: $(window).width(), height: $(window).height() }
  }

  setPlayerSize() {
    this.$el.removeClass('fullscreen')
    PlayerInfo.currentSize = PlayerInfo.previousSize
    PlayerInfo.previousSize = { width: $(window).width(), height: $(window).height() }
    this.resize(PlayerInfo.currentSize)
  }

  resize(options) {
    if (!isNumber(options.height) && !isNumber(options.width))  {
      this.el.style.height = `${options.height}`;
      this.el.style.width = `${options.width}`;
    } else {
      this.el.style.height = `${options.height}px`;
      this.el.style.width = `${options.width}px`;
    }
    PlayerInfo.previousSize = PlayerInfo.currentSize
    PlayerInfo.currentSize = options
    Mediator.trigger(Events.PLAYER_RESIZE)
  }

  enableResizeObserver() {
    var checkSizeCallback = () => {
      if (this.reqAnimFrame) cancelAnimationFrame(this.reqAnimFrame)
      if (this.previousSize.width != this.$el.width() ||
          this.previousSize.height != this.$el.height()) {
        Mediator.trigger(Events.PLAYER_RESIZE)
        this.previousSize = { width: this.$el.width(), height: this.$el.height() }
      }
      this.reqAnimFrame = requestAnimationFrame(checkSizeCallback)
    }

    this.reqAnimFrame = requestAnimationFrame(checkSizeCallback)
  }

  disableResizeObserver() {
    if (this.reqAnimFrame) cancelAnimationFrame(this.reqAnimFrame)
  }

  resolveOnContainersReady(containers) {
    $.when.apply($, containers).done(() =>this.defer.resolve(this))
  }

  addPlugin(plugin) {
    this.plugins.push(plugin)
  }

  hasPlugin(name) {
    return !!this.getPlugin(name)
  }

  getPlugin(name) {
    return find(this.plugins, (plugin) => plugin.name === name)
  }

  load(sources) {
    sources = sources && sources.constructor === Array ? sources : [sources.toString()];
    this.containers.forEach((container) => container.destroy())
    this.containerFactory.options = assign(this.options, {sources})
    this.containerFactory.createContainers().then((containers) => {
      this.setupContainers(containers)
    })
  }

  destroy() {
    disableResizeObserver()
    this.containers.forEach((container) => container.destroy())
    this.plugins.forEach((plugin) => plugin.destroy())
    this.$el.remove()
    this.mediaControl.destroy()
    $(document).unbind('fullscreenchange')
    $(document).unbind('MSFullscreenChange')
    $(document).unbind('mozfullscreenchange')
}

  exit() {
    this.updateSize()
    this.mediaControl.show()
  }

  setMediaControlContainer(container) {
    this.mediaControl.setContainer(container)
    this.mediaControl.render()
  }

  disableMediaControl() {
    this.mediaControl.disable()
    this.$el.removeClass('nocursor')
  }

  enableMediaControl() {
    this.mediaControl.enable()
  }

  removeContainer(container) {
    this.stopListening(container)
    this.containers = this.containers.filter((c) => c !== container)
  }

  appendContainer(container) {
    this.listenTo(container, Events.CONTAINER_DESTROYED, this.removeContainer)
    this.el.appendChild(container.render().el)
    this.containers.push(container)
  }

  setupContainers(containers) {
    containers.map(this.appendContainer.bind(this))
    this.setupMediaControl(this.getCurrentContainer())
    this.render()
    this.$el.appendTo(this.options.parentElement)
    return containers
  }

  createContainer(source, options) {
    var container = this.containerFactory.createContainer(source, options)
    this.appendContainer(container)
    return container
  }

  setupMediaControl(container) {
    if (this.mediaControl) {
      this.mediaControl.setContainer(container)
    } else {
      this.mediaControl = this.createMediaControl(assign({container: container}, this.options))
      this.listenTo(this.mediaControl, Events.MEDIACONTROL_FULLSCREEN, this.toggleFullscreen)
      this.listenTo(this.mediaControl, Events.MEDIACONTROL_SHOW, this.onMediaControlShow.bind(this, true))
      this.listenTo(this.mediaControl, Events.MEDIACONTROL_HIDE, this.onMediaControlShow.bind(this, false))
    }
  }

  createMediaControl(options) {
    if(options.mediacontrol && options.mediacontrol.external) {
      return new options.mediacontrol.external(options);
    } else {
      return new MediaControl(options);
    }
  }

  getCurrentContainer() {
    return this.containers[0]
  }

  toggleFullscreen() {
    if (!Fullscreen.isFullscreen()) {
      Fullscreen.requestFullscreen(this.el)
      this.$el.addClass('fullscreen')
    } else {
      Fullscreen.cancelFullscreen()
      this.$el.removeClass('fullscreen nocursor')
    }
    this.mediaControl.show()
  }

  showMediaControl(event) {
    this.mediaControl.show(event)
  }

  hideMediaControl(event) {
    this.mediaControl.hide(event)
  }

  onMediaControlShow(showing) {
    if (showing)
      this.$el.removeClass('nocursor')
    else if (Fullscreen.isFullscreen())
      this.$el.addClass('nocursor')
  }

  render() {
    var style = Styler.getStyleFor('core')
    //FIXME
    //this.$el.empty()
    this.$el.append(style)
    this.$el.append(this.mediaControl.render().el)

    this.options.width = this.options.width || this.$el.width()
    this.options.height = this.options.height || this.$el.height()
    var size = {width: this.options.width, height: this.options.height}
    PlayerInfo.previousSize = PlayerInfo.currentSize = size
    this.updateSize()

    this.previousSize = { width: this.$el.width(), height: this.$el.height() }

    this.enableResizeObserver()

    return this
  }
}

module.exports = Core
<|endoftext|>


<commit_before>import React, { Component, PropTypes } from 'react'
import range from 'lodash/range'

export default class Paginator extends Component {
  static propTypes = {
    initialPage: PropTypes.number,
    numberOfPages: PropTypes.number.isRequired,
    onPageChange: PropTypes.func,
    children: PropTypes.func.isRequired
  }

  static defaultProps = { initialPage: 1 }

  state = { page: this.props.initialPage }

  componentWillMount() {
    if (this.props.initialPage < 1) {
      throw new Error('initialPage should be greater than or equal to 1')
    }
    if (this.props.initialPage > this.props.numberOfPages) {
      throw new Error(
        `initialPage should be less than or equal to ${this.props.numberOfPages}`
      )
    }
  }

  getPageRange = (offset = 3) => {
    if (typeof offset !== 'number') {
      throw new Error('offset should be a number')
    }
    const start = this.state.page - offset
    const end = this.state.page + offset
    return range(
      start > 1 ? start : 1,
      (end < this.props.numberOfPages ? end : this.props.numberOfPages) + 1
    )
  }

  setPage = page => {
    if (typeof page !== 'number') {
      throw new Error('page should be a number')
    }
    if (page < 1) {
      throw new Error('page should be greater than or equal to 1')
    }
    if (page > this.props.numberOfPages) {
      throw new Error(
        `page should be less than or equal to ${this.props.numberOfPages}`
      )
    }
    this.setState({ page }, () => {
      if (this.props.onPageChange) {
        this.props.onPageChange(page)
      }
    })
  }

  incrementPage = () => {
    if (this.state.page === this.props.numberOfPages) {
      return
    }
    this.setPage(this.state.page + 1)
  }

  decrementPage = () => {
    if (this.state.page === 1) {
      return
    }
    this.setPage(this.state.page - 1)
  }

  render() {
    return this.props.children({
      ...this.state,
      getPageRange: this.getPageRange,
      setPage: this.setPage,
      incrementPage: this.incrementPage,
      decrementPage: this.decrementPage
    })
  }
}
<commit_msg>Improve Paginator validation<commit_after>import React, { Component, PropTypes } from 'react'
import range from 'lodash/range'

export default class Paginator extends Component {
  static propTypes = {
    initialPage: props => {
      if (typeof props.initialPage !== 'number') {
        return new Error(`initialPage should be a number`)
      }
      if (props.initialPage < 1) {
        throw new Error('initialPage should be greater than or equal to 1')
      }
      if (props.initialPage > props.numberOfPages) {
        throw new Error(
          `initialPage should be less than or equal to ${props.numberOfPages}`
        )
      }
    },
    numberOfPages: PropTypes.number.isRequired,
    onPageChange: PropTypes.func,
    children: PropTypes.func.isRequired
  }

  static defaultProps = { initialPage: 1 }

  state = { page: this.props.initialPage }

  getPageRange = (offset = 3) => {
    if (typeof offset !== 'number') {
      throw new Error('offset should be a number')
    }
    const start = this.state.page - offset
    const end = this.state.page + offset
    return range(
      start > 1 ? start : 1,
      (end < this.props.numberOfPages ? end : this.props.numberOfPages) + 1
    )
  }

  setPage = page => {
    if (typeof page !== 'number') {
      throw new Error('page should be a number')
    }
    if (page < 1) {
      throw new Error('page should be greater than or equal to 1')
    }
    if (page > this.props.numberOfPages) {
      throw new Error(
        `page should be less than or equal to ${this.props.numberOfPages}`
      )
    }
    this.setState({ page }, () => {
      if (this.props.onPageChange) {
        this.props.onPageChange(page)
      }
    })
  }

  incrementPage = () => {
    if (this.state.page === this.props.numberOfPages) {
      return
    }
    this.setPage(this.state.page + 1)
  }

  decrementPage = () => {
    if (this.state.page === 1) {
      return
    }
    this.setPage(this.state.page - 1)
  }

  render() {
    return this.props.children({
      ...this.state,
      getPageRange: this.getPageRange,
      setPage: this.setPage,
      incrementPage: this.incrementPage,
      decrementPage: this.decrementPage
    })
  }
}
<|endoftext|>


<commit_before>import React from 'react'
import Helmet from 'react-helmet'
import system from 'system-components'
import Header from '../components/Header'
import Stats from '../components/Stats'
import { Title, Paragraph } from '../components/Typography'
import { useSiteMetadata } from '../utils/hooks'
import { themeHover, themeUnderline } from '../utils/styles'

const AboutLink = system({ is: 'a' }, themeHover, themeUnderline)

const AboutPage = () => {
  const { title } = useSiteMetadata()

  return (
    <>
      <Helmet>
        <title>About • {title}</title>
      </Helmet>

          <Title>Self-Doxxing</Title>

          <Paragraph fontSize={[1, 2]} lineHeight="copy" mt={5} mb={3}>
            “I’m a frontend developer & designer, with a passion
            for legibility, performance, and the open web. A quick learner with
            a thirst for knowledge. Enthusiastic and hard-working, I pride
            myself on my attention to detail.”
          </Paragraph>

          <Paragraph fontSize={[1, 2]} lineHeight="copy" mt={3} mb={3}>
            That’s what the intro on{' '}
            <AboutLink href="https://resume.lowmess.com">my résumé</AboutLink>{' '}
            says. Kind of boring, huh? Well, it’s a résumé. They’re
            supposed to be boring. (Don’t be such a nitpicker.)
          </Paragraph>

          <Paragraph fontSize={[1, 2]} lineHeight="copy" mt={3} mb={0}>
            Here’s the fun stuff: my name technically isn’t Alec
            (don’t worry about it), my favorite beer is all of them, and
            I’m a staunch believer in the Oxford comma. If you have any
            questions, I’m happy to send you a non sequitur and/or
            fully-serious response if you{' '}
            <AboutLink href="mailto:alec@lowmess.com">email me</AboutLink>.
          </Paragraph>

          <Stats mt={[4, 5]} />

    </>
  )
}

export default AboutPage
<commit_msg>:speech_balloon: change about snippet<commit_after>import React from 'react'
import Helmet from 'react-helmet'
import system from 'system-components'
import Header from '../components/Header'
import Stats from '../components/Stats'
import { Title, Paragraph } from '../components/Typography'
import { useSiteMetadata } from '../utils/hooks'
import { themeHover, themeUnderline } from '../utils/styles'

const AboutLink = system({ is: 'a' }, themeHover, themeUnderline)

const AboutPage = () => {
  const { title } = useSiteMetadata()

  return (
    <>
      <Helmet>
        <title>About • {title}</title>
      </Helmet>

          <Title>Self-Doxxing</Title>

          <Paragraph fontSize={[1, 2]} lineHeight="copy" mt={5} mb={3}>
            “I’m a frontend developer & designer, with a passion
            for legibility, performance, and the open web. A quick learner with
            a thirst for knowledge. Enthusiastic and hard-working, I pride
            myself on my attention to detail.”
          </Paragraph>

          <Paragraph fontSize={[1, 2]} lineHeight="copy" mt={3} mb={3}>
            That’s what the intro on{' '}
            <AboutLink href="https://resume.lowmess.com">my résumé</AboutLink>{' '}
            says. Kind of boring, huh? Well, it’s a résumé. They’re
            supposed to be boring. (Don’t be such a nitpicker.)
          </Paragraph>

          <Paragraph fontSize={[1, 2]} lineHeight="copy" mt={3} mb={0}>
            Here’s the fun stuff: my name technically isn’t Alec
            (don’t worry about it), my favorite beer is all of them, and I
            like to list things in threes. If you have any questions, I’m
            happy to send you a non sequitur and/or fully-serious response if
            you <AboutLink href="mailto:alec@lowmess.com">email me</AboutLink>.
          </Paragraph>

          <Stats mt={[4, 5]} />

    </>
  )
}

export default AboutPage
<|endoftext|>


<commit_before>import * as api from '../api';

export function deleteAddon(addonId) {
    return function(dispatch, getState) {
        const projectId = getState().project.get('id');
        dispatch(deletingAddon(addonId));
        if (addonId < 0) {
            dispatch(deletedAddon(addonId));
        } else {
            return api.delete(`/projects/${projectId}/integrations/${addonId}`)
                .then(resp => {
                    dispatch(deletedAddon(addonId));
                }, resp => {
                    throw new Error(`Failed to delete addon #${addonId}`);
                });
        }
    }
}

export function deletingAddon(id) {
    return {
        type: 'DELETING_ADDON',
        id
    }
}

export function deletedAddon(id) {
    return {
        type: 'DELETED_ADDON',
        id
    }
}

export function newAddon(kind) {
    const id = (+new Date()) * -1;
    return {
        type: 'NEW_ADDON',
        kind,
        id
    }
}

export function fetchAddons() {
    function shouldFetch(state) {
        let isFetching = state.addons.get('isFetching');
        let itemCount = state.addons.get('items').size;
        if (!isFetching && itemCount === 0) {
            return true;
        } else {
            return false;
        }
    }

    return function(dispatch, getState) {
        const state = getState();
        const projectId = state.project.get('id');
        if (shouldFetch(state)) {
            dispatch(fetchingAddons());
            return api.get(`/projects/${projectId}/addons`)
                .then(resp => {
                    dispatch(receivedAddons(resp.addon));
                }, resp => {
                    throw new Error('Failed to fetch project addons.');
                });
        } else {
          return Promise.resolve();
        }
    }
}

export function fetchingAddons() {
    return {
        type: 'FETCHING_ADDONS'
    }
}

export function receivedAddons(addons) {
    return {
        type: 'RECEIVED_ADDONS',
        items: addons
    }
}

export function saveAddon(id, kind, data) {
    return function(dispatch, getState) {
        const state = getState();
        const projectId = state.project.get('id');
        const body = JSON.stringify({
            kind,
            data
        });
        let push;

        if (id < 0) {
            push = fetch(`/projects/${projectId}/integrations`, {
                method: 'POST',
                body
            });
        } else {
            push = fetch(`/projects/${projectId}/integrations/${id}`, {
                method: 'PATCH',
                body
            });
        }

        return push.then(resp => {
            dispatch(changedAddon(resp.addon, id));
        }, resp => {
            throw new Error(`Failed to save addon ${kind} #${id}.`)
        });
  }
}

export function changedAddon(addon, prevId) {
    return {
        type: 'CHANGED_ADDON',
        addon,
        prevId
    }
}<commit_msg>match methods in api<commit_after>import * as api from '../api';

export function deleteAddon(addonId) {
    return function(dispatch, getState) {
        const projectId = getState().project.get('id');
        dispatch(deletingAddon(addonId));
        if (addonId < 0) {
            dispatch(deletedAddon(addonId));
        } else {
            return api.delet(`/projects/${projectId}/integrations/${addonId}`)
                .then(resp => {
                    dispatch(deletedAddon(addonId));
                }, resp => {
                    throw new Error(`Failed to delete addon #${addonId}`);
                });
        }
    }
}

export function deletingAddon(id) {
    return {
        type: 'DELETING_ADDON',
        id
    }
}

export function deletedAddon(id) {
    return {
        type: 'DELETED_ADDON',
        id
    }
}

export function newAddon(kind) {
    const id = (+new Date()) * -1;
    return {
        type: 'NEW_ADDON',
        kind,
        id
    }
}

export function fetchAddons() {
    function shouldFetch(state) {
        let isFetching = state.addons.get('isFetching');
        let itemCount = state.addons.get('items').size;
        if (!isFetching && itemCount === 0) {
            return true;
        } else {
            return false;
        }
    }

    return function(dispatch, getState) {
        const state = getState();
        const projectId = state.project.get('id');
        if (shouldFetch(state)) {
            dispatch(fetchingAddons());
            return api.get(`/projects/${projectId}/addons`)
                .then(resp => {
                    dispatch(receivedAddons(resp.addon));
                }, resp => {
                    throw new Error('Failed to fetch project addons.');
                });
        } else {
          return Promise.resolve();
        }
    }
}

export function fetchingAddons() {
    return {
        type: 'FETCHING_ADDONS'
    }
}

export function receivedAddons(addons) {
    return {
        type: 'RECEIVED_ADDONS',
        items: addons
    }
}

export function saveAddon(id, kind, data) {
    return function(dispatch, getState) {
        const state = getState();
        const projectId = state.project.get('id');
        const body = JSON.stringify({
            kind,
            data
        });
        let push;

        if (id < 0) {
            push = api.post(`/projects/${projectId}/integrations`, {
                body
            });
        } else {
            push = api.patch(`/projects/${projectId}/integrations/${id}`, {
                body
            });
        }

        return push.then(resp => {
            dispatch(changedAddon(resp.addon, id));
        }, resp => {
            throw new Error(`Failed to save addon ${kind} #${id}.`)
        });
  }
}

export function changedAddon(addon, prevId) {
    return {
        type: 'CHANGED_ADDON',
        addon,
        prevId
    }
}<|endoftext|>


<commit_before>import { connect } from 'react-redux'
import { reduxForm } from 'redux-form'
import * as formatNumberHelper from '~client/utils/format-number-helper'
import Page from './page'

const mapStateToProps = (state, props) => {
  const { goal } = props.widget

  return {
    initialValues: {
      goal: goal ? formatNumberHelper.float(goal) : undefined,
      default_donation_value: 1,
      main_color: '#54d0f6',
      recurring_period: 30,
      payment_type: 'unique',
      payment_methods: 'false',
      ...props.widget.settings || {}
    }
  }
}

const fields = [
  'title_text', 'button_text', 'goal', 'goal_date_limit', 'main_color', 'default_donation_value',
  'donation_value1', 'donation_value2', 'donation_value3', 'donation_value4',
  'donation_value5', 'recurring_period', 'payment_type', 'payment_methods'
]

const validate = values => {
  const errors = {}
  if (!values.button_text) {
    errors.button_text = 'Insira o texto do botão'
  } else if (values.button_text.length > 50) {
    errors.button_text = 'O limite de caracteres foi atingido.'
  }

  if (values.goal && String(values.goal).replace(/\./g, '').match(/\D/g)) {
    errors.goal = 'Formato inválido. ' +
      'Utilize apenas números e ponto como separador decimal. ' +
      'Ex: 10000.50'
  }
  return errors
}

export default connect(mapStateToProps)(
  reduxForm({ form: 'widgetsDonationForm', fields, validate })(Page)
)
<commit_msg>chore(donation-widget): improve goal value validation #814<commit_after>import { connect } from 'react-redux'
import { reduxForm } from 'redux-form'
import * as formatNumberHelper from '~client/utils/format-number-helper'
import Page from './page'

const mapStateToProps = (state, props) => {
  const { goal } = props.widget

  return {
    initialValues: {
      goal: goal ? formatNumberHelper.float(goal) : undefined,
      default_donation_value: 1,
      main_color: '#54d0f6',
      recurring_period: 30,
      payment_type: 'unique',
      payment_methods: 'false',
      ...props.widget.settings || {}
    }
  }
}

const fields = [
  'title_text', 'button_text', 'goal', 'goal_date_limit', 'main_color', 'default_donation_value',
  'donation_value1', 'donation_value2', 'donation_value3', 'donation_value4',
  'donation_value5', 'recurring_period', 'payment_type', 'payment_methods'
]

const validate = values => {
  const errors = {}
  if (!values.button_text) {
    errors.button_text = 'Insira o texto do botão'
  } else if (values.button_text.length > 50) {
    errors.button_text = 'O limite de caracteres foi atingido.'
  }

  if (values.goal) {
    const hasAlphanumerics = String(values.goal).replace(/\./g, '').match(/\D/g)
    const hasMultipleDots = String(values.goal).match(/\./g || []).length > 1
    if (hasAlphanumerics || hasMultipleDots) {
      errors.goal = 'Formato inválido. ' +
        'Utilize apenas números e ponto como separador decimal. ' +
        'Ex: 10000.50'
    }
  }
  return errors
}

export default connect(mapStateToProps)(
  reduxForm({ form: 'widgetsDonationForm', fields, validate })(Page)
)
<|endoftext|>


<commit_before>/*!
 * Copyright 2016 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * 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';

var assert = require('assert');
var async = require('async');
var util = require('util');
var winston = require('winston');

var env = require('../../../system-test/env.js');

var logging = require('@google-cloud/logging')(env);
var loggingWinston = require('../');

describe('LoggingWinston', function() {
  var WRITE_CONSISTENCY_DELAY_MS = 20000;

  winston.add(loggingWinston, env);

  describe('log', function() {
    var testTimestamp = new Date();

    var testData = [
      {
        args: ['first'],
        verify: function(entry) {
          assert.strictEqual(entry.data, 'first');
        }
      },

      {
        args: ['second'],
        verify: function(entry) {
          assert.strictEqual(entry.data, 'second');
        }
      },

      {
        args: [
          'third',
          {
            testTimestamp: testTimestamp
          }
        ],
        verify: function(entry) {
          assert.strictEqual(entry.data, 'third');

          assert.strictEqual(
            entry.metadata.labels.testTimestamp,
            util.inspect(testTimestamp)
          );
        }
      }
    ];

    it('should properly write log entries', function(done) {
      async.each(testData, function(test, callback) {
        winston.info.apply(winston, test.args.concat(callback));
      }, function(err) {
        assert.ifError(err);

        setTimeout(function() {
          logging.log('winston_log').getEntries({
            pageSize: testData.length
          }, function(err, entries) {
            assert.ifError(err);

            assert.strictEqual(entries.length, testData.length);

            entries
              .reverse()
              .forEach(function(entry, index) {
                var test = testData[index];
                test.verify(entry);
              });

            done();
          });
        }, WRITE_CONSISTENCY_DELAY_MS);
      });
    });
  });
});
<commit_msg>logging-winston: avoid printing to console during test (#1985)<commit_after>/*!
 * Copyright 2016 Google Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * 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';

var assert = require('assert');
var async = require('async');
var util = require('util');
var winston = require('winston');

var env = require('../../../system-test/env.js');

var logging = require('@google-cloud/logging')(env);
var LoggingWinston = require('../');

describe('LoggingWinston', function() {
  var WRITE_CONSISTENCY_DELAY_MS = 20000;

  var logger = new winston.Logger({
    transports: [
      new LoggingWinston(env)
    ]
  });

  describe('log', function() {
    var testTimestamp = new Date();

    var testData = [
      {
        args: ['first'],
        verify: function(entry) {
          assert.strictEqual(entry.data, 'first');
        }
      },

      {
        args: ['second'],
        verify: function(entry) {
          assert.strictEqual(entry.data, 'second');
        }
      },

      {
        args: [
          'third',
          {
            testTimestamp: testTimestamp
          }
        ],
        verify: function(entry) {
          assert.strictEqual(entry.data, 'third');

          assert.strictEqual(
            entry.metadata.labels.testTimestamp,
            util.inspect(testTimestamp)
          );
        }
      }
    ];

    it('should properly write log entries', function(done) {
      async.each(testData, function(test, callback) {
        logger.info.apply(logger, test.args.concat(callback));
      }, function(err) {
        assert.ifError(err);

        setTimeout(function() {
          logging.log('winston_log').getEntries({
            pageSize: testData.length
          }, function(err, entries) {
            assert.ifError(err);

            assert.strictEqual(entries.length, testData.length);

            entries
              .reverse()
              .forEach(function(entry, index) {
                var test = testData[index];
                test.verify(entry);
              });

            done();
          });
        }, WRITE_CONSISTENCY_DELAY_MS);
      });
    });
  });
});
<|endoftext|>


<commit_before>(function () {
    "use strict";
    // Save a reference to the global object (`window` in the browser, `exports`
    // on the server).
    var root = this;

    // Check if the `FH` namespace already exists and create it otherwise. We'll
    // attach all our exposed objects to it.
    var FH = root.FH = root.FH || {};

    // Map of FH types to Backgrid cell types
    var FHToBackgridTypes = {
        'integer': 'integer',
        'decimal': 'number',
        /*'select': '',
         'select all that apply': '',
         'select one': '',*/
        'photo': '',
        'image': '',
        'date': 'date',
        'datetime': 'datetime'
    };

    var PageableDataset = FH.PageableDataset = Backbone.PageableCollection.extend({
        state: {
            pageSize: 50
        },
        mode: "client", // page entirely on the client side,
        model: FH.Data,
        initialize: function (models, options) {
            // set the url
            /*if(! options.url) {
             throw new Error(
             "You must specify the dataset's url within the options");
             }*/
            this.url = options && options.url;

            // Call super
            return Backbone.PageableCollection.prototype.initialize.apply(this, arguments);
        }
    });

    var NameLabelToggle = Backbone.View.extend({
        className: 'table-control-container label-toggle-container',

        template: _.template('' +
            '' +
            '' +
            '<input class="name-label-toggle" type="checkbox" name="toggle_labels" aria-controls="data-table" <% if (isChecked) { %>checked="checked" <% } %> />' +
            ' Toggle between choice names and choice labels' +
            '' +
            ''),

        events: {
            'click .name-label-toggle': "toggleLabels"
        },

        render: function () {
            this.$el.empty().append(this.template({
                isChecked: false
            }));
            this.delegateEvents();
            return this;
        },

        toggleLabels: function (e) {
            var enabled = !!$(e.currentTarget).attr('checked');
            this.trigger('toggled', enabled);
        }
    });

    var ClickableRow = Backgrid.Row.extend({
        highlightColor: 'lightYellow',
        /*events: {
            'click': 'rowClicked',
            'focusout': 'rowLostFocus'
        },*/
        initialize: function (options) {
            return Backgrid.Row.prototype.initialize.apply(this, arguments);
        },
        rowClicked: function () {
            this.$el.css('background-color', this.highlightColor);
        },
        rowLostFocus: function () {
            this.$el.removeAttr('style');
        }
    });

    var DataTableView = FH.DataTableView = Backbone.View.extend({
        // Instance of the `Form` object
        form: void 0,

        // Instance of the `Data` object
        data: void 0,

        // Whether to show header names or labels
        showHeaderLabels: false,

        // Whether to show select names or labels
        showLabels: false,

        initialize: function (options) {
            if (!options.formUrl) {
                throw new Error("You must define a formUrl property within options");
            }

            if (!options.dataUrl) {
                throw new Error("You must define a dataUrl property within options");
            }

            // Setup the form
            this.form = new FH.Form({}, {url: options.formUrl});

            // Setup the data
            this.data = new FH.PageableDataset([], {
                url: options.dataUrl
            });

            // Initialize the header name/label toggle
            var headerLangSwitcher = new NameLabelLanguagePicker({
                label: "Column Headers",
                model: this.form
            });

            // Initialize the data name/label toggle
            var dataLangSwitcher = new NameLabelLanguagePicker({
                label: "Answer Values",
                model: this.form
            });

            this.form.on('load', function () {
                var dataTableView = this;

                // Initialize the data
                this.data.on('load', function () {

                    // Disable this callback - infinite loop
                    this.data.off('load');

                    // Append the toggle labels checkbox
                    $(this.labelToggleTemplate({isChecked: this.showLabels})).insertAfter(this.$('.dynatable-per-page'));

                    this.delegateEvents({
                        'click input.toggle-labels': 'onToggleLabels'
                    });
                }, this);

                // Initialize the grid
                this.dataGrid = new Backgrid.Grid({
                    row: ClickableRow,
                    className: 'backgrid table table-striped table-hover',
                    columns: this.form.fields.map(function (f) {
                        var column = {
                            name: f.get(FH.constants.XPATH),
                            //label: f.get(FH.constants.LABEL, dataTableView.form.get('language')),
                            label: f.get(FH.constants.NAME),
                            editable: false,
                            cell: "string"//FHToBackgridTypes[f.get(FH.constants.TYPE)] || "string"
                        };
                        if (f.isA(FH.types.SELECT_ONE) || f.isA(FH.types.SELECT_MULTIPLE)) {
                            column.formatter = {
                                fromRaw: function (rawData) {
                                    return DataTableView.NameOrLabel(f, rawData, dataTableView.showLabels, dataTableView.form.get('language'));
                                }
                            };
                        }
                        return column;
                    }),
                    collection: this.data
                });

                this.$el.append(this.dataGrid.render().$el);

                // Initialize the paginator
                var paginator = new Backgrid.Extension.Paginator({
                    collection: this.data
                });

                // Render the paginator
                this.$el.append(paginator.render().$el);

                // Initialize a client-side filter to filter on the client
                // mode pageable collection's cache.
                var filter = new Backgrid.Extension.ClientSideFilter({
                    collection: this.data,
                    fields: this.form.fields
                        .filter(function (f) {
                            // Ignore select fields
                            return !f.isA(FH.types.SELECT_ONE) && !f.isA(FH.types.SELECT_MULTIPLE);
                        }).map(function (f) {
                            return f.get(FH.constants.XPATH);
                        })
                });

                // Render the filter
                this.$el.prepend(filter.render().$el);

                // Add some space to the filter and move it to the right
                filter.$el.css({float: "right", margin: "20px"});

                // catch the `switched` event
                dataLangSwitcher.on('switch', function (language) {
                    // if the new language is `0`, we want to show xml values, otherwise, we want labels in whatever language is specified
                    this.showLabels = language !== '-1';
                    // set the language if we're showing labels
                    if(this.showLabels) {
                        this.form.set({language: language}, {silent: true});
                    }
                    this.dataGrid.render();
                }, this);

                this.$el.prepend(dataLangSwitcher.render().$el);

                // catch the `switched` event
                headerLangSwitcher.on('switch', function (language) {
                    // if the new language is `0`, we want to show xml values, otherwise, we want labels in whatever language is specified
                    this.showHeaderLabels = language !== '-1';
                    // set the language if we're showing labels
                    this.form.set({header_language: language});
                }, this);

                this.$el.prepend(headerLangSwitcher.render().$el);

                // only add the language picker if we have multiple languages
                if (this.form.get('languages') && this.form.get('languages').length > 1) {
                    // Initialize the language selector
                    var languagePicker = new FH.LanguagePicker({
                        model: this.form,
                        className: 'table-control-container language-picker-container'
                    });

                    languagePicker.render().$el.insertBefore(this.$('.label-toggle-container'));
                }

                // Fetch some data
                this.data.fetch({reset: true});

            }, this);

            // Catch language change events
            this.form.on('change:header_language', function (model, language) {
                var dataTableView = this;
                if (this.dataGrid) {
                    this.dataGrid.columns.each(function (column) {
                        var label,
                            field = dataTableView.form.fields
                                .find(function (f) {
                                    return f.get(FH.constants.XPATH) === column.get('name');
                                });

                        if(dataTableView.showHeaderLabels) {
                            label = field.get(FH.constants.LABEL, language);
                        } else {
                            label = field.get(FH.constants.NAME);
                        }
                        column.set({'label': label});
                    });
                    this.dataGrid.header.render();
                }
            }, this);

            this.form.load();
        }
    });

    var NameLabelLanguagePicker = Backbone.View.extend({
        label: void 0,

        className: 'table-control-container',

        template: _.template(
            '<%= label %><select><% _.each(languages, function(lang){ %>' +
                '<option value="<%= lang["name"] %>"><%= lang["value"] %></option> ' +
            '<% }); %></select>'),

        events: {
            'change select': function (evt) {
                var value = $(evt.currentTarget).val() || undefined;
                this.trigger('switch', value);
            }
        },

        initialize: function (options) {
            this.label = options.label || " ";
            Backbone.View.prototype.initialize.apply(this, arguments);
        },

        render: function () {
            //this.$el.html(this.template());
            var languages = NameLabelLanguagePicker.LanguagesForSelect(
                this.model);
            this.$el.empty().append(this.template({
                languages: languages,
                label: this.label
            }));
            return this;
        }
    });

    NameLabelLanguagePicker.LanguagesForSelect = function (model) {
        var languages = model.get('languages').length == 0?
            [{name: null, value: 'Show Labels'}]:
            model.get('languages').map(
                function(lang){
                    return {name: lang, value: "Show Labels in " + lang};
                });
        languages.unshift({name: '-1', value: 'Show XML Values'});
        return languages
    };

    // Used by select formatters to return wither name the name or label for a response
    DataTableView.NameOrLabel = function (field, value, showLabels, language) {
        var xpath,
            choices,
            selections,
            results;

        // if showLabels === true, get the label for the selected value(s)
        if (showLabels) {
            choices = new FH.FieldSet(field.get(FH.constants.CHILDREN));

            // Split the value on a space to get a list for multiple choices
            selections = value && value.split(' ') || [];
            results = [];

            _.each(selections, function (selection) {
                var choice = choices.find(function (c) {
                    return c.get(FH.constants.NAME) === selection;
                });
                if (choice) {
                    results.push(choice.get(FH.constants.LABEL, language));
                }
            });
            return results.join(', ');
        } else {
            return value;
        }
    };
}).call(this);
<commit_msg>PLD: remove comments<commit_after>(function () {
    "use strict";
    // Save a reference to the global object (`window` in the browser, `exports`
    // on the server).
    var root = this;

    // Check if the `FH` namespace already exists and create it otherwise. We'll
    // attach all our exposed objects to it.
    var FH = root.FH = root.FH || {};

    // Map of FH types to Backgrid cell types
    var FHToBackgridTypes = {
        'integer': 'integer',
        'decimal': 'number',
        /*'select': '',
         'select all that apply': '',
         'select one': '',*/
        'photo': '',
        'image': '',
        'date': 'date',
        'datetime': 'datetime'
    };

    var PageableDataset = FH.PageableDataset = Backbone.PageableCollection.extend({
        state: {
            pageSize: 50
        },
        mode: "client", // page entirely on the client side,
        model: FH.Data,
        initialize: function (models, options) {
            // set the url
            /*if(! options.url) {
             throw new Error(
             "You must specify the dataset's url within the options");
             }*/
            this.url = options && options.url;

            // Call super
            return Backbone.PageableCollection.prototype.initialize.apply(this, arguments);
        }
    });

    var NameLabelToggle = Backbone.View.extend({
        className: 'table-control-container label-toggle-container',

        template: _.template('' +
            '' +
            '' +
            '<input class="name-label-toggle" type="checkbox" name="toggle_labels" aria-controls="data-table" <% if (isChecked) { %>checked="checked" <% } %> />' +
            ' Toggle between choice names and choice labels' +
            '' +
            ''),

        events: {
            'click .name-label-toggle': "toggleLabels"
        },

        render: function () {
            this.$el.empty().append(this.template({
                isChecked: false
            }));
            this.delegateEvents();
            return this;
        },

        toggleLabels: function (e) {
            var enabled = !!$(e.currentTarget).attr('checked');
            this.trigger('toggled', enabled);
        }
    });

    var ClickableRow = Backgrid.Row.extend({
        highlightColor: 'lightYellow',
        /*events: {
            'click': 'rowClicked',
            'focusout': 'rowLostFocus'
        },*/
        initialize: function (options) {
            return Backgrid.Row.prototype.initialize.apply(this, arguments);
        },
        rowClicked: function () {
            this.$el.css('background-color', this.highlightColor);
        },
        rowLostFocus: function () {
            this.$el.removeAttr('style');
        }
    });

    var DataTableView = FH.DataTableView = Backbone.View.extend({
        // Instance of the `Form` object
        form: void 0,

        // Instance of the `Data` object
        data: void 0,

        // Whether to show header names or labels
        showHeaderLabels: false,

        // Whether to show select names or labels
        showLabels: false,

        initialize: function (options) {
            if (!options.formUrl) {
                throw new Error("You must define a formUrl property within options");
            }

            if (!options.dataUrl) {
                throw new Error("You must define a dataUrl property within options");
            }

            // Setup the form
            this.form = new FH.Form({}, {url: options.formUrl});

            // Setup the data
            this.data = new FH.PageableDataset([], {
                url: options.dataUrl
            });

            // Initialize the header name/label toggle
            var headerLangSwitcher = new NameLabelLanguagePicker({
                label: "Column Headers",
                model: this.form
            });

            // Initialize the data name/label toggle
            var dataLangSwitcher = new NameLabelLanguagePicker({
                label: "Answer Values",
                model: this.form
            });

            this.form.on('load', function () {
                var dataTableView = this;

                // Initialize the data
                this.data.on('load', function () {

                    // Disable this callback - infinite loop
                    this.data.off('load');

                    // Append the toggle labels checkbox
                    $(this.labelToggleTemplate({isChecked: this.showLabels})).insertAfter(this.$('.dynatable-per-page'));

                    this.delegateEvents({
                        'click input.toggle-labels': 'onToggleLabels'
                    });
                }, this);

                // Initialize the grid
                this.dataGrid = new Backgrid.Grid({
                    row: ClickableRow,
                    className: 'backgrid table table-striped table-hover',
                    columns: this.form.fields.map(function (f) {
                        var column = {
                            name: f.get(FH.constants.XPATH),
                            label: f.get(FH.constants.NAME),
                            editable: false,
                            cell: "string"//FHToBackgridTypes[f.get(FH.constants.TYPE)] || "string"
                        };
                        if (f.isA(FH.types.SELECT_ONE) || f.isA(FH.types.SELECT_MULTIPLE)) {
                            column.formatter = {
                                fromRaw: function (rawData) {
                                    return DataTableView.NameOrLabel(f, rawData, dataTableView.showLabels, dataTableView.form.get('language'));
                                }
                            };
                        }
                        return column;
                    }),
                    collection: this.data
                });

                this.$el.append(this.dataGrid.render().$el);

                // Initialize the paginator
                var paginator = new Backgrid.Extension.Paginator({
                    collection: this.data
                });

                // Render the paginator
                this.$el.append(paginator.render().$el);

                // Initialize a client-side filter to filter on the client
                // mode pageable collection's cache.
                var filter = new Backgrid.Extension.ClientSideFilter({
                    collection: this.data,
                    fields: this.form.fields
                        .filter(function (f) {
                            // Ignore select fields
                            return !f.isA(FH.types.SELECT_ONE) && !f.isA(FH.types.SELECT_MULTIPLE);
                        }).map(function (f) {
                            return f.get(FH.constants.XPATH);
                        })
                });

                // Render the filter
                this.$el.prepend(filter.render().$el);

                // Add some space to the filter and move it to the right
                filter.$el.css({float: "right", margin: "20px"});

                // catch the `switched` event
                dataLangSwitcher.on('switch', function (language) {
                    // if the new language is `0`, we want to show xml values, otherwise, we want labels in whatever language is specified
                    this.showLabels = language !== '-1';
                    // set the language if we're showing labels
                    if(this.showLabels) {
                        this.form.set({language: language}, {silent: true});
                    }
                    this.dataGrid.render();
                }, this);

                this.$el.prepend(dataLangSwitcher.render().$el);

                // catch the `switched` event
                headerLangSwitcher.on('switch', function (language) {
                    // if the new language is `0`, we want to show xml values, otherwise, we want labels in whatever language is specified
                    this.showHeaderLabels = language !== '-1';
                    // set the language if we're showing labels
                    this.form.set({header_language: language});
                }, this);

                this.$el.prepend(headerLangSwitcher.render().$el);

                // only add the language picker if we have multiple languages
                if (this.form.get('languages') && this.form.get('languages').length > 1) {
                    // Initialize the language selector
                    var languagePicker = new FH.LanguagePicker({
                        model: this.form,
                        className: 'table-control-container language-picker-container'
                    });

                    languagePicker.render().$el.insertBefore(this.$('.label-toggle-container'));
                }

                // Fetch some data
                this.data.fetch({reset: true});

            }, this);

            // Catch language change events
            this.form.on('change:header_language', function (model, language) {
                var dataTableView = this;
                if (this.dataGrid) {
                    this.dataGrid.columns.each(function (column) {
                        var label,
                            field = dataTableView.form.fields
                                .find(function (f) {
                                    return f.get(FH.constants.XPATH) === column.get('name');
                                });

                        if(dataTableView.showHeaderLabels) {
                            label = field.get(FH.constants.LABEL, language);
                        } else {
                            label = field.get(FH.constants.NAME);
                        }
                        column.set({'label': label});
                    });
                    this.dataGrid.header.render();
                }
            }, this);

            this.form.load();
        }
    });

    var NameLabelLanguagePicker = Backbone.View.extend({
        label: void 0,

        className: 'table-control-container',

        template: _.template(
            '<%= label %><select><% _.each(languages, function(lang){ %>' +
                '<option value="<%= lang["name"] %>"><%= lang["value"] %></option> ' +
            '<% }); %></select>'),

        events: {
            'change select': function (evt) {
                var value = $(evt.currentTarget).val() || undefined;
                this.trigger('switch', value);
            }
        },

        initialize: function (options) {
            this.label = options.label || " ";
            Backbone.View.prototype.initialize.apply(this, arguments);
        },

        render: function () {
            var languages = NameLabelLanguagePicker.LanguagesForSelect(
                this.model);
            this.$el.empty().append(this.template({
                languages: languages,
                label: this.label
            }));
            return this;
        }
    });

    NameLabelLanguagePicker.LanguagesForSelect = function (model) {
        var languages = model.get('languages').length == 0?
            [{name: null, value: 'Show Labels'}]:
            model.get('languages').map(
                function(lang){
                    return {name: lang, value: "Show Labels in " + lang};
                });
        languages.unshift({name: '-1', value: 'Show XML Values'});
        return languages
    };

    // Used by select formatters to return wither name the name or label for a response
    DataTableView.NameOrLabel = function (field, value, showLabels, language) {
        var xpath,
            choices,
            selections,
            results;

        // if showLabels === true, get the label for the selected value(s)
        if (showLabels) {
            choices = new FH.FieldSet(field.get(FH.constants.CHILDREN));

            // Split the value on a space to get a list for multiple choices
            selections = value && value.split(' ') || [];
            results = [];

            _.each(selections, function (selection) {
                var choice = choices.find(function (c) {
                    return c.get(FH.constants.NAME) === selection;
                });
                if (choice) {
                    results.push(choice.get(FH.constants.LABEL, language));
                }
            });
            return results.join(', ');
        } else {
            return value;
        }
    };
}).call(this);
<|endoftext|>