<commit_before>import fs from 'fs';
import path from 'path';
import electron from 'electron';
import windowStateKeeper from 'electron-window-state';
import helpers from './../../helpers/helpers';
import createMenu from './../menu/menu';
import initContextMenu from './../contextMenu/contextMenu';

const {BrowserWindow, shell, ipcMain, dialog} = electron;
const {isOSX, linkIsInternal, getCssToInject} = helpers;

const ZOOM_INTERVAL = 0.1;

/**
 *
 * @param {{}} options AppArgs from nativefier.json
 * @param {function} onAppQuit
 * @param {function} setDockBadge
 * @returns {electron.BrowserWindow}
 */
function createMainWindow(options, onAppQuit, setDockBadge) {
    const mainWindowState = windowStateKeeper({
        defaultWidth: options.width || 1280,
        defaultHeight: options.height || 800
    });

    const mainWindow = new BrowserWindow({
        frame: !options.hideWindowFrame,
        width: mainWindowState.width,
        height: mainWindowState.height,
        x: mainWindowState.x,
        y: mainWindowState.y,
        'auto-hide-menu-bar': !options.showMenuBar,
        // Convert dashes to spaces because on linux the app name is joined with dashes
        title: options.name,
        'web-preferences': {
            javascript: true,
            plugins: true,
            // node globals causes problems with sites like messenger.com
            nodeIntegration: false,
            webSecurity: !options.insecure,
            preload: path.join(__dirname, 'static', 'preload.js')
        },
        // after webpack path here should reference `resources/app/`
        icon: path.join(__dirname, '../', '/icon.png'),
        // set to undefined and not false because explicitly setting to false will disable full screen
        fullscreen: options.fullScreen || undefined
    });

    mainWindowState.manage(mainWindow);

    // after first run, no longer force full screen to be true
    if (options.fullScreen) {
        options.fullScreen = undefined;
        fs.writeFileSync(path.join(__dirname, '..', 'nativefier.json'), JSON.stringify(options));
    }

    // after first run, no longer force maximize to be true
    if (options.maximize) {
        mainWindow.maximize();
        options.maximize = undefined;
        fs.writeFileSync(path.join(__dirname, '..', 'nativefier.json'), JSON.stringify(options));
    }

    let currentZoom = 1;

    const onZoomIn = () => {
        currentZoom += ZOOM_INTERVAL;
        mainWindow.webContents.send('change-zoom', currentZoom);
    };

    const onZoomOut = () => {
        currentZoom -= ZOOM_INTERVAL;
        mainWindow.webContents.send('change-zoom', currentZoom);
    };

    const clearAppData = () => {
        dialog.showMessageBox(mainWindow, {
            type: 'warning',
            buttons: ['Yes', 'Cancel'],
            defaultId: 1,
            title: 'Clear cache confirmation',
            message: 'This will clear all data (cookies, local storage etc) from this app. Are you sure you wish to proceed?'
        }, response => {
            if (response === 0) {
                const session = mainWindow.webContents.session;
                session.clearStorageData(() => {
                    session.clearCache(() => {
                        mainWindow.loadURL(options.targetUrl);
                    });
                });
            }
        });
    };

    const onGoBack = () => {
        mainWindow.webContents.goBack();
    };

    const onGoForward = () => {
        mainWindow.webContents.goForward();
    };

    const getCurrentUrl = () => {
        return mainWindow.webContents.getURL();
    };

    const menuOptions = {
        nativefierVersion: options.nativefierVersion,
        appQuit: onAppQuit,
        zoomIn: onZoomIn,
        zoomOut: onZoomOut,
        goBack: onGoBack,
        goForward: onGoForward,
        getCurrentUrl: getCurrentUrl,
        clearAppData: clearAppData
    };

    createMenu(menuOptions);
    if (!options.disableContextMenu) {
        initContextMenu(mainWindow);
    }

    if (options.userAgent) {
        mainWindow.webContents.setUserAgent(options.userAgent);
    }

    mainWindow.webContents.on('did-finish-load', () => {
        mainWindow.webContents.send('params', JSON.stringify(options));
        mainWindow.webContents.insertCSS(getCssToInject());
    });

    if (options.counter) {
        mainWindow.on('page-title-updated', () => {
            if (mainWindow.isFocused()) {
                return;
            }

            if (options.counter) {
                const itemCountRegex = /[\(\[{](\d*?)[}\]\)]/;
                const match = itemCountRegex.exec(mainWindow.getTitle());
                if (match) {
                    setDockBadge(match[1]);
                }
                return;
            }
            setDockBadge('●');
        });
    }

    mainWindow.webContents.on('new-window', (event, urlToGo) => {
        if (mainWindow.useDefaultWindowBehaviour) {
            mainWindow.useDefaultWindowBehaviour = false;
            return;
        }

        if (linkIsInternal(options.targetUrl, urlToGo)) {
            return;
        }
        event.preventDefault();
        shell.openExternal(urlToGo);
    });

    mainWindow.loadURL(options.targetUrl);

    mainWindow.on('focus', () => {
        setDockBadge('');
    });

    mainWindow.on('close', event => {
        if (mainWindow.isFullScreen()) {
            mainWindow.setFullScreen(false);
            mainWindow.once('leave-full-screen', maybeHideWindow.bind(this, mainWindow, event, options.fastQuit));
        }
        maybeHideWindow(mainWindow, event, options.fastQuit);
    });

    return mainWindow;
}

ipcMain.on('cancelNewWindowOverride', () => {
    const allWindows = BrowserWindow.getAllWindows();
    allWindows.forEach(window => {
        window.useDefaultWindowBehaviour = false;
    });
});

function maybeHideWindow(window, event, fastQuit) {
    if (isOSX() && !fastQuit) {
        // this is called when exiting from clicking the cross button on the window
        event.preventDefault();
        window.hide();
    }
    // will close the window on other platforms
}

export default createMainWindow;
<commit_msg>Update browserWindow options to follow new electron api<commit_after>import fs from 'fs';
import path from 'path';
import electron from 'electron';
import windowStateKeeper from 'electron-window-state';
import helpers from './../../helpers/helpers';
import createMenu from './../menu/menu';
import initContextMenu from './../contextMenu/contextMenu';

const {BrowserWindow, shell, ipcMain, dialog} = electron;
const {isOSX, linkIsInternal, getCssToInject} = helpers;

const ZOOM_INTERVAL = 0.1;

/**
 *
 * @param {{}} options AppArgs from nativefier.json
 * @param {function} onAppQuit
 * @param {function} setDockBadge
 * @returns {electron.BrowserWindow}
 */
function createMainWindow(options, onAppQuit, setDockBadge) {
    const mainWindowState = windowStateKeeper({
        defaultWidth: options.width || 1280,
        defaultHeight: options.height || 800
    });

    const mainWindow = new BrowserWindow({
        frame: !options.hideWindowFrame,
        width: mainWindowState.width,
        height: mainWindowState.height,
        x: mainWindowState.x,
        y: mainWindowState.y,
        autoHideMenuBar: !options.showMenuBar,
        // Convert dashes to spaces because on linux the app name is joined with dashes
        title: options.name,
        webPreferences: {
            javascript: true,
            plugins: true,
            // node globals causes problems with sites like messenger.com
            nodeIntegration: false,
            webSecurity: !options.insecure,
            preload: path.join(__dirname, 'static', 'preload.js')
        },
        // after webpack path here should reference `resources/app/`
        icon: path.join(__dirname, '../', '/icon.png'),
        // set to undefined and not false because explicitly setting to false will disable full screen
        fullscreen: options.fullScreen || undefined
    });

    mainWindowState.manage(mainWindow);

    // after first run, no longer force full screen to be true
    if (options.fullScreen) {
        options.fullScreen = undefined;
        fs.writeFileSync(path.join(__dirname, '..', 'nativefier.json'), JSON.stringify(options));
    }

    // after first run, no longer force maximize to be true
    if (options.maximize) {
        mainWindow.maximize();
        options.maximize = undefined;
        fs.writeFileSync(path.join(__dirname, '..', 'nativefier.json'), JSON.stringify(options));
    }

    let currentZoom = 1;

    const onZoomIn = () => {
        currentZoom += ZOOM_INTERVAL;
        mainWindow.webContents.send('change-zoom', currentZoom);
    };

    const onZoomOut = () => {
        currentZoom -= ZOOM_INTERVAL;
        mainWindow.webContents.send('change-zoom', currentZoom);
    };

    const clearAppData = () => {
        dialog.showMessageBox(mainWindow, {
            type: 'warning',
            buttons: ['Yes', 'Cancel'],
            defaultId: 1,
            title: 'Clear cache confirmation',
            message: 'This will clear all data (cookies, local storage etc) from this app. Are you sure you wish to proceed?'
        }, response => {
            if (response === 0) {
                const session = mainWindow.webContents.session;
                session.clearStorageData(() => {
                    session.clearCache(() => {
                        mainWindow.loadURL(options.targetUrl);
                    });
                });
            }
        });
    };

    const onGoBack = () => {
        mainWindow.webContents.goBack();
    };

    const onGoForward = () => {
        mainWindow.webContents.goForward();
    };

    const getCurrentUrl = () => {
        return mainWindow.webContents.getURL();
    };

    const menuOptions = {
        nativefierVersion: options.nativefierVersion,
        appQuit: onAppQuit,
        zoomIn: onZoomIn,
        zoomOut: onZoomOut,
        goBack: onGoBack,
        goForward: onGoForward,
        getCurrentUrl: getCurrentUrl,
        clearAppData: clearAppData
    };

    createMenu(menuOptions);
    if (!options.disableContextMenu) {
        initContextMenu(mainWindow);
    }

    if (options.userAgent) {
        mainWindow.webContents.setUserAgent(options.userAgent);
    }

    mainWindow.webContents.on('did-finish-load', () => {
        mainWindow.webContents.send('params', JSON.stringify(options));
        mainWindow.webContents.insertCSS(getCssToInject());
    });

    if (options.counter) {
        mainWindow.on('page-title-updated', () => {
            if (mainWindow.isFocused()) {
                return;
            }

            if (options.counter) {
                const itemCountRegex = /[\(\[{](\d*?)[}\]\)]/;
                const match = itemCountRegex.exec(mainWindow.getTitle());
                if (match) {
                    setDockBadge(match[1]);
                }
                return;
            }
            setDockBadge('●');
        });
    }

    mainWindow.webContents.on('new-window', (event, urlToGo) => {
        if (mainWindow.useDefaultWindowBehaviour) {
            mainWindow.useDefaultWindowBehaviour = false;
            return;
        }

        if (linkIsInternal(options.targetUrl, urlToGo)) {
            return;
        }
        event.preventDefault();
        shell.openExternal(urlToGo);
    });

    mainWindow.loadURL(options.targetUrl);

    mainWindow.on('focus', () => {
        setDockBadge('');
    });

    mainWindow.on('close', event => {
        if (mainWindow.isFullScreen()) {
            mainWindow.setFullScreen(false);
            mainWindow.once('leave-full-screen', maybeHideWindow.bind(this, mainWindow, event, options.fastQuit));
        }
        maybeHideWindow(mainWindow, event, options.fastQuit);
    });

    return mainWindow;
}

ipcMain.on('cancelNewWindowOverride', () => {
    const allWindows = BrowserWindow.getAllWindows();
    allWindows.forEach(window => {
        window.useDefaultWindowBehaviour = false;
    });
});

function maybeHideWindow(window, event, fastQuit) {
    if (isOSX() && !fastQuit) {
        // this is called when exiting from clicking the cross button on the window
        event.preventDefault();
        window.hide();
    }
    // will close the window on other platforms
}

export default createMainWindow;
<|endoftext|>


<commit_before>describe('Frontpage', function () {
  beforeEach(function() {
    // This instructs the webdriver to load the given page. Note that this is
    // xolvio/sync-webdriverio, hence we are inside a fiber here and do not
    // need to add callbacks (i.e., do not add then()).
    // http://www.webdriver.io/api/protocol/url.html
    browser.url(process.env.ROOT_URL);

    // Navigate to the page by by loading a route inside the browser.
    // http://www.webdriver.io/api/protocol/execute.html
    browser.execute(function() {
      // This code runs in the browser (phantomjs).
      Router.go('/');
    })

    // According to the testing docs, it should be possible to wait until all
    // subscriptions become ready at this point. However, for some reason that
    // does not work in this app. This can be easily verified by opening up the
    // browser and pasting the line `DDP._allSubscriptionsReady()` into the
    // console. At some point it should return `true`, but it never does (in my
    // case).
    // https://github.com/meteor/guide/blob/testing-modules-content/content/testing.md#full-app-integration-test
    //
    // browser.waitUntil(function() {
    //   var response = browser.execute(function() {
    //     return DDP._allSubscriptionsReady();
    //   });
    //   return response.value;
    // });

    // Instead we simply wait for critical elements to appear, and then each
    // test-case needs to wait again for those elements it wants to run
    // assertions against.
    // http://www.webdriver.io/api/utility/waitForExist.html
    browser.waitForExist('.courselist_course_container');
  });

  it('should list 6 courses for unauthenticated user (Testistan)', function () {
    // Wait until all courses have loaded.
    // http://www.webdriver.io/api/utility/waitUntil.html
    browser.waitUntil(function() {
      var divcount = browser.selectorExecute('.courselist_course_container', function(divs) {
        // This code runs in the browser (phantomjs).
        return divs.length;
      });
      return divcount == 6;
    });

    // Then collect data and assert stuff.
    var titles = browser.getText('.courselist_course_container h4');
    expect(titles[0]).to.equal('Deutschkurse');
    expect(titles[1]).to.equal('Elektronik');
    expect(titles[2]).to.equal('Schweissen');
    expect(titles[3]).to.equal('Aikido');
    expect(titles[4]).to.equal('Sprachaustausch');
    expect(titles[5]).to.equal('Web Design mit HTML und Phyton/PHP');
  });
});
<commit_msg>Comments inside webdriverio execute blocks apparently break in-browser code<commit_after>describe('Frontpage', function () {
  beforeEach(function() {
    // This instructs the webdriver to load the given page. Note that this is
    // xolvio/sync-webdriverio, hence we are inside a fiber here and do not
    // need to add callbacks (i.e., do not add then()).
    // http://www.webdriver.io/api/protocol/url.html
    browser.url(process.env.ROOT_URL);

    // Navigate to the page by loading a route inside the browser.
    // http://www.webdriver.io/api/protocol/execute.html
    browser.execute(function() {
      Router.go('/');
    })

    // According to the testing docs, it should be possible to wait until all
    // subscriptions become ready at this point. However, for some reason that
    // does not work in this app. This can be easily verified by opening up the
    // browser and pasting the line `DDP._allSubscriptionsReady()` into the
    // console. At some point it should return `true`, but it never does (in my
    // case).
    // https://github.com/meteor/guide/blob/testing-modules-content/content/testing.md#full-app-integration-test
    //
    // browser.waitUntil(function() {
    //   var response = browser.execute(function() {
    //     return DDP._allSubscriptionsReady();
    //   });
    //   return response.value;
    // });

    // Instead we simply wait for critical elements to appear, and then each
    // test-case needs to wait again for those elements it wants to run
    // assertions against.
    // http://www.webdriver.io/api/utility/waitForExist.html
    browser.waitForExist('.courselist_course_container');
  });

  it('should list 6 courses for unauthenticated user (Testistan)', function () {
    // Wait until all courses have loaded.
    // http://www.webdriver.io/api/utility/waitUntil.html
    browser.waitUntil(function() {
      var divcount = browser.selectorExecute('.courselist_course_container', function(divs) {
        return divs.length;
      });
      return divcount == 6;
    });

    // Then collect data and assert stuff.
    var titles = browser.getText('.courselist_course_container h4');
    expect(titles[0]).to.equal('Deutschkurse');
    expect(titles[1]).to.equal('Elektronik');
    expect(titles[2]).to.equal('Schweissen');
    expect(titles[3]).to.equal('Aikido');
    expect(titles[4]).to.equal('Sprachaustausch');
    expect(titles[5]).to.equal('Web Design mit HTML und Phyton/PHP');
  });
});
<|endoftext|>


<commit_before>import React, { Component } from 'react';

import PropTypes from 'prop-types';

import { withStyles } from '@material-ui/core/styles';
import Card from '@material-ui/core/Card';
import CardActions from '@material-ui/core/CardActions';
import CardContent from '@material-ui/core/CardContent';
import Typography from '@material-ui/core/Typography';
import List from '@material-ui/core/List';
import ListItem from '@material-ui/core/ListItem';
import ListItemText from '@material-ui/core/ListItemText';
import DialogTitle from '@material-ui/core/DialogTitle';
import Dialog from '@material-ui/core/Dialog';
import DialogActions from '@material-ui/core/DialogActions';
import DialogContent from '@material-ui/core/DialogContent';
import Button from '@material-ui/core/Button';
import withWidth from '@material-ui/core/withWidth';

import RequestButton from './RequestButton.js';
import FavoriteButton from './FavoriteButton.js';

const styles = theme => ({
    card: {
        alignItems: 'center',
    },
    details: {
        display: 'flex',
        flexDirection: 'column',
        alignItems: 'center',
    },
    content: {
        flex: '1 0 auto',
        alignItems: 'center',
    },
    thumbnail: {
      width: "5%"
    },
});

class Search extends Component {

    constructor(props){
        super(props);
        this.ws = props.ws
        this.props = props;
    }

    handleClose = () => {
        this.props.onClose(this.props.selectedValue);
    };

    handleListItemClick = value => {
        this.props.onClose(value);
    };

  htmlDecode = (input) => {
    var doc = new DOMParser().parseFromString(input, "text/html");
    return doc.documentElement.textContent;
  }

    detectSmallScreen = () => {
        return this.props.width === "sm" || this.props.width === "xs"
    }

    render(){
        const { classes, searchQuery, searchResults, onClose, selectedValue, ...other } = this.props;

        return (
            <Dialog onClose={this.handleOnClose} {...other} maxWidth="md" fullScreen={this.detectSmallScreen()}>
                <DialogTitle id="simple-dialog-title">Search results for: {searchQuery}</DialogTitle>
                <DialogContent>

                        <List>
                            {searchResults !== undefined && searchResults.map((el,idx)=>{
                                return (

                                        <ListItem spacing={5} key={el.id}>
                                            {String(idx+1) + ". "}
                                            { !this.detectSmallScreen() &&
                                                {"| "}
                                            }
                                            { !this.detectSmallScreen() &&

                                            }
                                            <ListItemText primary={this.htmlDecode(el.title)} />
                                            { !this.detectSmallScreen() &&
                                                <RequestButton song={el}/>
                                            }
                                            { !this.detectSmallScreen() &&
                                                <FavoriteButton song={el}/>
                                            }
                                        </ListItem>
                                        { this.detectSmallScreen() &&

                                                <RequestButton song={el}/>
                                                <FavoriteButton song={el}/>

                                        }

                                )
                            })}
                        </List>

                </DialogContent>
                 <DialogActions>
                     <Button onClick={this.handleClose} color="primary">
                         Close
                     </Button>
                </DialogActions>
            </Dialog>
        )
    }
}

Search.propTypes = {
  classes: PropTypes.object.isRequired,
  theme: PropTypes.object.isRequired,
};

export default withWidth()(withStyles(styles)(Search));
<commit_msg>fix search results<commit_after>import React, { Component } from 'react';

import PropTypes from 'prop-types';

import { withStyles } from '@material-ui/core/styles';
import Card from '@material-ui/core/Card';
import CardActions from '@material-ui/core/CardActions';
import CardContent from '@material-ui/core/CardContent';
import Typography from '@material-ui/core/Typography';
import List from '@material-ui/core/List';
import ListItem from '@material-ui/core/ListItem';
import ListItemText from '@material-ui/core/ListItemText';
import DialogTitle from '@material-ui/core/DialogTitle';
import Dialog from '@material-ui/core/Dialog';
import DialogActions from '@material-ui/core/DialogActions';
import DialogContent from '@material-ui/core/DialogContent';
import Button from '@material-ui/core/Button';
import withWidth from '@material-ui/core/withWidth';
import Divider from '@material-ui/core/Divider';

import RequestButton from './RequestButton.js';
import FavoriteButton from './FavoriteButton.js';

const styles = theme => ({
    card: {
        alignItems: 'center',
    },
    details: {
        display: 'flex',
        flexDirection: 'column',
        alignItems: 'center',
    },
    content: {
        flex: '1 0 auto',
        alignItems: 'center',
    },
    thumbnail: {
      width: "5%"
    },
});

class Search extends Component {

    constructor(props){
        super(props);
        this.ws = props.ws
        this.props = props;
    }

    handleClose = () => {
        this.props.onClose(this.props.selectedValue);
    };

    handleListItemClick = value => {
        this.props.onClose(value);
    };

  htmlDecode = (input) => {
    var doc = new DOMParser().parseFromString(input, "text/html");
    return doc.documentElement.textContent;
  }

    detectSmallScreen = () => {
        return this.props.width === "sm" || this.props.width === "xs"
    }

    render(){
        const { classes, searchQuery, searchResults, onClose, selectedValue, ...other } = this.props;

        return (
            <Dialog onClose={this.handleOnClose} {...other} maxWidth="md" fullScreen={this.detectSmallScreen()}>
                <DialogTitle id="simple-dialog-title">Search results for: {searchQuery}</DialogTitle>
                <DialogContent>

                        <List>
                            {searchResults !== undefined && searchResults.map((el,idx)=>{
                                return (

                                        <ListItem spacing={5} key={el.id}>
                                            {String(idx+1) + ". "}

                                            { !this.detectSmallScreen() &&

                                            }

                                            <ListItemText primary={this.htmlDecode(el.title)} />
                                            { !this.detectSmallScreen() &&
                                                <RequestButton song={el}/>
                                            }

                                            { !this.detectSmallScreen() &&
                                                <FavoriteButton song={el}/>
                                            }
                                        </ListItem>
                                        { this.detectSmallScreen() &&

                                                <RequestButton song={el}/>
                                                <FavoriteButton song={el}/>

                                        }
                                        { idx !== searchResults.length-1 && <Divider/>}

                                )
                            })}
                        </List>

                </DialogContent>
                 <DialogActions>
                     <Button onClick={this.handleClose} color="primary">
                         Close
                     </Button>
                </DialogActions>
            </Dialog>
        )
    }
}

Search.propTypes = {
  classes: PropTypes.object.isRequired,
  theme: PropTypes.object.isRequired,
};

export default withWidth()(withStyles(styles)(Search));
<|endoftext|>


<commit_before>let test = require('ava'),
    webdriver = require('selenium-webdriver'),
    By = webdriver.By,
    chalk = require('chalk')

// This gives us access to send Chrome commands.
require('chromedriver')

// Webdriver's promise manager only made sense before Node had async/await support.
// Now it's a deprecated legacy feature, so we should use the simpler native Node support instead.
webdriver.promise.USE_PROMISE_MANAGER = false

console.log(
    chalk.bold.cyan('These tests assume that you have just run ../insertSqlBaseData.sql on your SQLServer instance')
)

// We use the beforeEach hook to put helpers on t.context and set up test scaffolding.
test.beforeEach(t => {
    t.context.driver = new webdriver.Builder()
        .forBrowser('chrome')
        .build()

    // This method is a helper so we don't have to keep repeating the hostname.
    // Passing the authentication through the querystring is a hack so we can
    // pass the information along via window.fetch.
    t.context.get = async pathname =>
        await t.context.driver.get(`http://localhost:3000${pathname}?user=erin&pass=erin`)

    // For debugging purposes.
    t.context.waitForever = async () => {
        console.log(chalk.red('Waiting forever so you can debug...'))
        await t.context.driver.wait(() => {})
    }

    t.context.$ = cssSelector => t.context.driver.findElement(By.css(cssSelector))
    t.context.$$ = cssSelector => t.context.driver.findElements(By.css(cssSelector))

    // This helper method is necessary because we don't know when React has finished rendering the page.
    // We will wait for it to be done, with a max timeout so the test does not hang if the rendering fails.
    let fiveSecondsMs = 5000
    t.context.waitUntilElementHasText = async (elem, expectedText) =>
        await t.context.driver.wait(async () => {
            let text = await elem.getText()
            return text === expectedText
        }, fiveSecondsMs, `Element did not have text '${expectedText}' within ${fiveSecondsMs} milliseconds`)

    // A helper function to combine waiting for an element to have rendered and then asserting on its contents.
    t.context.assertElementText = async (t, elem, expectedText) => {
        try {
            await t.context.waitUntilElementHasText(elem, expectedText)
        } catch (e) {
            // If we got a TimeoutError because the element did not have the text we expected, just swallow it here
            // and let the assertion on blow up instead. That will produce a clearer error message.
            if (e.name !== 'TimeoutError') {
                throw e
            }
        }
        t.is(await elem.getText(), expectedText)
    }
})

// Shut down the browser when we are done.
test.afterEach.always(async t => {
    if (t.context.driver) {
        await t.context.driver.quit()
    }
})

test('Home Page', async t => {
    // We can use t.plan() to indicate how many assertions we plan to make.
    // This provides safety in case there's a silent failure and the test
    // looks like it exited successfully, when in fact it just died. I've
    // seen people get bit by that a done with frameworks like Mocha which
    // do not offer test planning.
    t.plan(5)

    let {assertElementText, $, $$} = t.context

    await t.context.get('/')

    // Use a CSS selector to find an element that we care about on the page.
    let [$reportsPending, $draftReports, $orgReports, $upcomingEngagements] = await $$('.home-tile h1')

    await assertElementText(t, $reportsPending, '0')
    await assertElementText(t, $draftReports, '0')
    await assertElementText(t, $orgReports, '2')
    await assertElementText(t, $upcomingEngagements, '0')

    await $('.persistent-tour-launcher').click()
    let $hopscotchTitle = await $('.hopscotch-title')
    await assertElementText(t, $hopscotchTitle, 'Welcome')
})
<commit_msg>adding a test for hopscotch<commit_after>let test = require('ava'),
    webdriver = require('selenium-webdriver'),
    By = webdriver.By,
    chalk = require('chalk')

// This gives us access to send Chrome commands.
require('chromedriver')

// Webdriver's promise manager only made sense before Node had async/await support.
// Now it's a deprecated legacy feature, so we should use the simpler native Node support instead.
webdriver.promise.USE_PROMISE_MANAGER = false

console.log(
    chalk.bold.cyan('These tests assume that you have just run ../insertSqlBaseData.sql on your SQLServer instance')
)

// We use the beforeEach hook to put helpers on t.context and set up test scaffolding.
test.beforeEach(t => {
    t.context.driver = new webdriver.Builder()
        .forBrowser('chrome')
        .build()

    // This method is a helper so we don't have to keep repeating the hostname.
    // Passing the authentication through the querystring is a hack so we can
    // pass the information along via window.fetch.
    t.context.get = async pathname =>
        await t.context.driver.get(`http://localhost:3000${pathname}?user=erin&pass=erin`)

    // For debugging purposes.
    t.context.waitForever = async () => {
        console.log(chalk.red('Waiting forever so you can debug...'))
        await t.context.driver.wait(() => {})
    }

    t.context.$ = cssSelector => t.context.driver.findElement(By.css(cssSelector))
    t.context.$$ = cssSelector => t.context.driver.findElements(By.css(cssSelector))

    // This helper method is necessary because we don't know when React has finished rendering the page.
    // We will wait for it to be done, with a max timeout so the test does not hang if the rendering fails.
    let fiveSecondsMs = 5000
    t.context.waitUntilElementHasText = async ($elem, expectedText) =>
        await t.context.driver.wait(async () => {
            let text = await $elem.getText()
            return text === expectedText
        }, fiveSecondsMs, `Element did not have text '${expectedText}' within ${fiveSecondsMs} milliseconds`)

    // A helper function to combine waiting for an element to have rendered and then asserting on its contents.
    t.context.assertElementText = async (t, $elem, expectedText, message) => {
        try {
            await t.context.waitUntilElementHasText($elem, expectedText)
        } catch (e) {
            // If we got a TimeoutError because the element did not have the text we expected, just swallow it here
            // and let the assertion on blow up instead. That will produce a clearer error message.
            if (e.name !== 'TimeoutError') {
                throw e
            }
        }
        t.is(await $elem.getText(), expectedText, message)
    }

    t.context.assertElementNotPresent = async (t, cssSelector, message) => {
        try {
            await t.context.driver.wait(
                async () => {
                    try {
                        return !(await t.context.$(cssSelector))
                    } catch (e) {
                        if (e.name === 'NoSuchElementError') {
                            return true
                        }
                        throw e
                    }
                },
                fiveSecondsMs,
                `Element was still present after ${fiveSecondsMs} milliseconds`
            )
        } catch (e) {
            if (e.name === 'TimeoutError') {
                t.fail(`Element with css selector '${cssSelector}' was still present after ${fiveSecondsMs} milliseconds`)
            } else {
                throw e
            }
        }
        t.pass(message || 'Element was not present')
    }
})

// Shut down the browser when we are done.
test.afterEach.always(async t => {
    if (t.context.driver) {
        await t.context.driver.quit()
    }
})

test('Home Page', async t => {
    // We can use t.plan() to indicate how many assertions we plan to make.
    // This provides safety in case there's a silent failure and the test
    // looks like it exited successfully, when in fact it just died. I've
    // seen people get bit by that a done with frameworks like Mocha which
    // do not offer test planning.
    t.plan(6)

    let {assertElementText, assertElementNotPresent, $, $$} = t.context

    await t.context.get('/')

    // Use a CSS selector to find an element that we care about on the page.
    let [$reportsPending, $draftReports, $orgReports, $upcomingEngagements] = await $$('.home-tile h1')

    await assertElementText(t, $reportsPending, '0')
    await assertElementText(t, $draftReports, '0')
    await assertElementText(t, $orgReports, '2')
    await assertElementText(t, $upcomingEngagements, '0')

    await $('.persistent-tour-launcher').click()
    let $hopscotchTitle = await $('.hopscotch-title')
    await assertElementText(
        t, $hopscotchTitle, 'Welcome', 'Clicking the hopscotch launch button starts the hopscotch tour'
    )

    await $('.hopscotch-next').click()

    await t.context.driver.findElement(By.linkText('My reports')).click()
    await assertElementNotPresent(t, '.hopscotch-title', 'Navigating to a new page clears the hopscotch tour')
})
<|endoftext|>


<commit_before>/** Texyed - Autogrow module
 *
 * based on Autogrow JQuery Plugin (http://opensourcejavaphp.net/phpapps/vanilla/js/library/jquery.autogrow.js.html)
 *
 * @author		Jan Pecha, <janpecha@email.cz>
 * @version		2012-07-26-1
 */

;(function($){
	$.extend($.fn, {
		teAutogrow: function() {
			if((this.hasClass('texyed-textarea')))
			{
				//this.data('texyed-autogrow-min', this.height());
				this.css('min-height', this.height() - parseInt(this.css('padding-top'), 10) - parseInt(this.css('padding-bottom'), 10));
				this.on('focus', function(e) {
					startExpand($(e.target));
				}).on('blur', function() {
					stopExpand();
				});
			}

			function startExpand(obj)
			{
//				obj.css('overflow-y', 'hidden');
				$.fn.teAutogrow_lineHeight = parseInt(obj.css('line-height'), 10);
				$.fn.teAutogrow_maxHeight = parseInt(obj.css('max-height'), 10);

				$.fn.teAutogrow_interval = window.setInterval(function() {
					autogrowUpdate(obj);
				}, 500);
			}

			function stopExpand()
			{
				window.clearInterval($.fn.teAutogrow_interval);
			}

			function autogrowUpdate(obj)
			{
//				stopExpand();
//				var o = obj.get(0);
				var currentChars = obj.val().length;

				if($.fn.teAutogrow_lastChars == undefined)
				{
					$.fn.teAutogrow_lastChars = currentChars;
				}

				if($.fn.teAutogrow_charDiff == undefined)
				{
					$.fn.teAutogrow_cumulativeCharDiff = 0;
				}

				var iterationCharDifference = currentChars - $.fn.teAutogrow_lastChars;
				$.fn.teAutogrow_lastChars = currentChars;
				$.fn.teAutogrow_cumulativeCharDiff += iterationCharDifference;

				var absoluteCharDiff = Math.abs($.fn.teAutogrow_charDiff);
				var heightDifference = Math.abs(obj.attr('scrollHeight') - obj.attr('clientHeight'));

				if(!heightDifference && absoluteCharDiff < 10)
				{
					return;
				}

				if ($.fn.teAutogrow_dummy == null)
				{
					$.fn.teAutogrow_dummy = $('

');
					$.fn.teAutogrow_dummy.css({
						'font-size': obj.css('font-size'),
						'font-family': obj.css('font-family'),
						'width': obj.css('width'),
						'padding-left': obj.css('padding-left'),
						'padding-right': obj.css('padding-right'),
						'padding-top': obj.css('padding-top'),
						'padding-bottom': obj.css('padding-bottom'),
						'line-height': $.fn.teAutogrow_lineHeight + 'px',
						'overflow-x': 'hidden',
						'position': 'absolute',
						'top': 0,
						'left': -9999
					}).appendTo('body');
				}

				// Strip HTML tags
				var html = obj.val().replace(/(<|>)/g, '');

				// IE is different, as per usual
				if($.browser.msie)
				{
					html = html.replace(/\n/g, '
new');
				}
				else
				{
					html = html.replace(/\n/g, '
new');
				}

				if($.fn.teAutogrow_dummy.html() != html)
				{
					$.fn.teAutogrow_dummy.html(html);

					if($.fn.teAutogrow_maxHeight > 0 && ($.fn.teAutogrow_dummy.height() + $.fn.teAutogrow_lineHeight > $.fn.teAutogrow_maxHeight))
					{
						obj.css('overflow-y', 'auto');
					}
					else
					{
						obj.css('overflow-y', 'hidden');
						if (obj.height() < $.fn.teAutogrow_dummy.height() + $.fn.teAutogrow_lineHeight || ($.fn.teAutogrow_dummy.height() < obj.height()))
						{
							obj.animate({
								height: ($.fn.teAutogrow_dummy.height() + $.fn.teAutogrow_lineHeight) + 'px'
							});
						}
						//obj.focus();
					}
				}

				$.fn.teAutogrow_cumulativeCharDiff = 0;

////				alert(obj.height());
////				alert(obj.attr('scrollHeight'));
//				var borderSize = obj.attr('offsetHeight') - obj.attr('clientHeight');
//				var currentHeight = obj.height() - borderSize;
////				alert(currentHeight);
////				alert(borderSize);
////				stopExpand();
//				if(obj.attr('scrollHeight') != currentHeight)
//				{
//					obj.animate({
//						height: currentHeight + 'px'
//					}, {
//						duration: 50,
//						easing: 'ease',
//						complete: function() {
//							obj.get(0).focus();
//						}
//					});
////					obj.get(0).blur();
//				}
			}

			return this;
		},

		teAutogrow_interval: null,
		teAutogrow_dummy: null,
		teAutogrow_lineHeight: null,
		teAutogrow_maxHeight: 0
	});

})(Zepto);

<commit_msg>[autogrow] cleanup code<commit_after>/** Texyed - Autogrow module
 *
 * based on Autogrow JQuery Plugin (http://opensourcejavaphp.net/phpapps/vanilla/js/library/jquery.autogrow.js.html)
 *
 * @author		Jan Pecha, <janpecha@email.cz>
 * @version		2012-07-26-1
 */

;(function($){
	$.extend($.fn, {
		teAutogrow: function() {
			if((this.hasClass('texyed-textarea')))
			{
				this.css('min-height', this.height() - parseInt(this.css('padding-top'), 10) - parseInt(this.css('padding-bottom'), 10));
				this.on('focus', function(e) {
					startExpand($(e.target));
				}).on('blur', function() {
					stopExpand();
				});
			}

			function startExpand(obj)
			{
				$.fn.teAutogrow_lineHeight = parseInt(obj.css('line-height'), 10);
				$.fn.teAutogrow_maxHeight = parseInt(obj.css('max-height'), 10);

				$.fn.teAutogrow_interval = window.setInterval(function() {
					autogrowUpdate(obj);
				}, 500);
			}

			function stopExpand()
			{
				window.clearInterval($.fn.teAutogrow_interval);
			}

			function autogrowUpdate(obj)
			{
				var currentChars = obj.val().length;

				if($.fn.teAutogrow_lastChars == undefined)
				{
					$.fn.teAutogrow_lastChars = currentChars;
				}

				if($.fn.teAutogrow_charDiff == undefined)
				{
					$.fn.teAutogrow_cumulativeCharDiff = 0;
				}

				var iterationCharDifference = currentChars - $.fn.teAutogrow_lastChars;
				$.fn.teAutogrow_lastChars = currentChars;
				$.fn.teAutogrow_cumulativeCharDiff += iterationCharDifference;

				var absoluteCharDiff = Math.abs($.fn.teAutogrow_charDiff);
				var heightDifference = Math.abs(obj.attr('scrollHeight') - obj.attr('clientHeight'));

				if(!heightDifference && absoluteCharDiff < 10)
				{
					return;
				}

				if ($.fn.teAutogrow_dummy == null)
				{
					$.fn.teAutogrow_dummy = $('

');
					$.fn.teAutogrow_dummy.css({
						'font-size': obj.css('font-size'),
						'font-family': obj.css('font-family'),
						'width': obj.css('width'),
						'padding-left': obj.css('padding-left'),
						'padding-right': obj.css('padding-right'),
						'padding-top': obj.css('padding-top'),
						'padding-bottom': obj.css('padding-bottom'),
						'line-height': $.fn.teAutogrow_lineHeight + 'px',
						'overflow-x': 'hidden',
						'position': 'absolute',
						'top': 0,
						'left': -9999
					}).appendTo('body');
				}

				// Strip HTML tags
				var html = obj.val().replace(/(<|>)/g, '');

				// IE is different, as per usual
				if($.browser.msie)
				{
					html = html.replace(/\n/g, '
new');
				}
				else
				{
					html = html.replace(/\n/g, '
new');
				}

				if($.fn.teAutogrow_dummy.html() != html)
				{
					$.fn.teAutogrow_dummy.html(html);

					if($.fn.teAutogrow_maxHeight > 0 && ($.fn.teAutogrow_dummy.height() + $.fn.teAutogrow_lineHeight > $.fn.teAutogrow_maxHeight))
					{
						obj.css('overflow-y', 'auto');
					}
					else
					{
						obj.css('overflow-y', 'hidden');
						if (obj.height() < $.fn.teAutogrow_dummy.height() + $.fn.teAutogrow_lineHeight || ($.fn.teAutogrow_dummy.height() < obj.height()))
						{
							obj.animate({
								height: ($.fn.teAutogrow_dummy.height() + $.fn.teAutogrow_lineHeight) + 'px'
							});
						}
						//obj.focus();
					}
				}

				$.fn.teAutogrow_cumulativeCharDiff = 0;
			}

			return this;
		},

		teAutogrow_interval: null,
		teAutogrow_dummy: null,
		teAutogrow_lineHeight: null,
		teAutogrow_maxHeight: 0
	});

})(Zepto);

<|endoftext|>


<commit_before>/**
    Licensed to the Apache Software Foundation (ASF) under one
    or more contributor license agreements.  See the NOTICE file
    distributed with this work for additional information
    regarding copyright ownership.  The ASF licenses this file
    to you 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.
*/
var fs            = require('fs'),
    path          = require('path'),
    et            = require('elementtree'),
    util          = require('../util'),
    events        = require('../events'),
    shell         = require('shelljs'),
    child_process = require('child_process'),
    Q             = require('q'),
    config_parser = require('../config_parser'),
    xml           = require('../xml-helpers'),
    config        = require('../config');

module.exports = function wp8_parser(project) {
    try {
        // TODO : Check that it's not a wp8 project?
        var csproj_file   = fs.readdirSync(project).filter(function(e) { return e.match(/\.csproj$/i); })[0];
        if (!csproj_file) throw new Error('No .csproj file.');
        this.wp8_proj_dir = project;
        this.csproj_path  = path.join(this.wp8_proj_dir, csproj_file);
        this.sln_path     = path.join(this.wp8_proj_dir, csproj_file.replace(/\.csproj/, '.sln'));
    } catch(e) {
        throw new Error('The provided path "' + project + '" is not a Windows Phone 8 project. ' + e);
    }
    this.manifest_path  = path.join(this.wp8_proj_dir, 'Properties', 'WMAppManifest.xml');
    this.config_path = path.join(this.wp8_proj_dir, 'config.xml');
    this.config = new util.config_parser(this.config_path);
};

// Returns a promise.
module.exports.check_requirements = function(project_root) {
    events.emit('log', 'Checking wp8 requirements...');
    var lib_path = util.getDefaultPlatformLibPath(project_root, 'wp8');
    var custom_path = config.has_custom_path(project_root, 'wp8');
    if (custom_path) lib_path = custom_path;
    var command = '"' + path.join(lib_path, 'bin', 'check_reqs') + '"';
    events.emit('verbose', 'Running "' + command + '" (output to follow)');
    var d = Q.defer();
    child_process.exec(command, function(err, output, stderr) {
        events.emit('verbose', output);
        if (err) {
            d.reject(new Error('Error while checking requirements: ' + output + stderr));
        } else {
            d.resolve();
        }
    });
    return d.promise;
};

module.exports.prototype = {
    update_from_config:function(config) {
        //check config parser
        if (config instanceof config_parser) {
        } else throw new Error('update_from_config requires a config_parser object');

        //Get manifest file
        var manifest = xml.parseElementtreeSync(this.manifest_path);

        //Update app version
        var version = config.version();
        manifest.find('.//App').attrib.Version = version;

        // Update app name by editing app title in Properties\WMAppManifest.xml
        var name = config.name();
        var prev_name = manifest.find('.//App[@Title]')['attrib']['Title'];
        if(prev_name != name) {
            //console.log("Updating app name from " + prev_name + " to " + name);
            manifest.find('.//App').attrib.Title = name;
            manifest.find('.//App').attrib.Publisher = name + " Publisher";
            manifest.find('.//App').attrib.Author = name + " Author";
            manifest.find('.//PrimaryToken').attrib.TokenID = name;
            //update name of sln and csproj.
            name = name.replace(/(\.\s|\s\.|\s+|\.+)/g, '_'); //make it a ligitamate name
            prev_name = prev_name.replace(/(\.\s|\s\.|\s+|\.+)/g, '_');
            // TODO: might return .sln.user? (generated file)
            var sln_name = fs.readdirSync(this.wp8_proj_dir).filter(function(e) { return e.match(/\.sln$/i); })[0];
            var sln_path = path.join(this.wp8_proj_dir, sln_name);
            var sln_file = fs.readFileSync(sln_path, 'utf-8');
            var name_regex = new RegExp(prev_name, "g");
            fs.writeFileSync(sln_path, sln_file.replace(name_regex, name), 'utf-8');
            shell.mv('-f', this.csproj_path, path.join(this.wp8_proj_dir, name + '.csproj'));
            this.csproj_path = path.join(this.wp8_proj_dir, name + '.csproj');
            shell.mv('-f', sln_path, path.join(this.wp8_proj_dir, name + '.sln'));
            this.sln_path    = path.join(this.wp8_proj_dir, name + '.sln');
        }

        // Update package name by changing:
        /*  - CordovaAppProj.csproj
         *  - MainPage.xaml
         *  - MainPage.xaml.cs
         *  - App.xaml
         *  - App.xaml.cs
         */
         var pkg = config.packageName();
         var csproj = xml.parseElementtreeSync(this.csproj_path);
         prev_name = csproj.find('.//RootNamespace').text;
         if(prev_name != pkg) {
            //console.log("Updating package name from " + prev_name + " to " + pkg);
            //CordovaAppProj.csproj
            csproj.find('.//RootNamespace').text = pkg;
            csproj.find('.//AssemblyName').text = pkg;
            csproj.find('.//XapFilename').text = pkg + '.xap';
            csproj.find('.//SilverlightAppEntry').text = pkg + '.App';
            fs.writeFileSync(this.csproj_path, csproj.write({indent: 4}), 'utf-8');
            //MainPage.xaml
            var mainPageXAML = xml.parseElementtreeSync(path.join(this.wp8_proj_dir, 'MainPage.xaml'));
            mainPageXAML.getroot().attrib['x:Class'] = pkg + '.MainPage';
            fs.writeFileSync(path.join(this.wp8_proj_dir, 'MainPage.xaml'), mainPageXAML.write({indent: 4}), 'utf-8');
            //MainPage.xaml.cs
            var mainPageCS = fs.readFileSync(path.join(this.wp8_proj_dir, 'MainPage.xaml.cs'), 'utf-8');
            var namespaceRegEx = new RegExp('namespace ' + prev_name);
            fs.writeFileSync(path.join(this.wp8_proj_dir, 'MainPage.xaml.cs'), mainPageCS.replace(namespaceRegEx, 'namespace ' + pkg), 'utf-8');
            //App.xaml
            var appXAML = xml.parseElementtreeSync(path.join(this.wp8_proj_dir, 'App.xaml'));
            appXAML.getroot().attrib['x:Class'] = pkg + '.App';
            fs.writeFileSync(path.join(this.wp8_proj_dir, 'App.xaml'), appXAML.write({indent: 4}), 'utf-8');
            //App.xaml.cs
            var appCS = fs.readFileSync(path.join(this.wp8_proj_dir, 'App.xaml.cs'), 'utf-8');
            fs.writeFileSync(path.join(this.wp8_proj_dir, 'App.xaml.cs'), appCS.replace(namespaceRegEx, 'namespace ' + pkg), 'utf-8');
         }

         // Update content (start page) element
         this.config.content(config.content());

         //Write out manifest
         fs.writeFileSync(this.manifest_path, manifest.write({indent: 4}), 'utf-8');
    },
    // Returns the platform-specific www directory.
    www_dir:function() {
        return path.join(this.wp8_proj_dir, 'xface3', 'helloxface');
    },
    config_xml:function() {
    },
    // copy files from merges directory to actual www dir
    copy_merges:function(merges_sub_path) {
        var merges_path = path.join(util.appDir(util.isCordova(this.wp8_proj_dir)), 'merges', merges_sub_path);
        if (fs.existsSync(merges_path)) {
            var overrides = path.join(merges_path, '*');
            shell.cp('-rf', overrides, this.www_dir());
        }
    },
    // copies the app www folder into the wp8 project's www folder and updates the csproj file.
    update_www:function() {
        var project_root = util.isxFace(this.wp8_proj_dir);
        var project_www = util.projectWww(project_root);
        var platformWww = path.resolve(path.join(this.www_dir(), '..'));
        // remove stock platform assets
        shell.rm('-rf', this.www_dir());
        shell.mkdir('-p', platformWww);
        // copy over all app www assets
        shell.cp('-rf', project_www + '/*', platformWww);

        // copy all files from merges directories (generic first, then specific)
        this.copy_merges('wp');
        this.copy_merges('wp8');

        // copy over wp8 lib's cordova.js
        var lib_path = util.getDefaultPlatformLibPath(project_root, 'wp8');
        var custom_path = config.has_custom_path(project_root, 'wp8');
        if (custom_path) lib_path = custom_path;
        var cordovajs_path = path.join(lib_path, 'xFaceLib', 'xFaceLib', 'xface.js');
        fs.writeFileSync(path.join(this.www_dir(), 'xface.js'), fs.readFileSync(cordovajs_path, 'utf-8'), 'utf-8');
    },
    staging_dir: function() {
        return path.join(this.wp8_proj_dir, '.staging', 'www');
    },

    update_staging: function() {
        var projectRoot = util.isxFace(this.wp8_proj_dir);
        if (fs.existsSync(this.staging_dir())) {
            var staging = path.join(this.staging_dir(), '*');
            shell.cp('-rf', staging, this.www_dir());
        }
    },

    // calls the nessesary functions to update the wp8 project
    // Returns a promise.
    update_project:function(cfg) {
        try {
            this.update_from_config(cfg);
        } catch(e) {
            return Q.reject(e);
        }
        // overrides (merges) are handled in update_www()
        this.update_www();
        this.update_staging();
        util.deleteSvnFolders(this.www_dir());
        return Q();
    }
};
<commit_msg>fixed merge error<commit_after>/**
    Licensed to the Apache Software Foundation (ASF) under one
    or more contributor license agreements.  See the NOTICE file
    distributed with this work for additional information
    regarding copyright ownership.  The ASF licenses this file
    to you 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.
*/
var fs            = require('fs'),
    path          = require('path'),
    et            = require('elementtree'),
    util          = require('../util'),
    events        = require('../events'),
    shell         = require('shelljs'),
    child_process = require('child_process'),
    Q             = require('q'),
    config_parser = require('../config_parser'),
    xml           = require('../xml-helpers'),
    config        = require('../config');

module.exports = function wp8_parser(project) {
    try {
        // TODO : Check that it's not a wp8 project?
        var csproj_file   = fs.readdirSync(project).filter(function(e) { return e.match(/\.csproj$/i); })[0];
        if (!csproj_file) throw new Error('No .csproj file.');
        this.wp8_proj_dir = project;
        this.csproj_path  = path.join(this.wp8_proj_dir, csproj_file);
        this.sln_path     = path.join(this.wp8_proj_dir, csproj_file.replace(/\.csproj/, '.sln'));
    } catch(e) {
        throw new Error('The provided path "' + project + '" is not a Windows Phone 8 project. ' + e);
    }
    this.manifest_path  = path.join(this.wp8_proj_dir, 'Properties', 'WMAppManifest.xml');
    this.config_path = path.join(this.wp8_proj_dir, 'config.xml');
    this.config = new util.config_parser(this.config_path);
};

// Returns a promise.
module.exports.check_requirements = function(project_root) {
    events.emit('log', 'Checking wp8 requirements...');
    var lib_path = util.getDefaultPlatformLibPath(project_root, 'wp8');
    var custom_path = config.has_custom_path(project_root, 'wp8');
    if (custom_path) lib_path = custom_path;
    var command = '"' + path.join(lib_path, 'bin', 'check_reqs') + '"';
    events.emit('verbose', 'Running "' + command + '" (output to follow)');
    var d = Q.defer();
    child_process.exec(command, function(err, output, stderr) {
        events.emit('verbose', output);
        if (err) {
            d.reject(new Error('Error while checking requirements: ' + output + stderr));
        } else {
            d.resolve();
        }
    });
    return d.promise;
};

module.exports.prototype = {
    update_from_config:function(config) {
        //check config parser
        if (config instanceof config_parser) {
        } else throw new Error('update_from_config requires a config_parser object');

        //Get manifest file
        var manifest = xml.parseElementtreeSync(this.manifest_path);

        //Update app version
        var version = config.version();
        manifest.find('.//App').attrib.Version = version;

        // Update app name by editing app title in Properties\WMAppManifest.xml
        var name = config.name();
        var prev_name = manifest.find('.//App[@Title]')['attrib']['Title'];
        if(prev_name != name) {
            //console.log("Updating app name from " + prev_name + " to " + name);
            manifest.find('.//App').attrib.Title = name;
            manifest.find('.//App').attrib.Publisher = name + " Publisher";
            manifest.find('.//App').attrib.Author = name + " Author";
            manifest.find('.//PrimaryToken').attrib.TokenID = name;
            //update name of sln and csproj.
            name = name.replace(/(\.\s|\s\.|\s+|\.+)/g, '_'); //make it a ligitamate name
            prev_name = prev_name.replace(/(\.\s|\s\.|\s+|\.+)/g, '_');
            // TODO: might return .sln.user? (generated file)
            var sln_name = fs.readdirSync(this.wp8_proj_dir).filter(function(e) { return e.match(/\.sln$/i); })[0];
            var sln_path = path.join(this.wp8_proj_dir, sln_name);
            var sln_file = fs.readFileSync(sln_path, 'utf-8');
            var name_regex = new RegExp(prev_name, "g");
            fs.writeFileSync(sln_path, sln_file.replace(name_regex, name), 'utf-8');
            shell.mv('-f', this.csproj_path, path.join(this.wp8_proj_dir, name + '.csproj'));
            this.csproj_path = path.join(this.wp8_proj_dir, name + '.csproj');
            shell.mv('-f', sln_path, path.join(this.wp8_proj_dir, name + '.sln'));
            this.sln_path    = path.join(this.wp8_proj_dir, name + '.sln');
        }

        // Update package name by changing:
        /*  - CordovaAppProj.csproj
         *  - MainPage.xaml
         *  - MainPage.xaml.cs
         *  - App.xaml
         *  - App.xaml.cs
         */
         var pkg = config.packageName();
         var csproj = xml.parseElementtreeSync(this.csproj_path);
         prev_name = csproj.find('.//RootNamespace').text;
         if(prev_name != pkg) {
            //console.log("Updating package name from " + prev_name + " to " + pkg);
            //CordovaAppProj.csproj
            csproj.find('.//RootNamespace').text = pkg;
            csproj.find('.//AssemblyName').text = pkg;
            csproj.find('.//XapFilename').text = pkg + '.xap';
            csproj.find('.//SilverlightAppEntry').text = pkg + '.App';
            fs.writeFileSync(this.csproj_path, csproj.write({indent: 4}), 'utf-8');
            //MainPage.xaml
            var mainPageXAML = xml.parseElementtreeSync(path.join(this.wp8_proj_dir, 'MainPage.xaml'));
            mainPageXAML.getroot().attrib['x:Class'] = pkg + '.MainPage';
            fs.writeFileSync(path.join(this.wp8_proj_dir, 'MainPage.xaml'), mainPageXAML.write({indent: 4}), 'utf-8');
            //MainPage.xaml.cs
            var mainPageCS = fs.readFileSync(path.join(this.wp8_proj_dir, 'MainPage.xaml.cs'), 'utf-8');
            var namespaceRegEx = new RegExp('namespace ' + prev_name);
            fs.writeFileSync(path.join(this.wp8_proj_dir, 'MainPage.xaml.cs'), mainPageCS.replace(namespaceRegEx, 'namespace ' + pkg), 'utf-8');
            //App.xaml
            var appXAML = xml.parseElementtreeSync(path.join(this.wp8_proj_dir, 'App.xaml'));
            appXAML.getroot().attrib['x:Class'] = pkg + '.App';
            fs.writeFileSync(path.join(this.wp8_proj_dir, 'App.xaml'), appXAML.write({indent: 4}), 'utf-8');
            //App.xaml.cs
            var appCS = fs.readFileSync(path.join(this.wp8_proj_dir, 'App.xaml.cs'), 'utf-8');
            fs.writeFileSync(path.join(this.wp8_proj_dir, 'App.xaml.cs'), appCS.replace(namespaceRegEx, 'namespace ' + pkg), 'utf-8');
         }

         // Update content (start page) element
         this.config.content(config.content());

         //Write out manifest
         fs.writeFileSync(this.manifest_path, manifest.write({indent: 4}), 'utf-8');
    },
    // Returns the platform-specific www directory.
    www_dir:function() {
        return path.join(this.wp8_proj_dir, 'xface3', 'helloxface');
    },
    config_xml:function() {
    },
    // copy files from merges directory to actual www dir
    copy_merges:function(merges_sub_path) {
        var merges_path = path.join(util.appDir(util.isxFace(this.wp8_proj_dir)), 'merges', merges_sub_path);
        if (fs.existsSync(merges_path)) {
            var overrides = path.join(merges_path, '*');
            shell.cp('-rf', overrides, this.www_dir());
        }
    },
    // copies the app www folder into the wp8 project's www folder and updates the csproj file.
    update_www:function() {
        var project_root = util.isxFace(this.wp8_proj_dir);
        var project_www = util.projectWww(project_root);
        var platformWww = path.resolve(path.join(this.www_dir(), '..'));
        // remove stock platform assets
        shell.rm('-rf', this.www_dir());
        shell.mkdir('-p', platformWww);
        // copy over all app www assets
        shell.cp('-rf', project_www + '/*', platformWww);

        // copy all files from merges directories (generic first, then specific)
        this.copy_merges('wp');
        this.copy_merges('wp8');

        // copy over wp8 lib's cordova.js
        var lib_path = util.getDefaultPlatformLibPath(project_root, 'wp8');
        var custom_path = config.has_custom_path(project_root, 'wp8');
        if (custom_path) lib_path = custom_path;
        var cordovajs_path = path.join(lib_path, 'xFaceLib', 'xFaceLib', 'xface.js');
        fs.writeFileSync(path.join(this.www_dir(), 'xface.js'), fs.readFileSync(cordovajs_path, 'utf-8'), 'utf-8');
    },
    staging_dir: function() {
        return path.join(this.wp8_proj_dir, '.staging', 'www');
    },

    update_staging: function() {
        var projectRoot = util.isxFace(this.wp8_proj_dir);
        if (fs.existsSync(this.staging_dir())) {
            var staging = path.join(this.staging_dir(), '*');
            shell.cp('-rf', staging, this.www_dir());
        }
    },

    // calls the nessesary functions to update the wp8 project
    // Returns a promise.
    update_project:function(cfg) {
        try {
            this.update_from_config(cfg);
        } catch(e) {
            return Q.reject(e);
        }
        // overrides (merges) are handled in update_www()
        this.update_www();
        this.update_staging();
        util.deleteSvnFolders(this.www_dir());
        return Q();
    }
};
<|endoftext|>


<commit_before>module.exports = (ctx) => ({
  plugins: [
    require('postcss-import')({
      root: ctx.file.dirname
    }),
    require('postcss-custom-media')({}),
    require('postcss-custom-selectors')({}),
//    require('postcss-discard-comments')({}),
    require("stylelint")({
      "rules": {
          "color-no-invalid-hex": true
        , "comment-no-empty": true
        , "number-max-precision": 4
        , "unit-blacklist": ["pt"]
        , "custom-property-pattern": "(RS|USER)__.+"
        , "selector-max-empty-lines": 0
        , "color-hex-length": "long"
        , "font-family-name-quotes": "always-where-recommended"
        , "function-url-quotes": "always"
        , "number-leading-zero": "always"
        , "number-no-trailing-zeros": true
        , "length-zero-no-unit": true
        , "unit-case": "lower"
        , "selector-attribute-quotes": "always"
        , "comment-whitespace-inside": "always"
        , "max-empty-lines": 1
        , "indentation": 2
        , "no-duplicate-at-import-rules": true
        , "no-extra-semicolons": true
        , "no-invalid-double-slash-comments": true
        , "max-nesting-depth": 0
        , "string-quotes": "double"
        , "declaration-bang-space-before": "always"
        , "block-closing-brace-newline-after": "always"
        , "block-opening-brace-newline-after": "always"
      }
    }),
    require('postcss-sorting')({
      'properties-order': [
          'object-fit'
        , 'position'
        , 'top'
        , 'right'
        , 'bottom'
        , 'left'
        , 'z-index'
        , 'display'
        , 'float'
        , 'columns'
        , 'column-width'
        , 'column-count'
        , 'column-gap'
        , 'column-fill'
        , 'width'
        , 'height'
        , 'max-width'
        , 'max-height'
        , 'min-width'
        , 'min-height'
        , 'padding'
        , 'padding-top'
        , 'padding-right'
        , 'padding-bottom'
        , 'padding-left'
        , 'margin'
        , 'margin-top'
        , 'margin-right'
        , 'margin-bottom'
        , 'margin-left'
        , 'margin-collapse'
        , 'margin-top-collapse'
        , 'margin-right-collapse'
        , 'margin-bottom-collapse'
        , 'margin-left-collapse'
        , 'overflow'
        , 'overflow-x'
        , 'overflow-y'
        , 'clip'
        , 'clear'
        , 'font'
        , 'font-family'
        , 'font-size'
        , 'font-smoothing'
        , 'font-style'
        , 'font-weight'
        , 'hyphens'
        , 'widows'
        , 'orphans'
        , 'src'
        , 'line-height'
        , 'letter-spacing'
        , 'word-spacing'
        , 'color'
        , 'text-align'
        , 'text-align-last'
        , 'text-decoration'
        , 'text-indent'
        , 'text-overflow'
        , 'text-rendering'
        , 'text-size-adjust'
        , 'text-shadow'
        , 'text-transform'
        , 'word-break'
        , 'word-wrap'
        , 'white-space'
        , 'vertical-align'
        , 'list-style'
        , 'list-style-type'
        , 'list-style-position'
        , 'list-style-image'
        , 'pointer-events'
        , 'cursor'
        , 'background'
        , 'background-attachment'
        , 'background-color'
        , 'background-image'
        , 'background-position'
        , 'background-repeat'
        , 'background-size'
        , 'border'
        , 'border-collapse'
        , 'border-top'
        , 'border-right'
        , 'border-bottom'
        , 'border-left'
        , 'border-color'
        , 'border-image'
        , 'border-top-color'
        , 'border-right-color'
        , 'border-bottom-color'
        , 'border-left-color'
        , 'border-spacing'
        , 'border-style'
        , 'border-top-style'
        , 'border-right-style'
        , 'border-bottom-style'
        , 'border-left-style'
        , 'border-width'
        , 'border-top-width'
        , 'border-right-width'
        , 'border-bottom-width'
        , 'border-left-width'
        , 'border-radius'
        , 'border-top-right-radius'
        , 'border-bottom-right-radius'
        , 'border-bottom-left-radius'
        , 'border-top-left-radius'
        , 'border-radius-topright'
        , 'border-radius-bottomright'
        , 'border-radius-bottomleft'
        , 'border-radius-topleft'
        , 'content'
        , 'quotes'
        , 'outline'
        , 'outline-offset'
        , 'opacity'
        , 'filter'
        , 'visibility'
        , 'size'
        , 'zoom'
        , 'transform'
        , 'box-align'
        , 'box-flex'
        , 'box-orient'
        , 'box-pack'
        , 'box-shadow'
        , 'box-sizing'
        , 'table-layout'
        , 'animation'
        , 'animation-delay'
        , 'animation-duration'
        , 'animation-iteration-count'
        , 'animation-name'
        , 'animation-play-state'
        , 'animation-timing-function'
        , 'animation-fill-mode'
        , 'transition'
        , 'transition-delay'
        , 'transition-duration'
        , 'transition-property'
        , 'transition-timing-function'
        , 'background-clip'
        , 'backface-visibility'
        , 'resize'
        , 'appearance'
        , 'user-select'
        , 'interpolation-mode'
        , 'direction'
        , 'marks'
        , 'page'
        , 'column-break-after'
        , 'page-break-after'
        , 'break-after'
        , 'column-break-before'
        , 'page-break-before'
        , 'break-before'
        , 'column-break-inside'
        , 'page-break-inside'
        , 'break-inside'
        , 'unicode-bidi'
        , 'speak'
      ],
      'unspecified-properties-position': 'bottomAlphabetical'
    })
  ]
})<commit_msg>Improve stylelint messages and severity handling<commit_after>module.exports = (ctx) => ({
  plugins: [
    require('postcss-import')({
      root: ctx.file.dirname
    }),
    require('postcss-custom-media')({}),
    require('postcss-custom-selectors')({}),
//    require('postcss-discard-comments')({}),
    require("stylelint")({
      "defaultSeverity": "warning",
      "rules": {
          "color-no-invalid-hex": true
        , "comment-no-empty": [ true, {
            "message": "Please remove empty comments."
        }]
        , "number-max-precision": 4
        , "unit-blacklist": [ ["pt"], {
            "message": "Sorry, this unit is not allowed. Please use another one."
          , "severity": "error"
        }]
        , "custom-property-pattern": [ "(RS|USER)__.+", {
            "message": "It looks like you’re using a CSS variable prefix which is not supported. It should either start with “--RS__” or “--USER__”."
          , "severity": "error"
        }]
        , "selector-max-empty-lines": 0
        , "color-hex-length": [ "long", {
            "message": "We recommend using long color HEX to prevent unexpected issues."
        }]
        , "font-family-name-quotes": [ "always-where-recommended", {
            "message": "If looks like there are spaces or digits in your “font-family”, please use quotes."
        }]
        , "function-url-quotes": "always"
        , "number-leading-zero": "always"
        , "number-no-trailing-zeros": true
        , "length-zero-no-unit": [ true, {
            "message": "The value of this property being 0, you don’t need an unit. Please remove it."
        }]
        , "unit-case": "lower"
        , "selector-attribute-quotes": "always"
        , "comment-whitespace-inside": "always"
        , "max-empty-lines": 1
        , "indentation": [ 2, {
            "message": "You should use 2 spaces to indent."
        }]
        , "no-duplicate-at-import-rules": true
        , "no-extra-semicolons": true
        , "no-invalid-double-slash-comments": [ true, {
            "message": "It looks like you’re using single-line JS comments. This is CSS, you can’t use that."
          , "severity": "error"
        }]
        , "max-nesting-depth": [ 0, {
            "message": "We’re using Vanilla CSS with PostCSS and our current configuration doesn’t allow nesting selectors as in LESS or SASS."
          , "severity": "error"
        }]
        , "string-quotes": "double"
        , "declaration-bang-space-before": [ "always", {
            "message": "Please put a space before !important as it will improve readability."
        }]
        , "block-closing-brace-newline-after": "always"
        , "block-opening-brace-newline-after": "always"
      }
    }),
    require('postcss-sorting')({
      'properties-order': [
          'object-fit'
        , 'position'
        , 'top'
        , 'right'
        , 'bottom'
        , 'left'
        , 'z-index'
        , 'display'
        , 'float'
        , 'columns'
        , 'column-width'
        , 'column-count'
        , 'column-gap'
        , 'column-fill'
        , 'width'
        , 'height'
        , 'max-width'
        , 'max-height'
        , 'min-width'
        , 'min-height'
        , 'padding'
        , 'padding-top'
        , 'padding-right'
        , 'padding-bottom'
        , 'padding-left'
        , 'margin'
        , 'margin-top'
        , 'margin-right'
        , 'margin-bottom'
        , 'margin-left'
        , 'margin-collapse'
        , 'margin-top-collapse'
        , 'margin-right-collapse'
        , 'margin-bottom-collapse'
        , 'margin-left-collapse'
        , 'overflow'
        , 'overflow-x'
        , 'overflow-y'
        , 'clip'
        , 'clear'
        , 'font'
        , 'font-family'
        , 'font-size'
        , 'font-smoothing'
        , 'font-style'
        , 'font-weight'
        , 'hyphens'
        , 'widows'
        , 'orphans'
        , 'src'
        , 'line-height'
        , 'letter-spacing'
        , 'word-spacing'
        , 'color'
        , 'text-align'
        , 'text-align-last'
        , 'text-decoration'
        , 'text-indent'
        , 'text-overflow'
        , 'text-rendering'
        , 'text-size-adjust'
        , 'text-shadow'
        , 'text-transform'
        , 'word-break'
        , 'word-wrap'
        , 'white-space'
        , 'vertical-align'
        , 'list-style'
        , 'list-style-type'
        , 'list-style-position'
        , 'list-style-image'
        , 'pointer-events'
        , 'cursor'
        , 'background'
        , 'background-attachment'
        , 'background-color'
        , 'background-image'
        , 'background-position'
        , 'background-repeat'
        , 'background-size'
        , 'border'
        , 'border-collapse'
        , 'border-top'
        , 'border-right'
        , 'border-bottom'
        , 'border-left'
        , 'border-color'
        , 'border-image'
        , 'border-top-color'
        , 'border-right-color'
        , 'border-bottom-color'
        , 'border-left-color'
        , 'border-spacing'
        , 'border-style'
        , 'border-top-style'
        , 'border-right-style'
        , 'border-bottom-style'
        , 'border-left-style'
        , 'border-width'
        , 'border-top-width'
        , 'border-right-width'
        , 'border-bottom-width'
        , 'border-left-width'
        , 'border-radius'
        , 'border-top-right-radius'
        , 'border-bottom-right-radius'
        , 'border-bottom-left-radius'
        , 'border-top-left-radius'
        , 'border-radius-topright'
        , 'border-radius-bottomright'
        , 'border-radius-bottomleft'
        , 'border-radius-topleft'
        , 'content'
        , 'quotes'
        , 'outline'
        , 'outline-offset'
        , 'opacity'
        , 'filter'
        , 'visibility'
        , 'size'
        , 'zoom'
        , 'transform'
        , 'box-align'
        , 'box-flex'
        , 'box-orient'
        , 'box-pack'
        , 'box-shadow'
        , 'box-sizing'
        , 'table-layout'
        , 'animation'
        , 'animation-delay'
        , 'animation-duration'
        , 'animation-iteration-count'
        , 'animation-name'
        , 'animation-play-state'
        , 'animation-timing-function'
        , 'animation-fill-mode'
        , 'transition'
        , 'transition-delay'
        , 'transition-duration'
        , 'transition-property'
        , 'transition-timing-function'
        , 'background-clip'
        , 'backface-visibility'
        , 'resize'
        , 'appearance'
        , 'user-select'
        , 'interpolation-mode'
        , 'direction'
        , 'marks'
        , 'page'
        , 'column-break-after'
        , 'page-break-after'
        , 'break-after'
        , 'column-break-before'
        , 'page-break-before'
        , 'break-before'
        , 'column-break-inside'
        , 'page-break-inside'
        , 'break-inside'
        , 'unicode-bidi'
        , 'speak'
      ],
      'unspecified-properties-position': 'bottomAlphabetical'
    })
  ]
})<|endoftext|>


<commit_before>$(function() {
    var enabledProtocols = {
        'xhr-streaming' : true, 'xhr-polling' : true
    };

    var protocolCheck = function(p) {
        return $("#enable-" + p);
    }

    var i, p;
    for(i in protocols) {
        (function(p) {
            var checkbox = protocolCheck(p);
            if(p in enabledProtocols) {
                checkbox.attr('checked', true);
            }
            checkbox.click(function() {
                if ($(this).is(':checked')) {
                    enabledProtocols[p] = true;
                } else {
                    delete enabledProtocols[p];
                }
            });
        })(protocols[i]);
    }

    var output = function(msg) {
        $("#output").append('
' + msg + '
')
    };

    var clear = function() {
        $('#output').html('');
    }

    var tryAll = function() {
        var i, p, whitelist;
        var sock;
        for(i in protocols) {
            p = protocols[i];
            (function(i, p) {
                var whitelist = [p];
                var success = undefined;
                var text = 'Some text';
                var textReceived = false;
                var sock = new SockJS('/echo', null, {
                    protocols_whitelist: whitelist
                });
                sock.onopen = function() {
                    success = true;
                    sock.send(text);
                };
                sock.onmessage = function(e) {
                    if(e.data === text) {
                        textReceived = true;
                        sock.close()
                    }
                };
                sock.onclose = function(e) {
                    var result = $("#result-" + p + " span");
                    if(success === undefined) {
                        success = false;
                    }

                    result.removeClass("indeterminate");
                    if(success && textReceived) {
                        result.text("succeeded").addClass("success");
                    } else if(success) {
                        result.text("opened, wrong text").addClass("failure");
                    } else {
                        result.text("failed").addClass("failure");
                    }
                };
            })(i, protocols[i]);
        }
    };
    tryAll();

    var sock;
    var newSock = function() {
        var whitelist = [];
        var i, p;
        for (i in protocols) {
            p = protocols[i];
            if(enabledProtocols[p]) {
                whitelist.push(p);
            }
        }
        sock = new SockJS('/echo', null, {
            protocols_whitelist: whitelist
        });
        sock.onopen = function() {
            output("Opened socket with protocol " + sock.protocol);
            sock.send("Ohai!");
            sock.send("Second send");
        };
        sock.onclose = function(e) {
            output("Byebye! " +  e.code + ', ' + e.reason)
        }
        sock.onmessage = function(e) {
            output(e.data);
        };

    };
    $('#send').click(function() {
        if(sock) {
            sock.send("Boo!");
        }
    });

    $('#close').click(function() {
        if(sock) {
            sock.close();
            sock = null;
        }
    });
    $('#restart').click(function() {
        if(sock) {
            sock.close();
        }
        newSock();
    });
    $('#clear').click(clear);

});
<commit_msg>jslint cleanup<commit_after>/*global $, protocols, SockJS*/

$(function() {
    var enabledProtocols = {
        'xhr-streaming' : true, 'xhr-polling' : true
    };

    var protocolCheck = function(p) {
        return $("#enable-" + p);
    };

    var i;
    for(i in protocols) {
        (function(p) {
            var checkbox = protocolCheck(p);
            if(enabledProtocols[p]) {
                checkbox.attr('checked', true);
            }
            checkbox.click(function() {
                if ($(this).is(':checked')) {
                    enabledProtocols[p] = true;
                } else {
                    delete enabledProtocols[p];
                }
            });
        })(protocols[i]);
    }

    var output = function(msg) {
        $("#output").append('
' + msg + '
');
    };

    var clear = function() {
        $('#output').html('');
    };

    var tryAll = function() {
        var i, p;
        for(i in protocols) {
            p = protocols[i];
            (function(i, p) {
                var whitelist = [p];
                var success = undefined;
                var text = 'Some text';
                var textReceived = false;
                var sock = new SockJS('/echo', null, {
                    protocols_whitelist: whitelist
                });
                sock.onopen = function() {
                    success = true;
                    sock.send(text);
                };
                sock.onmessage = function(e) {
                    if(e.data === text) {
                        textReceived = true;
                        sock.close();
                    }
                };
                sock.onclose = function(e) {
                    var result = $("#result-" + p + " span");
                    if(success === undefined) {
                        success = false;
                    }

                    result.removeClass("indeterminate");
                    if(success && textReceived) {
                        result.text("succeeded").addClass("success");
                    } else if(success) {
                        result.text("opened, wrong text").addClass("failure");
                    } else {
                        result.text("failed").addClass("failure");
                    }
                };
            })(i, protocols[i]);
        }
    };
    tryAll();

    var sock;
    var newSock = function() {
        var whitelist = [];
        var i, p;
        for (i in protocols) {
            p = protocols[i];
            if(enabledProtocols[p]) {
                whitelist.push(p);
            }
        }
        sock = new SockJS('/echo', null, {
            protocols_whitelist: whitelist
        });
        sock.onopen = function() {
            output("Opened socket with protocol " + sock.protocol);
            sock.send("Ohai!");
            sock.send("Second send");
        };
        sock.onclose = function(e) {
            output("Byebye! " +  e.code + ', ' + e.reason);
        };
        sock.onmessage = function(e) {
            output(e.data);
        };

    };
    $('#send').click(function() {
        if(sock) {
            sock.send("Boo!");
        }
    });

    $('#close').click(function() {
        if(sock) {
            sock.close();
            sock = null;
        }
    });
    $('#restart').click(function() {
        if(sock) {
            sock.close();
        }
        newSock();
    });
    $('#clear').click(clear);

});
<|endoftext|>