<commit_before>import React, { Component, PropTypes } from 'react'
import Helmet from 'react-helmet'
import classNames from 'classnames'
import { connect } from 'react-redux'
import { debounce } from 'lodash'
import { autoCompleteUsers, loadEmojis } from '../actions/posts'
import { loadProfile } from '../actions/profile'
import { trackPageView } from '../actions/tracking'
import * as ACTION_TYPES from '../constants/action_types'
import { GUI } from '../constants/gui_types'
import Analytics from '../components/analytics/Analytics'
import DevTools from '../components/devtools/DevTools'
import Footer from '../components/footer/Footer'
import Modal from '../components/modals/Modal'
import Navbar from '../components/navbar/Navbar'
import Omnibar from '../components/omnibar/Omnibar'
import Completer from '../components/completers/Completer'
import TextTools from '../components/editor/TextTools'
import { addInputObject, removeInputObject } from '../components/editor/InputComponent'
import { replaceWordFromSelection } from '../components/editor/SelectionUtil'
import { triggerResize } from '../components/interface/ResizeComponent'

class App extends Component {

  static propTypes = {
    authentication: PropTypes.object.isRequired,
    children: PropTypes.node.isRequired,
    completions: PropTypes.shape({
      data: PropTypes.array,
      type: PropTypes.string,
    }),
    dispatch: PropTypes.func.isRequired,
    editorStore: PropTypes.object.isRequired,
    emoji: PropTypes.object.isRequired,
    pathname: PropTypes.string.isRequired,
  };

  static defaultProps = {
    editorStore: {},
    lastLocation: '',
  };

  componentWillMount() {
    this.state = {
      activeTools: {},
      coordinates: { top: -200, left: -666 },
      hideCompleter: true,
      hideTextTools: true,
    }
    const loggedOutPaths = {
      explore: /^\/explore/,
      explore_recent: /^\/explore\/recent/,
      explore_trending: /^\/explore\/trending/,
      find: /^\/find$/,
      forgot_password: /^\/forgot-password/,
      signup: /^\/signup/,
    }
    this.lastLocation = ''
    // need to clear out the authentication for the case of
    // when you are on ello.co and go to /onboarding (logging in)
    // then logging out of the mothership wouldn't clear out the
    // authentication here and would show you the wrong navbar
    // and the links would be wrong for user/post detail pages
    const { dispatch, pathname } = this.props
    let isLoggedOutPath = false
    for (const re in loggedOutPaths) {
      if (pathname.match(loggedOutPaths[re])) {
        isLoggedOutPath = true
        break
      }
    }
    if (isLoggedOutPath) {
      dispatch({ type: ACTION_TYPES.AUTHENTICATION.LOGOUT })
    }
    this.onUserCompleter = debounce(this.onUserCompleter, 300)
  }

  componentDidMount() {
    if (!GUI.innerWidth) { triggerResize() }
    addInputObject(this)
  }

  componentWillReceiveProps(nextProps) {
    const prevAuthentication = this.props.authentication
    const { authentication, dispatch, location } = nextProps
    if (authentication &&
        !prevAuthentication.isLoggedIn &&
        authentication.isLoggedIn &&
        location.pathname !== '/') {
      dispatch(loadProfile())
    }
  }

  componentDidUpdate() {
    const { pathname, dispatch } = this.props
    if (pathname !== this.lastLocation) {
      this.lastLocation = pathname
      dispatch(trackPageView())
    }
  }

  componentWillUnmount() {
    removeInputObject(this)
  }

  onHideCompleter() {
    const { completions, dispatch } = this.props
    this.setState({ hideCompleter: true })
    if (completions) {
      dispatch({ type: ACTION_TYPES.POST.AUTO_COMPLETE_CLEAR })
    }
  }

  onPositionChange(props) {
    this.setState(props)
  }

  onShowTextTools({ activeTools }) {
    this.setState({ hideTextTools: false, activeTools })
  }

  onHideTextTools() {
    this.setState({ hideTextTools: true })
  }

  onUserCompleter({ word }) {
    const { dispatch } = this.props
    dispatch(autoCompleteUsers('user', word))
    this.setState({ hideCompleter: false })
  }

  onEmojiCompleter({ word }) {
    const { dispatch, emoji } = this.props
    if (emoji.emojis && emoji.emojis.length) {
      dispatch({
        type: ACTION_TYPES.EMOJI.LOAD_SUCCESS,
        payload: {
          response: {
            emojis: emoji.emojis,
          },
          type: 'emoji',
          word,
        },
      })
    } else {
      dispatch(loadEmojis('emoji', word))
    }
    this.setState({ hideCompleter: false })
  }

  handleCompletion = ({ value }) => {
    replaceWordFromSelection(value)
    this.handleCancelAutoCompleter()
  };

  handleCancelAutoCompleter = () => {
    this.onHideCompleter()
    this.onHideTextTools()
    // TODO: maybe clear out the completions from the editor store
  };

  render() {
    const { authentication, children, completions, pathname } = this.props
    const { activeTools, coordinates, hideCompleter, hideTextTools } = this.state
    const { isLoggedIn } = authentication
    const appClasses = classNames(
      'App',
      { isLoggedIn },
      { isLoggedOut: !isLoggedIn },
    )
    return (

        <Helmet
          title="Be inspired."
          titleTemplate="Ello | %s"
          meta={[
            {
              name: 'apple-itunes-app',
              content: 'app-id=953614327',
              'app-argument': pathname,
            },
          ]}
        />
        { isLoggedIn ? <Omnibar/> : null }

          {children}

        { !hideCompleter && completions ?
          <Completer
            completions={ completions }
            onCancel={ this.handleCancelAutoCompleter }
            onCompletion={ this.handleCompletion }
          /> :
          null
        }
        { !hideTextTools ?
          <TextTools
            activeTools={ activeTools }
            isHidden={ hideTextTools }
            coordinates={ coordinates }
            key={ JSON.stringify(activeTools) }
          /> :
          null
        }
        <Navbar/>

        <Modal/>
        <DevTools/>
        <Analytics isLoggedIn={isLoggedIn}/>

    )
  }
}

App.preRender = (store) => {
  const state = store.getState()
  if (state.authentication && state.authentication.isLoggedIn) {
    return store.dispatch(loadProfile())
  }
}

function mapStateToProps(state) {
  return {
    authentication: state.authentication,
    completions: state.editor.completions,
    emoji: state.emoji,
    pathname: state.routing.location.pathname,
  }
}

export default connect(mapStateToProps)(App)

<commit_msg>Start off by just logging things<commit_after>import React, { Component, PropTypes } from 'react'
import Helmet from 'react-helmet'
import classNames from 'classnames'
import { connect } from 'react-redux'
import { debounce } from 'lodash'
import { autoCompleteUsers, loadEmojis } from '../actions/posts'
import { loadProfile } from '../actions/profile'
import { trackPageView } from '../actions/tracking'
import * as ACTION_TYPES from '../constants/action_types'
import { GUI } from '../constants/gui_types'
import Analytics from '../components/analytics/Analytics'
import DevTools from '../components/devtools/DevTools'
import Footer from '../components/footer/Footer'
import Modal from '../components/modals/Modal'
import Navbar from '../components/navbar/Navbar'
import Omnibar from '../components/omnibar/Omnibar'
import Completer from '../components/completers/Completer'
import TextTools from '../components/editor/TextTools'
import { addInputObject, removeInputObject } from '../components/editor/InputComponent'
import { replaceWordFromSelection } from '../components/editor/SelectionUtil'
import { triggerResize } from '../components/interface/ResizeComponent'

class App extends Component {

  static propTypes = {
    authentication: PropTypes.object.isRequired,
    children: PropTypes.node.isRequired,
    completions: PropTypes.shape({
      data: PropTypes.array,
      type: PropTypes.string,
    }),
    dispatch: PropTypes.func.isRequired,
    editorStore: PropTypes.object.isRequired,
    emoji: PropTypes.object.isRequired,
    pathname: PropTypes.string.isRequired,
  };

  static defaultProps = {
    editorStore: {},
    lastLocation: '',
  };

  componentWillMount() {
    this.state = {
      activeTools: {},
      coordinates: { top: -200, left: -666 },
      hideCompleter: true,
      hideTextTools: true,
    }
    const loggedOutPaths = {
      explore: /^\/explore/,
      explore_recent: /^\/explore\/recent/,
      explore_trending: /^\/explore\/trending/,
      find: /^\/find$/,
      forgot_password: /^\/forgot-password/,
      signup: /^\/signup/,
    }
    this.lastLocation = ''
    // need to clear out the authentication for the case of
    // when you are on ello.co and go to /onboarding (logging in)
    // then logging out of the mothership wouldn't clear out the
    // authentication here and would show you the wrong navbar
    // and the links would be wrong for user/post detail pages
    const { pathname } = this.props
    let isLoggedOutPath = false
    for (const re in loggedOutPaths) {
      if (pathname.match(loggedOutPaths[re])) {
        isLoggedOutPath = true
        break
      }
    }
    if (isLoggedOutPath) {
      /* eslint-disable no-console */
      console.log('logged out page')
      /* eslint-enable no-console */
    }
    this.onUserCompleter = debounce(this.onUserCompleter, 300)
  }

  componentDidMount() {
    if (!GUI.innerWidth) { triggerResize() }
    addInputObject(this)
  }

  componentWillReceiveProps(nextProps) {
    const prevAuthentication = this.props.authentication
    const { authentication, dispatch, location } = nextProps
    if (authentication &&
        !prevAuthentication.isLoggedIn &&
        authentication.isLoggedIn &&
        location.pathname !== '/') {
      dispatch(loadProfile())
    }
  }

  componentDidUpdate() {
    const { pathname, dispatch } = this.props
    if (pathname !== this.lastLocation) {
      this.lastLocation = pathname
      dispatch(trackPageView())
    }
  }

  componentWillUnmount() {
    removeInputObject(this)
  }

  onHideCompleter() {
    const { completions, dispatch } = this.props
    this.setState({ hideCompleter: true })
    if (completions) {
      dispatch({ type: ACTION_TYPES.POST.AUTO_COMPLETE_CLEAR })
    }
  }

  onPositionChange(props) {
    this.setState(props)
  }

  onShowTextTools({ activeTools }) {
    this.setState({ hideTextTools: false, activeTools })
  }

  onHideTextTools() {
    this.setState({ hideTextTools: true })
  }

  onUserCompleter({ word }) {
    const { dispatch } = this.props
    dispatch(autoCompleteUsers('user', word))
    this.setState({ hideCompleter: false })
  }

  onEmojiCompleter({ word }) {
    const { dispatch, emoji } = this.props
    if (emoji.emojis && emoji.emojis.length) {
      dispatch({
        type: ACTION_TYPES.EMOJI.LOAD_SUCCESS,
        payload: {
          response: {
            emojis: emoji.emojis,
          },
          type: 'emoji',
          word,
        },
      })
    } else {
      dispatch(loadEmojis('emoji', word))
    }
    this.setState({ hideCompleter: false })
  }

  handleCompletion = ({ value }) => {
    replaceWordFromSelection(value)
    this.handleCancelAutoCompleter()
  };

  handleCancelAutoCompleter = () => {
    this.onHideCompleter()
    this.onHideTextTools()
    // TODO: maybe clear out the completions from the editor store
  };

  render() {
    const { authentication, children, completions, pathname } = this.props
    const { activeTools, coordinates, hideCompleter, hideTextTools } = this.state
    const { isLoggedIn } = authentication
    const appClasses = classNames(
      'App',
      { isLoggedIn },
      { isLoggedOut: !isLoggedIn },
    )
    return (

        <Helmet
          title="Be inspired."
          titleTemplate="Ello | %s"
          meta={[
            {
              name: 'apple-itunes-app',
              content: 'app-id=953614327',
              'app-argument': pathname,
            },
          ]}
        />
        { isLoggedIn ? <Omnibar/> : null }

          {children}

        { !hideCompleter && completions ?
          <Completer
            completions={ completions }
            onCancel={ this.handleCancelAutoCompleter }
            onCompletion={ this.handleCompletion }
          /> :
          null
        }
        { !hideTextTools ?
          <TextTools
            activeTools={ activeTools }
            isHidden={ hideTextTools }
            coordinates={ coordinates }
            key={ JSON.stringify(activeTools) }
          /> :
          null
        }
        <Navbar/>

        <Modal/>
        <DevTools/>
        <Analytics isLoggedIn={isLoggedIn}/>

    )
  }
}

App.preRender = (store) => {
  const state = store.getState()
  if (state.authentication && state.authentication.isLoggedIn) {
    return store.dispatch(loadProfile())
  }
}

function mapStateToProps(state) {
  return {
    authentication: state.authentication,
    completions: state.editor.completions,
    emoji: state.emoji,
    pathname: state.routing.location.pathname,
  }
}

export default connect(mapStateToProps)(App)
<|endoftext|>


<commit_before>var INT_PG_HOST = process.env.INT_PG_HOST || 'localhost',
    INT_PG_PORT = process.env.INT_PG_PORT || 5432,
    INT_PG_USER = process.env.INT_PG_USER,
    INT_PG_PASS = process.env.INT_PG_PASS,
    INT_PG_DB   = process.env.INT_PG_DB || 'medic-analytics-test',
    INT_COUCHDB_URL = process.env.INT_COUCHDB_URL || 'http://admin:pass@localhost:5984/medic-analytics-test';

process.env.POSTGRESQL_URL = 'postgres://' +
  (INT_PG_USER ? INT_PG_USER : '') +
  (INT_PG_PASS ? INT_PG_PASS += ':' + INT_PG_PASS : '') +
  (INT_PG_USER ? '@' : '') +
  INT_PG_HOST + ':' + INT_PG_PORT + '/' + INT_PG_DB;
process.env.COUCHDB_URL = INT_COUCHDB_URL;

process.env.COUCH2PG_DOC_LIMIT = 2;
process.env.COUCH2PG_CHANGES_LIMIT = 5;

var _ = require('underscore'),
    RSVP = require('rsvp'),
    common = require('../../tests/common'),
    expect = common.expect,
    Promise = RSVP.Promise,
    pgp = require('pg-promise')({ 'promiseLib': Promise }),
    format = require('pg-format'),
    couch2pg = require('../../libs/couch2pg/index'),
    pouchdb = require('pouchdb'),
    log = require('loglevel');

log.setDefaultLevel('error'); // CHANGE ME FOR MORE DETAILS

var DOCS_TO_CREATE = 10;
var DOCS_TO_ADD = 5;
var DOCS_TO_EDIT = 5;
var DOCS_TO_DELETE = 5;

var createPgConnection = function(host, port, user, pass, database) {
  var options = {
    host: host,
    port: port
  };
  if (user) {
    options.user = user;
  }
  if (pass) {
    options.pass = pass;
  }
  if (database) {
    options.database = database;
  }

  return pgp(options);
};

// drop and re-create couchdb
var initialiseCouchDb = function() {
  return pouchdb(INT_COUCHDB_URL).destroy().then(function() {
    return new Promise(function(res) {
      res(pouchdb(INT_COUCHDB_URL));
    });
  });
};

// Drop and re-create postgres db
var initialisePostgresql = function() {
  var pg = createPgConnection(INT_PG_HOST, INT_PG_PORT, INT_PG_USER, INT_PG_PASS);
  return pg.query(format('DROP DATABASE %I', INT_PG_DB))
    .then(function() {}, function() {}) // don't care if it passed or failed
    .then(function() {
      return pg.query(format('CREATE DATABASE %I', INT_PG_DB)).then(function() {
        return new Promise(function(res) {
          res(createPgConnection(INT_PG_HOST, INT_PG_PORT, INT_PG_USER, INT_PG_PASS, INT_PG_DB));
        });
      });
    });
};

var randomData = function() {
  return [
    _.sample(['happy', 'sad', 'angry', 'scared', 'morose', 'pensive', 'quixotic']),
    _.sample(['red', 'blue', 'green', 'orange', 'pink', 'mustard', 'vermillion']),
    _.sample(['cat', 'dog', 'panda', 'tiger', 'ant', 'lemur', 'dugong', 'vontsira'])
    ].join('-');
};

var generateRandomDocument = function() {
  return {
    data: randomData()
  };
};

describe('couch2pg', function() {
  var couchdb, pgdb;

  var createDocs = function(docs) {
    return couchdb.bulkDocs(docs).then(function(results) {
      return _.zip(docs, results).map(function(docAndResult) {
        // TODO replace with destructuring when node supports it
        var doc = docAndResult[0];
        var result = docAndResult[1];

        if (!result.ok) {
          throw ['Document failed to be saved', result];
        }

        doc._id = result.id;
        doc._rev = result.rev;

        return doc;
      });
    });
  };

  var couchDbDocs = function() {
    return couchdb.allDocs({
      include_docs: true
    }).then(function(results) {
      return _.pluck(results.rows, 'doc');
    });
  };

  var itRunsSuccessfully = function() {
    return couch2pg.migrate().then(couch2pg.import);
  };

  var itHasTheSameNumberOfDocs = function() {
    return RSVP.all([
      pgdb.one('SELECT COUNT(*) FROM couchdb'),
      couchDbDocs()
    ]).then(function(results) {
      var pgCount = parseInt(results[0].count),
          couchCount = results[1].length;

      expect(pgCount).to.equal(couchCount);
    });
  };

  var itHasTheSameDocuments = function() {
    return RSVP.all([
      pgdb.query('SELECT * from couchdb'),
      couchDbDocs()
    ]).then(function(results) {
      var pgdocs = _.pluck(results[0], 'doc');
      var docs = results[1];

      expect(_.sortBy(pgdocs, '_id')).deep.equals(_.sortBy(docs, '_id'));
    });
  };

  before(function() {
    return initialiseCouchDb()
      .then(function(db) {
        console.log('Initialised couchdb');
        couchdb = db;
        return initialisePostgresql();
      }).then(function(db) {
        console.log('Initialised postgres');
        pgdb = db;
      });
  });

  describe('initial import into postgres', function() {
    before(function() {
      return createDocs(_.range(DOCS_TO_CREATE).map(generateRandomDocument));
    });

    it('runs successfully', itRunsSuccessfully);
    it('has the same number of documents as couch', itHasTheSameNumberOfDocs);
    it('has the same documents as couch', itHasTheSameDocuments);
  });
  describe('subsequent creations, updates and deletions', function() {
    before(function() {
      return couchDbDocs().then(function(docs) {
        var deletedDocs = _.sample(docs, DOCS_TO_DELETE);
        docs = _.difference(docs, deletedDocs);
        deletedDocs.forEach(function(doc) {
          doc._deleted = true;
        });

        var updatedDocs = _.sample(docs, DOCS_TO_EDIT);
        updatedDocs.forEach(function(doc) {
          doc.data = randomData();
        });

        var createdDocs =_.range(DOCS_TO_ADD).map(generateRandomDocument);

        return couchdb.bulkDocs(deletedDocs)
        .then(function() {
          return couchdb.bulkDocs(updatedDocs).then(function(results) {
            results.forEach(function(result) {
              var doc = docs.find(function(d) {
                return d._id === result.id;
              });

              doc._id = result.id;
              doc._rev = result.rev;
            });
          });
        })
        .then(function() {
          return createDocs(createdDocs).then(function(createdDocs) {
            docs = docs.concat(createdDocs);
          });
        });
      });
    });

    it('runs successfully', itRunsSuccessfully);
    it('has the same number of documents as couch', itHasTheSameNumberOfDocs);
    it('has the same documents as couch', itHasTheSameDocuments);
  });
  describe('no change', function() {
    it('runs successfully', itRunsSuccessfully);
    it('still has the same number of documents as couch', itHasTheSameNumberOfDocs);
    it('still has the same documents as couch', itHasTheSameDocuments);
  });
  describe('Escaping', function() {
    it('should handle documents with \\u0000 in it', function() {
      return couchdb.put({
        _id: 'u0000-escaped',
        data: 'blah blah \u0000\u0000\u0000\u0000 blah'
      }).then(itRunsSuccessfully).catch(function(err) {
        console.log(err);
        throw new Error(err);
      });
    });
  });
});
<commit_msg>Old node support medic/medic-webapp#2430<commit_after>var INT_PG_HOST = process.env.INT_PG_HOST || 'localhost',
    INT_PG_PORT = process.env.INT_PG_PORT || 5432,
    INT_PG_USER = process.env.INT_PG_USER,
    INT_PG_PASS = process.env.INT_PG_PASS,
    INT_PG_DB   = process.env.INT_PG_DB || 'medic-analytics-test',
    INT_COUCHDB_URL = process.env.INT_COUCHDB_URL || 'http://admin:pass@localhost:5984/medic-analytics-test';

process.env.POSTGRESQL_URL = 'postgres://' +
  (INT_PG_USER ? INT_PG_USER : '') +
  (INT_PG_PASS ? INT_PG_PASS += ':' + INT_PG_PASS : '') +
  (INT_PG_USER ? '@' : '') +
  INT_PG_HOST + ':' + INT_PG_PORT + '/' + INT_PG_DB;
process.env.COUCHDB_URL = INT_COUCHDB_URL;

process.env.COUCH2PG_DOC_LIMIT = 2;
process.env.COUCH2PG_CHANGES_LIMIT = 5;

var _ = require('underscore'),
    RSVP = require('rsvp'),
    common = require('../../tests/common'),
    expect = common.expect,
    Promise = RSVP.Promise,
    pgp = require('pg-promise')({ 'promiseLib': Promise }),
    format = require('pg-format'),
    couch2pg = require('../../libs/couch2pg/index'),
    pouchdb = require('pouchdb'),
    log = require('loglevel');

log.setDefaultLevel('error'); // CHANGE ME FOR MORE DETAILS

var DOCS_TO_CREATE = 10;
var DOCS_TO_ADD = 5;
var DOCS_TO_EDIT = 5;
var DOCS_TO_DELETE = 5;

var createPgConnection = function(host, port, user, pass, database) {
  var options = {
    host: host,
    port: port
  };
  if (user) {
    options.user = user;
  }
  if (pass) {
    options.pass = pass;
  }
  if (database) {
    options.database = database;
  }

  return pgp(options);
};

// drop and re-create couchdb
var initialiseCouchDb = function() {
  return pouchdb(INT_COUCHDB_URL).destroy().then(function() {
    return new Promise(function(res) {
      res(pouchdb(INT_COUCHDB_URL));
    });
  });
};

// Drop and re-create postgres db
var initialisePostgresql = function() {
  var pg = createPgConnection(INT_PG_HOST, INT_PG_PORT, INT_PG_USER, INT_PG_PASS);
  return pg.query(format('DROP DATABASE %I', INT_PG_DB))
    .then(function() {}, function() {}) // don't care if it passed or failed
    .then(function() {
      return pg.query(format('CREATE DATABASE %I', INT_PG_DB)).then(function() {
        return new Promise(function(res) {
          res(createPgConnection(INT_PG_HOST, INT_PG_PORT, INT_PG_USER, INT_PG_PASS, INT_PG_DB));
        });
      });
    });
};

var randomData = function() {
  return [
    _.sample(['happy', 'sad', 'angry', 'scared', 'morose', 'pensive', 'quixotic']),
    _.sample(['red', 'blue', 'green', 'orange', 'pink', 'mustard', 'vermillion']),
    _.sample(['cat', 'dog', 'panda', 'tiger', 'ant', 'lemur', 'dugong', 'vontsira'])
    ].join('-');
};

var generateRandomDocument = function() {
  return {
    data: randomData()
  };
};

describe('couch2pg', function() {
  var couchdb, pgdb;

  var createDocs = function(docs) {
    return couchdb.bulkDocs(docs).then(function(results) {
      return _.zip(docs, results).map(function(docAndResult) {
        // TODO replace with destructuring when node supports it
        var doc = docAndResult[0];
        var result = docAndResult[1];

        if (!result.ok) {
          throw ['Document failed to be saved', result];
        }

        doc._id = result.id;
        doc._rev = result.rev;

        return doc;
      });
    });
  };

  var couchDbDocs = function() {
    return couchdb.allDocs({
      include_docs: true
    }).then(function(results) {
      return _.pluck(results.rows, 'doc');
    });
  };

  var itRunsSuccessfully = function() {
    return couch2pg.migrate().then(couch2pg.import);
  };

  var itHasTheSameNumberOfDocs = function() {
    return RSVP.all([
      pgdb.one('SELECT COUNT(*) FROM couchdb'),
      couchDbDocs()
    ]).then(function(results) {
      var pgCount = parseInt(results[0].count),
          couchCount = results[1].length;

      expect(pgCount).to.equal(couchCount);
    });
  };

  var itHasTheSameDocuments = function() {
    return RSVP.all([
      pgdb.query('SELECT * from couchdb'),
      couchDbDocs()
    ]).then(function(results) {
      var pgdocs = _.pluck(results[0], 'doc');
      var docs = results[1];

      expect(_.sortBy(pgdocs, '_id')).deep.equals(_.sortBy(docs, '_id'));
    });
  };

  before(function() {
    return initialiseCouchDb()
      .then(function(db) {
        console.log('Initialised couchdb');
        couchdb = db;
        return initialisePostgresql();
      }).then(function(db) {
        console.log('Initialised postgres');
        pgdb = db;
      });
  });

  describe('initial import into postgres', function() {
    before(function() {
      return createDocs(_.range(DOCS_TO_CREATE).map(generateRandomDocument));
    });

    it('runs successfully', itRunsSuccessfully);
    it('has the same number of documents as couch', itHasTheSameNumberOfDocs);
    it('has the same documents as couch', itHasTheSameDocuments);
  });
  describe('subsequent creations, updates and deletions', function() {
    before(function() {
      return couchDbDocs().then(function(docs) {
        var deletedDocs = _.sample(docs, DOCS_TO_DELETE);
        docs = _.difference(docs, deletedDocs);
        deletedDocs.forEach(function(doc) {
          doc._deleted = true;
        });

        var updatedDocs = _.sample(docs, DOCS_TO_EDIT);
        updatedDocs.forEach(function(doc) {
          doc.data = randomData();
        });

        var createdDocs =_.range(DOCS_TO_ADD).map(generateRandomDocument);

        return couchdb.bulkDocs(deletedDocs)
        .then(function() {
          return couchdb.bulkDocs(updatedDocs).then(function(results) {
            results.forEach(function(result) {
              var doc = _.find(docs, function(d) {
                return d._id === result.id;
              });

              doc._id = result.id;
              doc._rev = result.rev;
            });
          });
        })
        .then(function() {
          return createDocs(createdDocs).then(function(createdDocs) {
            docs = docs.concat(createdDocs);
          });
        });
      });
    });

    it('runs successfully', itRunsSuccessfully);
    it('has the same number of documents as couch', itHasTheSameNumberOfDocs);
    it('has the same documents as couch', itHasTheSameDocuments);
  });
  describe('no change', function() {
    it('runs successfully', itRunsSuccessfully);
    it('still has the same number of documents as couch', itHasTheSameNumberOfDocs);
    it('still has the same documents as couch', itHasTheSameDocuments);
  });
  describe('Escaping', function() {
    it('should handle documents with \\u0000 in it', function() {
      return couchdb.put({
        _id: 'u0000-escaped',
        data: 'blah blah \u0000\u0000\u0000\u0000 blah'
      }).then(itRunsSuccessfully).catch(function(err) {
        console.log(err);
        throw new Error(err);
      });
    });
  });
});
<|endoftext|>


<commit_before>import {h, Component} from 'preact';
import {Router} from 'preact-router';

import {Pages} from './pages';
import {DNE} from './dne';

import 'styles/app.scss';

class App extends Component {
  render(){
    return <Router>

        <Router>

One A

One B

        </Router>

      <Pages path="/" />
      <Pages path="/:pageid" />
      <DNE default />
    </Router>;
  }
}

export {App};
<commit_msg>routing works albeit with no wildcard<commit_after>import {h, Component} from 'preact';
import Router from 'preact-router';

import {Pages} from './pages';
import {DNE} from './dne';

import 'styles/app.scss';

class App extends Component {
  render(){
    return <Router>

1-wildcard

        <Router>

One A

One B

        </Router>

      <Pages path="/" />
      <Pages path="/:pageid" />
      <DNE default />
    </Router>;
  }
}

export {App};
<|endoftext|>


<commit_before>import { moduleFor, test } from 'ember-qunit'
import { blocklyWorkspaceMock } from '../../helpers/mocks';

moduleFor('service:blocks-gallery', 'Unit | Service | blocks-gallery', {
    needs: ['service:blocksGallery', 'service:blockly'],
    setup() {
        blocklyWorkspaceMock()
        this.container.lookup('service:blocksGallery').start();
    }
})

///////////// PARAMS /////////////

let procedure = `
<block type="procedures_defnoreturn" id="whpKBVIV.;:t%=8XN+E_" x="778" y="185">
<mutation>
  <arg name="param"></arg>
</mutation>
<field name="NAME">Hacer algo</field>
<field name="ARG0">param</field>
<statement name="STACK">
  <block type="GirarGrados" id=";qf!gXUI;'/BUa0nx#y]">
    <value name="grados">
      <block type="variables_get" id="wAE7-:@m*P0G[x'Uf$Hv">
        <mutation var="param" parent="whpKBVIV.;:t%=8XN+E_"></mutation>
      </block>
    </value>
  </block>
</statement>
</block>
`

test('Parameter in parent procedure should be ok', function(assert) {
    let param = findParam(Blockly.textToBlock(procedure))
    assert.notOk(param.disabled)
    assert.notOk(param.warning)
});

let emptyProcedure = `
<block type="procedures_defnoreturn" id="whpKBVIV.;:t%=8XN+E_" x="41" y="112">
<mutation>
  <arg name="param"></arg>
</mutation>
<field name="NAME">Hacer algo</field>
<field name="ARG0">param</field>
</block>
`

let main = `
<block type="al_empezar_a_ejecutar" id="~7u[uK:$SQa$}1uY9,h6" deletable="false" movable="false" editable="false" x="15" y="15">
<statement name="program">
  <block type="GirarGrados" id="jvQK2Fm?8Sh72]]8qk$Z">
    <value name="grados">
      <block type="variables_get" id="wAE7-:@m*P0G[x'Uf$Hv">
        <mutation var="param" parent="whpKBVIV.;:t%=8XN+E_"></mutation>
      </block>
    </value>
  </block>
</statement>
</block>
`

test('Parameter in non parent procedure should be disabled and with warning', function(assert) {
    Blockly.textToBlock(emptyProcedure)
    let param = findParam(Blockly.textToBlock(main))
    assert.ok(param.disabled)
    // TODO: No sé dónde / cuando se guarda el Warning, por consola anda pero acá no (ni siquiera después de hacer setWarningText)
    // assert.equal(param.warning.getText(), "")
});

let flying = `
<block type="variables_get" id="wAE7-:@m*P0G[x'Uf$Hv" disabled="true" x="399" y="294">
<mutation var="param" parent="whpKBVIV.;:t%=8XN+E_"></mutation>
</block>
`

test('Parameter in non parent procedure should only be disabled', function(assert) {
    Blockly.textToBlock(emptyProcedure)
    let param = findParam(Blockly.textToBlock(flying))
    assert.ok(param.disabled)
    assert.notOk(param.warning)
});

let mainWithoutParent = `
<block type="al_empezar_a_ejecutar" id="~7u[uK:$SQa$}1uY9,h6" deletable="false" movable="false" editable="false" x="15" y="15">
<statement name="program">
  <block type="GirarGrados" id="jvQK2Fm?8Sh72]]8qk$Z">
    <value name="grados">
      <block type="variables_get" id="wAE7-:@m*P0G[x'Uf$Hv">
        <mutation var="param"></mutation>
      </block>
    </value>
  </block>
</statement>
</block>
`

test('Parameter without parent procedure should be always ok', function(assert) {
    let param = findParam(Blockly.textToBlock(mainWithoutParent))
    assert.notOk(param.disabled)
    assert.notOk(param.warning)
});

function findParam(rootBlock) {
    let type = "variables_get"
    let param = rootBlock.type == type ? rootBlock : findChildren(rootBlock, type)
    param.onchange() // Force initialize
    return param
}

function findChildren(rootBlock, type) {
    return rootBlock.getChildren().find((b) => b.type == type) || findChildren(rootBlock.getChildren()[0], type)
}

///////////// ALIAS /////////////

let testAlias = function (alias, type) {
    test(`check if ${alias} block definition exist and is equal to ${type} block definition`, function (assert) {
        assert.ok(this.subject().areAliases(alias, type));
    });
}

testAlias('si', 'Si');
testAlias('sino', 'SiNo');
testAlias('Sino', 'SiNo');
testAlias('hasta', 'Hasta');
testAlias('prenderCompuConColision', 'PrenderComputadora');
testAlias('PrenderCompu', 'PrenderComputadora');
testAlias('ApagarCompu', 'ApagarComputadora');
testAlias('SiguienteCompu', 'PasarASiguienteComputadora');
testAlias('Descubralculpable', 'EsCulpable');
testAlias('repetir', 'Repetir');
testAlias('tocandoBanana', 'TocandoBanana');
testAlias('tocandoManzana', 'TocandoManzana');
testAlias('PrenderFogata', 'PrenderFogata');
testAlias('Dejarregalo', 'DejarRegalo');
testAlias('Contarbanana', 'ContarBanana');
testAlias('Contarmanzana', 'ContarManzana');
testAlias('AvanzarKm', 'Avanzar1km');
testAlias('cambiarColor', 'CambiarColor');
testAlias('siguienteFoco', 'siguienteFoco');
testAlias('empezarFiesta', 'EmpezarFiesta');
testAlias('Volveralbordeizquierdo', 'VolverAlBordeIzquierdo');
testAlias('Primersospechoso', 'IrAlPrimerSospechoso');
testAlias('PrimerSospechoso', 'IrAlPrimerSospechoso');
testAlias('Siguientesospechoso', 'IrAlSiguienteSospechoso');
testAlias('SiguienteSospechoso', 'IrAlSiguienteSospechoso');
testAlias('Sacardisfraz', 'InterrogarSospechoso');
testAlias('SacarDisfraz', 'InterrogarSospechoso');
testAlias('Estoyenunaesquina', 'EstoyEnEsquina');
testAlias('tocandoFogata', 'TocandoFogata');
testAlias('tocandoInicio', 'TocandoInicio');
testAlias('tocandoFinal', 'TocandoFinal');
testAlias('tocandoPelota', 'TocandoPelota');
testAlias('tocandoQueso', 'TocandoQueso');
testAlias('tocandoLuz', 'TocandoLuz');
testAlias('Abrirojos', 'AbrirOjos');
testAlias('Cerrarojos', 'CerrarOjos');
testAlias('Soar', 'Soniar');
testAlias('Agarrarllave', 'AgarrarLlave');
testAlias('Abrircofre', 'AbrirCofre');
testAlias('Darsombrero', 'DarSombrero');
testAlias('Atacarconespada', 'AtacarConEspada');
testAlias('Escaparenunicornio', 'EscaparEnUnicornio');
testAlias('estoyInicio', 'EstoySobreElInicio');
testAlias('estoyAlInicio', 'EstoySobreElInicio');
testAlias('estoyFinColumna', 'EstoySobreElFinal');
testAlias('EstoyAlFin', 'EstoySobreElFinal');
testAlias('ComerBananaNano', 'ComerBanana');
testAlias('saltar1', 'SaltarHablando');<commit_msg>Testing dispose param block<commit_after>import { moduleFor, test } from 'ember-qunit'
import { blocklyWorkspaceMock } from '../../helpers/mocks';

moduleFor('service:blocks-gallery', 'Unit | Service | blocks-gallery', {
    needs: ['service:blocksGallery', 'service:blockly'],
    setup() {
        blocklyWorkspaceMock()
        this.container.lookup('service:blocksGallery').start();
    }
})

///////////// PARAMS /////////////

let procedure = `
<block type="procedures_defnoreturn" id="whpKBVIV.;:t%=8XN+E_" x="778" y="185">
<mutation>
  <arg name="param"></arg>
</mutation>
<field name="NAME">Hacer algo</field>
<field name="ARG0">param</field>
<statement name="STACK">
  <block type="GirarGrados" id=";qf!gXUI;'/BUa0nx#y]">
    <value name="grados">
      <block type="variables_get" id="wAE7-:@m*P0G[x'Uf$Hv">
        <mutation var="param" parent="whpKBVIV.;:t%=8XN+E_"></mutation>
      </block>
    </value>
  </block>
</statement>
</block>
`

test('Parameter in parent procedure should be ok', function(assert) {
    let param = findParam(Blockly.textToBlock(procedure))
    assert.notOk(param.disabled)
    assert.notOk(param.warning)
});

let emptyProcedure = `
<block type="procedures_defnoreturn" id="whpKBVIV.;:t%=8XN+E_" x="41" y="112">
<mutation>
  <arg name="param"></arg>
</mutation>
<field name="NAME">Hacer algo</field>
<field name="ARG0">param</field>
</block>
`

let main = `
<block type="al_empezar_a_ejecutar" id="~7u[uK:$SQa$}1uY9,h6" deletable="false" movable="false" editable="false" x="15" y="15">
<statement name="program">
  <block type="GirarGrados" id="jvQK2Fm?8Sh72]]8qk$Z">
    <value name="grados">
      <block type="variables_get" id="wAE7-:@m*P0G[x'Uf$Hv">
        <mutation var="param" parent="whpKBVIV.;:t%=8XN+E_"></mutation>
      </block>
    </value>
  </block>
</statement>
</block>
`

test('Parameter in non parent procedure should be disabled and with warning', function(assert) {
    Blockly.textToBlock(emptyProcedure)
    let param = findParam(Blockly.textToBlock(main))
    assert.ok(param.disabled)
    // TODO: No sé dónde / cuando se guarda el Warning, por consola anda pero acá no (ni siquiera después de hacer setWarningText)
    // assert.equal(param.warning.getText(), "")
});

let flying = `
<block type="variables_get" id="wAE7-:@m*P0G[x'Uf$Hv" disabled="true" x="399" y="294">
<mutation var="param" parent="whpKBVIV.;:t%=8XN+E_"></mutation>
</block>
`

test('Parameter in non parent procedure should only be disabled', function(assert) {
    Blockly.textToBlock(emptyProcedure)
    let param = findParam(Blockly.textToBlock(flying))
    assert.ok(param.disabled)
    assert.notOk(param.warning)
});

let mainWithoutParent = `
<block type="al_empezar_a_ejecutar" id="~7u[uK:$SQa$}1uY9,h6" deletable="false" movable="false" editable="false" x="15" y="15">
<statement name="program">
  <block type="GirarGrados" id="jvQK2Fm?8Sh72]]8qk$Z">
    <value name="grados">
      <block type="variables_get" id="wAE7-:@m*P0G[x'Uf$Hv">
        <mutation var="param"></mutation>
      </block>
    </value>
  </block>
</statement>
</block>
`

test('Parameter without parent procedure should be always ok', function(assert) {
    let param = findParam(Blockly.textToBlock(mainWithoutParent))
    assert.notOk(param.disabled)
    assert.notOk(param.warning)
});

test('Parameter should dispose when procedure is disposed', function(assert) {
    let procedure = Blockly.textToBlock(emptyProcedure)
    let param = findParam(Blockly.textToBlock(flying))
    assert.ok(param.workspace)
    procedure.dispose()
    assertAsync(assert, function() {
        assert.notOk(param.workspace)
    })
});

function findParam(rootBlock) {
    let type = "variables_get"
    let param = rootBlock.type == type ? rootBlock : findChildren(rootBlock, type)
    param.onchange() // Force initialize
    return param
}

function findChildren(rootBlock, type) {
    return rootBlock.getChildren().find((b) => b.type == type) || findChildren(rootBlock.getChildren()[0], type)
}

function assertAsync(assert, fn) {
  let done = assert.async(1)
  setTimeout(function() {
      fn(); done()
  })
}

///////////// ALIAS /////////////

let testAlias = function (alias, type) {
    test(`check if ${alias} block definition exist and is equal to ${type} block definition`, function (assert) {
        assert.ok(this.subject().areAliases(alias, type));
    });
}

testAlias('si', 'Si');
testAlias('sino', 'SiNo');
testAlias('Sino', 'SiNo');
testAlias('hasta', 'Hasta');
testAlias('prenderCompuConColision', 'PrenderComputadora');
testAlias('PrenderCompu', 'PrenderComputadora');
testAlias('ApagarCompu', 'ApagarComputadora');
testAlias('SiguienteCompu', 'PasarASiguienteComputadora');
testAlias('Descubralculpable', 'EsCulpable');
testAlias('repetir', 'Repetir');
testAlias('tocandoBanana', 'TocandoBanana');
testAlias('tocandoManzana', 'TocandoManzana');
testAlias('PrenderFogata', 'PrenderFogata');
testAlias('Dejarregalo', 'DejarRegalo');
testAlias('Contarbanana', 'ContarBanana');
testAlias('Contarmanzana', 'ContarManzana');
testAlias('AvanzarKm', 'Avanzar1km');
testAlias('cambiarColor', 'CambiarColor');
testAlias('siguienteFoco', 'siguienteFoco');
testAlias('empezarFiesta', 'EmpezarFiesta');
testAlias('Volveralbordeizquierdo', 'VolverAlBordeIzquierdo');
testAlias('Primersospechoso', 'IrAlPrimerSospechoso');
testAlias('PrimerSospechoso', 'IrAlPrimerSospechoso');
testAlias('Siguientesospechoso', 'IrAlSiguienteSospechoso');
testAlias('SiguienteSospechoso', 'IrAlSiguienteSospechoso');
testAlias('Sacardisfraz', 'InterrogarSospechoso');
testAlias('SacarDisfraz', 'InterrogarSospechoso');
testAlias('Estoyenunaesquina', 'EstoyEnEsquina');
testAlias('tocandoFogata', 'TocandoFogata');
testAlias('tocandoInicio', 'TocandoInicio');
testAlias('tocandoFinal', 'TocandoFinal');
testAlias('tocandoPelota', 'TocandoPelota');
testAlias('tocandoQueso', 'TocandoQueso');
testAlias('tocandoLuz', 'TocandoLuz');
testAlias('Abrirojos', 'AbrirOjos');
testAlias('Cerrarojos', 'CerrarOjos');
testAlias('Soar', 'Soniar');
testAlias('Agarrarllave', 'AgarrarLlave');
testAlias('Abrircofre', 'AbrirCofre');
testAlias('Darsombrero', 'DarSombrero');
testAlias('Atacarconespada', 'AtacarConEspada');
testAlias('Escaparenunicornio', 'EscaparEnUnicornio');
testAlias('estoyInicio', 'EstoySobreElInicio');
testAlias('estoyAlInicio', 'EstoySobreElInicio');
testAlias('estoyFinColumna', 'EstoySobreElFinal');
testAlias('EstoyAlFin', 'EstoySobreElFinal');
testAlias('ComerBananaNano', 'ComerBanana');
testAlias('saltar1', 'SaltarHablando');<|endoftext|>


<commit_before>console.log( "=== simpread contentscripts load ===" )

/*
    import css files
*/
require( "./vender/notify/notify.css" );

/*
    Golbal jQuery variable
*/
var $         = require( "jquery" ),
    Mousetrap = require( "mousetrap" ),
    Notify    = require( "notify" ),
    focus     = require( "focus" );

/*
    keyboard event handler
*/
Mousetrap.bind([ "a s" ], focuseMode );

/*
    message request listener
*/
chrome.runtime.onMessage.addListener( function( request, sender, sendResponse ) {
    console.log( request );
    focuseMode();
});

/*
    Focus mode
*/
function focuseMode() {
    console.log( "=== simpread focus mode active ===" )

    var $focus,
        sel, range, node, tag;

    // Uniqueness verification
    if ( !focus.Verify() ) return;

    // get focus tag
    if ( $( "body" ).find( "article" ).length > 0 ) {
        // find article tag
        $focus = $( "body" ).find( "article" );
    }
    else {
        // focus
        try {
            sel    = window.getSelection();
            range  = sel.getRangeAt( sel.rangeCount - 1 );
            node   = range.startContainer.nodeName;
        if ( node.toLowerCase() === "body" ) throw( "selection area is body tag." );
            $focus = $( range.startContainer.parentNode );
        } catch ( error ) {
            console.log( sel, range, node )
            console.error( error )
            new Notify().Render( 1, "当前并未获取任何正文，请重新选取。" );
            return;
        }
    }

    // add focus mode
    focus.Render( fixFocus( $focus ));
}

/*
    Fix $focus get bad tag, include: p, span, section
    good tag include: div, article
 */
function fixFocus( $focus ) {
    var tag = $focus[0].tagName.toLowerCase();
    while (
        ( tag === "p"      ) ||
        ( tag === "span"   ) ||
        ( tag === "strong" ) ||
        ( tag === "ul"     ) ||
        ( tag === "li"     ) ||
        ( tag === "pre"    ) ||
        ( tag === "section" )
        ) {
            $focus = $focus.parent();
            tag    = $focus[0].tagName.toLowerCase();
    }
    return $focus;
}<commit_msg>Add focus mode tag 'code'.<commit_after>console.log( "=== simpread contentscripts load ===" )

/*
    import css files
*/
require( "./vender/notify/notify.css" );

/*
    Golbal jQuery variable
*/
var $         = require( "jquery" ),
    Mousetrap = require( "mousetrap" ),
    Notify    = require( "notify" ),
    focus     = require( "focus" );

/*
    keyboard event handler
*/
Mousetrap.bind([ "a s" ], focuseMode );

/*
    message request listener
*/
chrome.runtime.onMessage.addListener( function( request, sender, sendResponse ) {
    console.log( request );
    focuseMode();
});

/*
    Focus mode
*/
function focuseMode() {
    console.log( "=== simpread focus mode active ===" )

    var $focus,
        sel, range, node, tag;

    // Uniqueness verification
    if ( !focus.Verify() ) return;

    // get focus tag
    if ( $( "body" ).find( "article" ).length > 0 ) {
        // find article tag
        $focus = $( "body" ).find( "article" );
    }
    else {
        // focus
        try {
            sel    = window.getSelection();
            range  = sel.getRangeAt( sel.rangeCount - 1 );
            node   = range.startContainer.nodeName;
        if ( node.toLowerCase() === "body" ) throw( "selection area is body tag." );
            $focus = $( range.startContainer.parentNode );
        } catch ( error ) {
            console.log( sel, range, node )
            console.error( error )
            new Notify().Render( 1, "当前并未获取任何正文，请重新选取。" );
            return;
        }
    }

    // add focus mode
    focus.Render( fixFocus( $focus ));
}

/*
    Fix $focus get bad tag, include: p, span, section
    good tag include: div, article
 */
function fixFocus( $focus ) {
    var tag = $focus[0].tagName.toLowerCase();
    while (
        ( tag === "p"      ) ||
        ( tag === "span"   ) ||
        ( tag === "strong" ) ||
        ( tag === "ul"     ) ||
        ( tag === "li"     ) ||
        ( tag === "code"   ) ||
        ( tag === "pre"    ) ||
        ( tag === "section" )
        ) {
            $focus = $focus.parent();
            tag    = $focus[0].tagName.toLowerCase();
    }
    return $focus;
}<|endoftext|>


<commit_before>const levelup = require('levelup');
const sublevel = require('level-sublevel');
const fs = require('fs');

/**
 * @implements {IJungleDB}
 */
class JungleDB {
    /**
     * Initiates a new database connection. All changes to the database structure
     * require an increase in the version number.
     * Whenever new object stores need to be created, old ones deleted,
     * or indices created/deleted, the dbVersion number has to be increased.
     * When the version number increases, the given function onUpgradeNeeded is called
     * after modifying the database structure.
     * @param {string} databaseDir The name of the database.
     * @param {number} dbVersion The current version of the database.
     * @param {function()} [onUpgradeNeeded] A function to be called after upgrades of the structure.
     */
    constructor(databaseDir, dbVersion, onUpgradeNeeded) {
        if (dbVersion <= 0) throw 'The version provided must not be less or equal to 0.';
        this._databaseDir = databaseDir.endsWith('/') ? databaseDir : `${databaseDir}/`;
        this._dbVersion = dbVersion;
        this._onUpgradeNeeded = onUpgradeNeeded;
        this._connected = false;
        this._objectStores = new Map();
        this._objectStoreBackends = [];
        this._objectStoresToDelete = [];
    }

    /**
     * Returns a promise of the current database version.
     * @returns {Promise.<number>} The promise for the database version.
     * @private
     */
    _readDBVersion() {
        return new Promise((resolve, reject) => {
            this._db.get('_dbVersion', { valueEncoding: 'ascii' }, (err, value) => {
                if (err) {
                    resolve(-1);
                    return;
                }
                resolve(parseInt(value));
            });
        });
    }

    /**
     * Writes a new database version to the db.
     * @returns {Promise} A promise that resolves after successfully writing the database version.
     * @private
     */
    _writeDBVersion(version) {
        return new Promise((resolve, reject) => {
            this._db.put('_dbVersion', `${version}`, { valueEncoding: 'ascii' }, err => {
                if (err) {
                    reject(err);
                    return;
                }
                resolve();
            });
        });
    }

    /** The underlying LevelDB. */
    get backend() {
        return this._db;
    }

    /**
     * Connects to the indexedDB.
     * @returns {Promise} A promise resolving on successful connection.
     */
    connect() {
        if (this._db) return Promise.resolve(this._db);

        // Ensure existence of directory.
        if (!fs.existsSync(this._databaseDir)){
            fs.mkdirSync(this._databaseDir);
        }

        this._db = sublevel(levelup(this._databaseDir, {
            keyEncoding: 'ascii'
        }));

        return this._initDB();
    }

    /**
     * Closes the database connection.
     * @returns {Promise} The promise resolves after closing the database.
     */
    close() {
        if (this._connected) {
            this._connected = false;
            return new Promise((resolve, error) => {
                this._db.close(err => {
                    if (err) {
                        error(err);
                        return;
                    }
                    resolve();
                });
            });
        }
        return Promise.resolve();
    }

    /**
     * Fully deletes the database.
     * @returns {Promise} The promise resolves after deleting the database.
     */
    async destroy() {
        await this.close();
        return LevelDBBackend.destroy(this._databaseDir);
    }

    /**
     * Internal method that is called for opening the database connection.
     * Also handles the db version upgrade.
     * @returns {Promise.<void>} A promise that resolves after successful completion.
     * @private
     */
    async _initDB() {
        const storedVersion = await this._readDBVersion();
        let promises = [];
        // Upgrade database.
        if (this._dbVersion > storedVersion) {
            // Delete object stores, if requested.
            for (const tableName of this._objectStoresToDelete) {
                promises.push(LevelDBBackend.truncate(this._db, tableName));
            }
            delete this._objectStoresToDelete;

            // The order of the above promises does not matter.
            await Promise.all(promises);
            await this._writeDBVersion(this._dbVersion);
            promises = [];
        }

        // Create new ObjectStores.
        for (const objStore of this._objectStoreBackends) {
            promises.push(objStore.init());
        }

        // The order of the above promises does not matter.
        await Promise.all(promises);

        // Upgrade database (part 2).
        if (this._dbVersion > storedVersion) {
            // Call user defined function if requested.
            if (this._onUpgradeNeeded) {
                await this._onUpgradeNeeded();
            }
        }

        this._connected = true;
    }

    /** @type {boolean} Whether a connection is established. */
    get connected() {
        return this._connected;
    }

    /**
     * Returns the ObjectStore object for a given table name.
     * @param {string} tableName The table name to access.
     * @returns {ObjectStore} The ObjectStore object.
     */
    getObjectStore(tableName) {
        return this._objectStores.get(tableName);
    }

    /**
     * Creates a volatile object store (non-persistent).
     * @param {ICodec} [codec] A codec for the object store.
     * @returns {IObjectStore}
     */
    static createVolatileObjectStore(codec=null) {
        return new ObjectStore(new InMemoryBackend('', codec), this);
    }

    /**
     * Creates a new object store (and allows to access it).
     * This method always has to be called before connecting to the database.
     * If it is not called, the object store will not be accessible afterwards.
     * If a call is newly introduced, but the database version did not change,
     * the table does not exist yet.
     * @param {string} tableName The name of the object store.
     * @param {ICodec} [codec] A codec for the object store (by default, it is the identity codec with JSON encoding).
     * @param {boolean} [persistent] If set to false, this object store is not persistent.
     * @returns {IObjectStore}
     */
    createObjectStore(tableName, codec=null, persistent=true) {
        if (this._connected) throw 'Cannot create ObjectStore while connected';
        if (this._objectStores.has(tableName)) {
            return this._objectStores.get(tableName);
        }

        // LevelDB already implements a LRU cache. so we don't need to cache it.
        const backend = persistent
            ? new LevelDBBackend(this, tableName, codec)
            : new InMemoryBackend(tableName, codec);
        const objStore = new ObjectStore(backend, this);
        this._objectStores.set(tableName, objStore);
        this._objectStoreBackends.push(backend);
        return objStore;
    }

    /**
     * Deletes an object store.
     * This method has to be called before connecting to the database.
     * @param {string} tableName
     */
    async deleteObjectStore(tableName) {
        if (this._connected) throw 'Cannot delete ObjectStore while connected';
        this._objectStoresToDelete.push(tableName);
    }
}
/**
 * A LevelDB JSON encoding that can handle Uint8Arrays and Sets.
 * @type {{encode: function(val:*):*, decode: function(val:*):*, buffer: boolean, type: string}}
 */
JungleDB.JSON_ENCODING = {
    encode: val => JSON.stringify(val, (k, v) => {
        if (v instanceof Uint8Array) {
            return Array.from(v);
        }
        if (v instanceof Set) {
            return Array.from(v);
        }
        return v;
    }),
    decode: JSON.parse,
    buffer: false,
    type: 'json'
};
Class.register(JungleDB);
<commit_msg>fix fs scoping<commit_after>const levelup = require('levelup');
const sublevel = require('level-sublevel');

/**
 * @implements {IJungleDB}
 */
class JungleDB {
    /**
     * Initiates a new database connection. All changes to the database structure
     * require an increase in the version number.
     * Whenever new object stores need to be created, old ones deleted,
     * or indices created/deleted, the dbVersion number has to be increased.
     * When the version number increases, the given function onUpgradeNeeded is called
     * after modifying the database structure.
     * @param {string} databaseDir The name of the database.
     * @param {number} dbVersion The current version of the database.
     * @param {function()} [onUpgradeNeeded] A function to be called after upgrades of the structure.
     */
    constructor(databaseDir, dbVersion, onUpgradeNeeded) {
        if (dbVersion <= 0) throw 'The version provided must not be less or equal to 0.';
        this._databaseDir = databaseDir.endsWith('/') ? databaseDir : `${databaseDir}/`;
        this._dbVersion = dbVersion;
        this._onUpgradeNeeded = onUpgradeNeeded;
        this._connected = false;
        this._objectStores = new Map();
        this._objectStoreBackends = [];
        this._objectStoresToDelete = [];
    }

    /**
     * Returns a promise of the current database version.
     * @returns {Promise.<number>} The promise for the database version.
     * @private
     */
    _readDBVersion() {
        return new Promise((resolve, reject) => {
            this._db.get('_dbVersion', { valueEncoding: 'ascii' }, (err, value) => {
                if (err) {
                    resolve(-1);
                    return;
                }
                resolve(parseInt(value));
            });
        });
    }

    /**
     * Writes a new database version to the db.
     * @returns {Promise} A promise that resolves after successfully writing the database version.
     * @private
     */
    _writeDBVersion(version) {
        return new Promise((resolve, reject) => {
            this._db.put('_dbVersion', `${version}`, { valueEncoding: 'ascii' }, err => {
                if (err) {
                    reject(err);
                    return;
                }
                resolve();
            });
        });
    }

    /** The underlying LevelDB. */
    get backend() {
        return this._db;
    }

    /**
     * Connects to the indexedDB.
     * @returns {Promise} A promise resolving on successful connection.
     */
    connect() {
        if (this._db) return Promise.resolve(this._db);

        const fs = require('fs');
        // Ensure existence of directory.
        if (!fs.existsSync(this._databaseDir)){
            fs.mkdirSync(this._databaseDir);
        }

        this._db = sublevel(levelup(this._databaseDir, {
            keyEncoding: 'ascii'
        }));

        return this._initDB();
    }

    /**
     * Closes the database connection.
     * @returns {Promise} The promise resolves after closing the database.
     */
    close() {
        if (this._connected) {
            this._connected = false;
            return new Promise((resolve, error) => {
                this._db.close(err => {
                    if (err) {
                        error(err);
                        return;
                    }
                    resolve();
                });
            });
        }
        return Promise.resolve();
    }

    /**
     * Fully deletes the database.
     * @returns {Promise} The promise resolves after deleting the database.
     */
    async destroy() {
        await this.close();
        return LevelDBBackend.destroy(this._databaseDir);
    }

    /**
     * Internal method that is called for opening the database connection.
     * Also handles the db version upgrade.
     * @returns {Promise.<void>} A promise that resolves after successful completion.
     * @private
     */
    async _initDB() {
        const storedVersion = await this._readDBVersion();
        let promises = [];
        // Upgrade database.
        if (this._dbVersion > storedVersion) {
            // Delete object stores, if requested.
            for (const tableName of this._objectStoresToDelete) {
                promises.push(LevelDBBackend.truncate(this._db, tableName));
            }
            delete this._objectStoresToDelete;

            // The order of the above promises does not matter.
            await Promise.all(promises);
            await this._writeDBVersion(this._dbVersion);
            promises = [];
        }

        // Create new ObjectStores.
        for (const objStore of this._objectStoreBackends) {
            promises.push(objStore.init());
        }

        // The order of the above promises does not matter.
        await Promise.all(promises);

        // Upgrade database (part 2).
        if (this._dbVersion > storedVersion) {
            // Call user defined function if requested.
            if (this._onUpgradeNeeded) {
                await this._onUpgradeNeeded();
            }
        }

        this._connected = true;
    }

    /** @type {boolean} Whether a connection is established. */
    get connected() {
        return this._connected;
    }

    /**
     * Returns the ObjectStore object for a given table name.
     * @param {string} tableName The table name to access.
     * @returns {ObjectStore} The ObjectStore object.
     */
    getObjectStore(tableName) {
        return this._objectStores.get(tableName);
    }

    /**
     * Creates a volatile object store (non-persistent).
     * @param {ICodec} [codec] A codec for the object store.
     * @returns {IObjectStore}
     */
    static createVolatileObjectStore(codec=null) {
        return new ObjectStore(new InMemoryBackend('', codec), this);
    }

    /**
     * Creates a new object store (and allows to access it).
     * This method always has to be called before connecting to the database.
     * If it is not called, the object store will not be accessible afterwards.
     * If a call is newly introduced, but the database version did not change,
     * the table does not exist yet.
     * @param {string} tableName The name of the object store.
     * @param {ICodec} [codec] A codec for the object store (by default, it is the identity codec with JSON encoding).
     * @param {boolean} [persistent] If set to false, this object store is not persistent.
     * @returns {IObjectStore}
     */
    createObjectStore(tableName, codec=null, persistent=true) {
        if (this._connected) throw 'Cannot create ObjectStore while connected';
        if (this._objectStores.has(tableName)) {
            return this._objectStores.get(tableName);
        }

        // LevelDB already implements a LRU cache. so we don't need to cache it.
        const backend = persistent
            ? new LevelDBBackend(this, tableName, codec)
            : new InMemoryBackend(tableName, codec);
        const objStore = new ObjectStore(backend, this);
        this._objectStores.set(tableName, objStore);
        this._objectStoreBackends.push(backend);
        return objStore;
    }

    /**
     * Deletes an object store.
     * This method has to be called before connecting to the database.
     * @param {string} tableName
     */
    async deleteObjectStore(tableName) {
        if (this._connected) throw 'Cannot delete ObjectStore while connected';
        this._objectStoresToDelete.push(tableName);
    }
}
/**
 * A LevelDB JSON encoding that can handle Uint8Arrays and Sets.
 * @type {{encode: function(val:*):*, decode: function(val:*):*, buffer: boolean, type: string}}
 */
JungleDB.JSON_ENCODING = {
    encode: val => JSON.stringify(val, (k, v) => {
        if (v instanceof Uint8Array) {
            return Array.from(v);
        }
        if (v instanceof Set) {
            return Array.from(v);
        }
        return v;
    }),
    decode: JSON.parse,
    buffer: false,
    type: 'json'
};
Class.register(JungleDB);
<|endoftext|>


<commit_before>// requestCSV:
// param: 	file 	- File name to be provided
// param: 	fn		- Callback function to receive the data.
function requestCSV(file, fn)
{
	// jQuery request of file.
	$.get(file,
		function(data) {
			fn(data);
		});
}

// CSVtoJSON
// param: 	data - CSV Data to be converted into an object
// return:  Object with all of the CSV values in the *.vals location as a Key/Value array, where the third column is the key.
function CSVtoJSON(data)
{
	// Rows are split off of new lines in simple CSV
	rows = data.split("\n");

	ret = new Object();
	ret.vals = new Object();
    var header = new Array();

	// iterate over all of the rows to build the object(must start a 0 so heading is avalible downstream)
	for (i = 0; i < rows.length; i++)
	{
		// Split on ", " in CSV data we received
		cols = rows[i].split(",", rows[0].split("\",\"").length);

		// Remove quotes out of all underlying elements
		for (j = 0; j < cols.length; j++)
			cols[j] = cols[j].replace(/\"/g, "");

		// Skip over undefined values for data integrity
		if (cols[2] != undefined)
        {
            //changed to add k/v object instead of array
            if(i==0)
            {
                header = cols;
            }
            else
            {
                var student = {};
                for (var k=0; k<cols.length;k++)
                    student[header[k]] = cols[k];
                ret.vals[cols[2]] = student;
            }
        }
	}
	return ret;
}

//student object constructor
//Param: student is object map of student data.
//Param: the types row from import (must be score,note,lettergrade,section or $TYPE(only repo cell))
//Param: catagories row from csv (assignment groupings can include anything)
var Student = function(student,types,catagories)
{
    this.letterGrade = "";
    this.notes = "";
    this.totalGrade = -1;
    this.section = "";
    this.repo = "";
    this.scores = getCatagories(catagories);

    //TODO Add set total Grade Function

    for(key in Object.keys(student))
    {

        if(type[key]=="$TYPE")
            this.repo = student[key];
        else if(type[key]==="note")
            this.note = student[key];
        else if(type[key]==="lettergrade")
            this.LetterGrade = student[key];
        else if(type[key]==="section")
            this.section = student[key];
        else if(type[key]==="score")
        {
            if(catagoies[key]=="totalgrade")
                this.totalGrade=student[key];
            else
            {
                this.scores[catagories[key]][key]=student[key];
            }

        }

    }
}

//Param: data - required for the passthrough object
//returns section object with map object of  students see Student construcor for more info
function getSection(data)
{
    var section = {};
    section.students = {};

    var json = CSVtoJSON(data);
    for (var repo in json.vals )
    {

        if(repo != "$TYPE" || repo != "$CATAGORY" || repo != "$FINAL" ||repo != "$QUIZ" ||repo != "$HW" ||repo != "$MIDTERM")
            section.students[repo] = new Student(json[repo],json["$TYPES"],json["$CATAGORIES"]);
    }
    //TODO add class prorates
    return section;

}

//
//return object of catagories
function getCatagories(catagoriesObj)
{
    var catagoriesList = {};

    if (typeof catagoriesObj != "object")
        return {};

    for (item in catagoriesObj)
        if(catagoriesObj[item] != "$CATAGORY" && catagoriesObj[item]!= "")
            catagoriesList[catagoriesObj[item]] = {};
    return catagoriesList;
}

<commit_msg>tried changing syntax<commit_after>// requestCSV:
// param: 	file 	- File name to be provided
// param: 	fn		- Callback function to receive the data.
function requestCSV(file, fn)
{
	// jQuery request of file.
	$.get(file,
		function(data) {
			fn(data);
		});
}

// CSVtoJSON
// param: 	data - CSV Data to be converted into an object
// return:  Object with all of the CSV values in the *.vals location as a Key/Value array, where the third column is the key.
function CSVtoJSON(data)
{
	// Rows are split off of new lines in simple CSV
	rows = data.split("\n");

	ret = new Object();
	ret.vals = new Object();
    var header = new Array();

	// iterate over all of the rows to build the object(must start a 0 so heading is avalible downstream)
	for (i = 0; i < rows.length; i++)
	{
		// Split on ", " in CSV data we received
		cols = rows[i].split(",", rows[0].split("\",\"").length);

		// Remove quotes out of all underlying elements
		for (j = 0; j < cols.length; j++)
			cols[j] = cols[j].replace(/\"/g, "");

		// Skip over undefined values for data integrity
		if (cols[2] != undefined)
        {
            //changed to add k/v object instead of array
            if(i==0)
            {
                header = cols;
            }
            else
            {
                var student = {};
                for (var k=0; k<cols.length;k++)
                    student[header[k]] = cols[k];
                ret.vals[cols[2]] = student;
            }
        }
	}
	return ret;
}

//student object constructor
//Param: student is object map of student data.
//Param: the types row from import (must be score,note,lettergrade,section or $TYPE(only repo cell))
//Param: catagories row from csv (assignment groupings can include anything)
var Student = function(student,types,catagories)
{
    this.letterGrade = "";
    this.notes = "";
    this.totalGrade = -1;
    this.section = "";
    this.repo = "";
    this.scores = getCatagories(catagories);

    //TODO Add set total Grade Function

    for(key in Object.keys(student))
    {

        if(type[key]=="$TYPE")
            this.repo = student[key];
        else if(type[key]==="note")
            this.note = student[key];
        else if(type[key]==="lettergrade")
            this.LetterGrade = student[key];
        else if(type[key]==="section")
            this.section = student[key];
        else if(type[key]==="score")
        {
            if(catagoies[key]=="totalgrade")
                this.totalGrade=student[key];
            else
            {
                this.scores[catagories[key]][key]=student[key];
            }

        }

    }
}

//Param: data - required for the passthrough object
//returns section object with map object of  students see Student construcor for more info
function getSection(data)
{
    var section = {};
    section.students = {};

    var json = CSVtoJSON(data);
    for (var repo in json.vals )
    {

        if(repo != "$TYPE" || repo != "$CATAGORY" || repo != "$FINAL" ||repo != "$QUIZ" ||repo != "$HW" ||repo != "$MIDTERM")
            section.students[repo] = new Student(json[repo],json["$TYPES"],json["$CATAGORIES"]);
    }
    //TODO add class prorates
    return section;

}

//
//return object of catagories
function getCatagories(catagoriesObj)
{
    var catagoriesList = {};

    if (typeof catagoriesObj != "object")
        return {};

    for (item in Object.keys(catagoriesObj))
        if(catagoriesObj[item] != "$CATAGORY" && catagoriesObj[item]!= "")
            catagoriesList[catagoriesObj[item]] = {};
    return catagoriesList;
}

<|endoftext|>


<commit_before>// ---------------------------------------------------------------------------------------
// Note: The following BSD-New license relates only to this particular file, which is
// not part of the runtime package for KnockoutJS - it is only used as part of a mechanism
// for testing KnockoutJS.
// The following license is *not* the license for KnockoutJS.
// ---------------------------------------------------------------------------------------

// Based on https://raw.github.com/ariya/phantomjs/1.7.0/examples/run-jasmine.js

//Redistribution and use in source and binary forms, with or without
//modification, are permitted provided that the following conditions are met:
//
//  * Redistributions of source code must retain the above copyright
//    notice, this list of conditions and the following disclaimer.
//  * Redistributions in binary form must reproduce the above copyright
//    notice, this list of conditions and the following disclaimer in the
//    documentation and/or other materials provided with the distribution.
//  * Neither the name of the <organization> nor the
//    names of its contributors may be used to endorse or promote products
//    derived from this software without specific prior written permission.
//
//THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
//AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
//IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
//ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
//DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
//(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
//LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
//ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
//(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
//THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

var system = require('system');

/**
 * Wait until the test condition is true or a timeout occurs. Useful for waiting
 * on a server response or for a ui change (fadeIn, etc.) to occur.
 *
 * @param testFx javascript condition that evaluates to a boolean,
 * it can be passed in as a string (e.g.: "1 == 1" or "$('#bar').is(':visible')" or
 * as a callback function.
 * @param onReady what to do when testFx condition is fulfilled,
 * it can be passed in as a string (e.g.: "1 == 1" or "$('#bar').is(':visible')" or
 * as a callback function.
 * @param timeOutMillis the max amount of time to wait. If not specified, 3 sec is used.
 */
function waitFor(testFx, onReady, onTimeout, timeOutMillis) {
    var maxtimeOutMillis = timeOutMillis ? timeOutMillis : 3001, //< Default Max Timeout is 3s
        start = new Date().getTime(),
        condition = false,
        interval = setInterval(function() {
            if ( (new Date().getTime() - start < maxtimeOutMillis) && !condition ) {
                // If not time-out yet and condition not yet fulfilled
                condition = (typeof(testFx) === "string" ? eval(testFx) : testFx()); //< defensive code
            } else {
                if(!condition) {
                    // If condition still not fulfilled (timeout but condition is 'false')
                    console.log("'waitFor()' timeout");
                    onTimeout ? onTimeout() : phantom.exit(1);
                } else {
                    clearInterval(interval); //< Stop this interval
                    // Condition fulfilled (timeout and/or condition is 'true')
                    typeof(onReady) === "string" ? eval(onReady) : onReady(); //< Do what it's supposed to do once the condition is fulfilled
                }
            }
        }, 100); //< repeat check every 100ms
};

var page = require('webpage').create();

console.log("Running Knockout tests in Phantom.js");

// Route "console.log()" calls from within the Page context to the main Phantom context (i.e. current "this")
page.onConsoleMessage = function(msg) {
    console.log(msg);
};

// Run the specs against the latest minified build
page.open(system.args[1] || 'spec/runner.html?src=build/output/knockout-latest.js', function(status){
    if (status !== "success") {
        console.log("Unable to access network");
        phantom.exit();
    } else {
        waitFor(function(){
            return page.evaluate(function(){
                return document.body.querySelector('.symbolSummary .pending') === null
            });
        }, function(){
            var exitCode = page.evaluate(function(){
                var list = document.body.querySelectorAll('.results > #details > .specDetail.failed');
                if (list && list.length > 0) {
                  console.log('');
                  console.log(list.length + ' test(s) FAILED:');
                  for (i = 0; i < list.length; ++i) {
                      var el = list[i],
                          desc = el.querySelector('.description'),
                          msg = el.querySelector('.resultMessage.fail');
                      console.log('');
                      console.log(desc.innerText);
                      console.log(msg.innerText);
                      console.log('');
                  }
                  return 1;
                } else {
                  console.log(document.body.querySelector('.alert > .passingAlert.bar').innerText);
                  return 0;
                }
            });
            phantom.exit(exitCode);
        }, function() {
            var exitCode = page.evaluate(function(){
                var specs = document.body.querySelectorAll('.results .specSummary');
                console.log('Last test completed:');
                console.log(specs[specs.length-1].innerText);
            });
            phantom.exit(exitCode);
        });
    }
});
<commit_msg>Make sure we exit with an error code if runner times out<commit_after>// ---------------------------------------------------------------------------------------
// Note: The following BSD-New license relates only to this particular file, which is
// not part of the runtime package for KnockoutJS - it is only used as part of a mechanism
// for testing KnockoutJS.
// The following license is *not* the license for KnockoutJS.
// ---------------------------------------------------------------------------------------

// Based on https://raw.github.com/ariya/phantomjs/1.7.0/examples/run-jasmine.js

//Redistribution and use in source and binary forms, with or without
//modification, are permitted provided that the following conditions are met:
//
//  * Redistributions of source code must retain the above copyright
//    notice, this list of conditions and the following disclaimer.
//  * Redistributions in binary form must reproduce the above copyright
//    notice, this list of conditions and the following disclaimer in the
//    documentation and/or other materials provided with the distribution.
//  * Neither the name of the <organization> nor the
//    names of its contributors may be used to endorse or promote products
//    derived from this software without specific prior written permission.
//
//THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
//AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
//IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
//ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
//DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
//(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
//LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
//ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
//(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
//THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

var system = require('system');

/**
 * Wait until the test condition is true or a timeout occurs. Useful for waiting
 * on a server response or for a ui change (fadeIn, etc.) to occur.
 *
 * @param testFx javascript condition that evaluates to a boolean,
 * it can be passed in as a string (e.g.: "1 == 1" or "$('#bar').is(':visible')" or
 * as a callback function.
 * @param onReady what to do when testFx condition is fulfilled,
 * it can be passed in as a string (e.g.: "1 == 1" or "$('#bar').is(':visible')" or
 * as a callback function.
 * @param timeOutMillis the max amount of time to wait. If not specified, 3 sec is used.
 */
function waitFor(testFx, onReady, onTimeout, timeOutMillis) {
    var maxtimeOutMillis = timeOutMillis ? timeOutMillis : 3001, //< Default Max Timeout is 3s
        start = new Date().getTime(),
        condition = false,
        interval = setInterval(function() {
            if ( (new Date().getTime() - start < maxtimeOutMillis) && !condition ) {
                // If not time-out yet and condition not yet fulfilled
                condition = (typeof(testFx) === "string" ? eval(testFx) : testFx()); //< defensive code
            } else {
                if(!condition) {
                    // If condition still not fulfilled (timeout but condition is 'false')
                    console.log("'waitFor()' timeout");
                    onTimeout ? onTimeout() : phantom.exit(1);
                } else {
                    clearInterval(interval); //< Stop this interval
                    // Condition fulfilled (timeout and/or condition is 'true')
                    typeof(onReady) === "string" ? eval(onReady) : onReady(); //< Do what it's supposed to do once the condition is fulfilled
                }
            }
        }, 100); //< repeat check every 100ms
};

var page = require('webpage').create();

console.log("Running Knockout tests in Phantom.js");

// Route "console.log()" calls from within the Page context to the main Phantom context (i.e. current "this")
page.onConsoleMessage = function(msg) {
    console.log(msg);
};

// Run the specs against the latest minified build
page.open(system.args[1] || 'spec/runner.html?src=build/output/knockout-latest.js', function(status){
    if (status !== "success") {
        console.log("Unable to access network");
        phantom.exit();
    } else {
        waitFor(function(){
            return page.evaluate(function(){
                return document.body.querySelector('.symbolSummary .pending') === null
            });
        }, function(){
            var exitCode = page.evaluate(function(){
                var list = document.body.querySelectorAll('.results > #details > .specDetail.failed');
                if (list && list.length > 0) {
                  console.log('');
                  console.log(list.length + ' test(s) FAILED:');
                  for (i = 0; i < list.length; ++i) {
                      var el = list[i],
                          desc = el.querySelector('.description'),
                          msg = el.querySelector('.resultMessage.fail');
                      console.log('');
                      console.log(desc.innerText);
                      console.log(msg.innerText);
                      console.log('');
                  }
                  return 1;
                } else {
                  console.log(document.body.querySelector('.alert > .passingAlert.bar').innerText);
                  return 0;
                }
            });
            phantom.exit(exitCode);
        }, function() {
            var exitCode = page.evaluate(function(){
                var specs = document.body.querySelectorAll('.results .specSummary');
                console.log('Last test completed:');
                console.log(specs[specs.length-1].innerText);
            });
            phantom.exit(exitCode || 1);
        });
    }
});
<|endoftext|>


<commit_before>/* File: FwMock.js
 * Authors: Palmer Truelson
 * Company: Massive Health, LLC
 * Licence: $LICENSE
 * About: Fireworks Mock Classes. Allows us to run unit tests on
 *  fireworks files
 */
// add underscore
_ = require('../lib/underscore.js');

// it all starts with an object
fw = {};
exports.fw = fw;

fw._mockHelpers = {};
fw.selection = [];

fw._mockHelpers.setBasics = function (obj) {
  obj.height = 0;
  obj.isLayer = false;
  obj.left = 0;
  obj.name = null;
  obj.top = 0;
  obj.width = 0;
  obj.visible = true;
}

// Group
var Group = function (spec) {
  fw._mockHelpers.setBasics(this);
  if(spec) {
    _.extend(this, spec);
  }
}
Group.prototype.toString = function () {
  return '[object Group]';
}
exports.Group = Group;

// Image
var Image = function (spec) {
  fw._mockHelpers.setBasics(this);
  if(spec) {
    _.extend(this, spec);
  }
}
Image.prototype.toString = function () {
  return '[object Image]';
}
exports.Image = Image;

// Instances
var Instance = function (spec) {
  fw._mockHelpers.setBasics(this);
  this.transform = { matrix: [1, 0, 0, 0, 1, 0, 10, 20, 1] };

  if(spec) {
    _.extend(this, spec);
  }
}
Instance.prototype.toString = function () {
  return '[object Instance]';
}
exports.Instance = Instance;

var RectanglePrimitive = function (spec) {
  fw._mockHelpers.setBasics(this);
  if(spec) {
    _.extend(this, spec);
  }
}
RectanglePrimitive.prototype.toString = function () {
  return '[object RectanglePrimitive]';
}
exports.RectanglePrimitive = RectanglePrimitive;

// Layer
var Layer = function (spec) {
  this.elems = [];
  this.isLayer = true;
  this.layerType = 'normal';
  this.name = null;
  this.visible = true;

  if(spec) {
    _.extend(this, spec);
  }
};
Layer.prototype.toString = function () {
  return '[object Layer]';
}
exports.Layer = Layer;

// Text
var Text = function (spec) {
  fw._mockHelpers.setBasics(this);
  this.alignment = 'left';
  this.pathAttributes = {
    fillColor: '#ff0000'
  },
  this.font = '';
  this.fontsize = 0;
  this.name = null;
  this.textChars = '';

  if(spec) {
    _.extend(this, spec);
  }
};
Text.prototype.toString = function () {
  return '[object Text]';
}
exports.Text = Text;

fw._resetMockDom = function(d) {
  d.pageName = 'TestView';
  d.width = 320;
  d.height = 480;
  d.backgroundColor = '#ffffff';
  d.exportOptions = {
    colorMode: '32 bit'
  }
  d.resolution = 300;
  d.resolutionUnits = 1;

  return d;
};

var mockLayer = {
  elems: [ ]
}

var mockCopyObj;

fw.createDocument = function() {
  return fw._resetMockDom({
    // dom functions
    exportElements: function (elems, file, imageDir, name) {},

    cloneSelection: function() {
      mockCopyObj = fw.selection;
    },
    flattenSelection: function() {},
    getSelectionBounds: function() {return {};},

    clipCut: function() {},
    clipPaste: function() {
      mockLayer.elems.push(mockCopyObj);
    },

    close: function() {},
    moveSelectionTo: function() {},

    setDocumentResolution: function() {},
    setDocumentCanvasSize: function() {},

    layers: [mockLayer]
  });
};

fw._mockDOM = fw.createDocument();
fw.getDocumentDOM = function () {return fw._mockDOM;};
fw.exportDocumentAs = function () {};

fw.runScript = function (scriptName) {};
fw.currentScriptDir = './';

fw.browseForFolderURL = function (name, hint) { return '~/TestDir'; };

// Mocks of Fireworks class objects

// File accessing mocks
fw._mockFile = {};
fw._mockFile.mockPath = '';
fw._mockFile.write = function (text) {};
fw._mockFile.writeUTF8 = function (text) {};
fw._mockFile.close = function () {};

Files = {};
exports.Files = Files;

Files.createFile = function (name, type, from) {};
Files.createDirectory = function (name) {};
Files.deleteFileIfExisting = function (name) {};

Files.open = function (path, canWrite) {
  fw._mockFile.mockPath = path;
  fw._mockFile.canWrite = (canWrite || false);
  return fw._mockFile;
};

<commit_msg>Got rid of semicolons in FwMock<commit_after>/* File: FwMock.js
 * Authors: Palmer Truelson
 * Company: Massive Health, LLC
 * Licence: $LICENSE
 * About: Fireworks Mock Classes. Allows us to run unit tests on
 *  fireworks files
 */
// add underscore
_ = require('../lib/underscore.js')

// it all starts with an object
fw = {}
exports.fw = fw

fw._mockHelpers = {}
fw.selection = []

fw._mockHelpers.setBasics = function (obj) {
  obj.height = 0
  obj.isLayer = false
  obj.left = 0
  obj.name = null
  obj.top = 0
  obj.width = 0
  obj.visible = true
  obj.pathAttributes = {
    brush: {
      brushColor: '#FFFFFF',
    },

    fill: {
      fillColor: '#FFFFFF',
      name: 'Solid',
      textureBlend: 0
    }
  }
}

// Group
var Group = function (spec) {
  fw._mockHelpers.setBasics(this)
  if(spec) {
    _.extend(this, spec)
  }
}
Group.prototype.toString = function () {
  return '[object Group]'
}
exports.Group = Group

// Image
var Image = function (spec) {
  fw._mockHelpers.setBasics(this)
  if(spec) {
    _.extend(this, spec)
  }
}
Image.prototype.toString = function () {
  return '[object Image]'
}
exports.Image = Image

// Instances
var Instance = function (spec) {
  fw._mockHelpers.setBasics(this)
  this.transform = { matrix: [1, 0, 0, 0, 1, 0, 10, 20, 1] }

  if(spec) {
    _.extend(this, spec)
  }
}
Instance.prototype.toString = function () {
  return '[object Instance]'
}
exports.Instance = Instance

var RectanglePrimitive = function (spec) {
  fw._mockHelpers.setBasics(this)
  if(spec) _.extend(this, spec)
}
RectanglePrimitive.prototype.toString = function () {
  return '[object RectanglePrimitive]'
}
exports.RectanglePrimitive = RectanglePrimitive

// Layer
var Layer = function (spec) {
  this.elems = []
  this.isLayer = true
  this.layerType = 'normal'
  this.name = null
  this.visible = true

  if(spec) {
    _.extend(this, spec)
  }
}
Layer.prototype.toString = function () {
  return '[object Layer]'
}
exports.Layer = Layer

// Text
var Text = function (spec) {
  fw._mockHelpers.setBasics(this)
  this.alignment = 'left'
  this.pathAttributes = {
    fillColor: '#ff0000'
  },
  this.font = ''
  this.fontsize = 0
  this.name = null
  this.textChars = ''

  if(spec) {
    _.extend(this, spec)
  }
}
Text.prototype.toString = function () {
  return '[object Text]'
}
exports.Text = Text

fw._resetMockDom = function(d) {
  d.pageName = 'TestView'
  d.width = 320
  d.height = 480
  d.backgroundColor = '#ffffff'
  d.exportOptions = {
    colorMode: '32 bit'
  }
  d.resolution = 300
  d.resolutionUnits = 1

  return d
}

var mockLayer = {
  elems: [ ]
}

var mockCopyObj

fw.createDocument = function() {
  return fw._resetMockDom({
    // dom functions
    exportElements: function (elems, file, imageDir, name) {},

    cloneSelection: function() {
      mockCopyObj = fw.selection
    },
    flattenSelection: function() {},
    getSelectionBounds: function() {return {}},

    clipCut: function() {},
    clipPaste: function() {
      mockLayer.elems.push(mockCopyObj)
    },

    close: function() {},
    moveSelectionTo: function() {},

    setDocumentResolution: function() {},
    setDocumentCanvasSize: function() {},

    layers: [mockLayer]
  })
}

fw._mockDOM = fw.createDocument()
fw.getDocumentDOM = function () {return fw._mockDOM}
fw.exportDocumentAs = function () {}

fw.runScript = function (scriptName) {}
fw.currentScriptDir = './'

fw.browseForFolderURL = function (name, hint) { return '~/TestDir' }

// Mocks of Fireworks class objects

// File accessing mocks
fw._mockFile = {}
fw._mockFile.mockPath = ''
fw._mockFile.write = function (text) {}
fw._mockFile.writeUTF8 = function (text) {}
fw._mockFile.close = function () {}

Files = {}
exports.Files = Files

Files.createFile = function (name, type, from) {}
Files.createDirectory = function (name) {}
Files.deleteFileIfExisting = function (name) {}

Files.open = function (path, canWrite) {
  fw._mockFile.mockPath = path
  fw._mockFile.canWrite = (canWrite || false)
  return fw._mockFile
}

<|endoftext|>


<commit_before>import Component from '../Component'
import PlayToggle from './PlayToggle'
import FullscreenToggle from './FullscreenToggle'
import {MSG as SliderMSG} from './Slider'
import Timeline from './Timeline'
import Timelabel from './Timelabel'
import Volume from './Volume'
import ClaritySwitcher from './ClaritySwitcher'
import {MSG as PlayerMSG} from '../message'
import * as dom from '../dom'
import * as util from '../util'
import * as B from '../browser'

/**
 *
 * @property {Timeline} timeline
 * @property {Volume} volume
 * @property {FullscreenToggle} fullscreen
 * @property {Player} player
 */
export default class Panel extends Component {
	constructor(player) {
		super(player, 'Panel');
	}
	render(owner) {
		this.createEl('div', {'class': 'vcp-controls-panel'});
		this.el.appendChild(dom.createEl('div', {'class': 'vcp-panel-bg'}));
		this.playToggle = new PlayToggle(this.player);
		this.playToggle.render(this.el);

		this.timelabel = new Timelabel(this.player);
		this.timelabel.render(this.el);

		this.timeline = new Timeline(this.player);
		this.timeline.render(this.el);

		if (!B.IS_X5TBS) {
			this.fullscreen = new FullscreenToggle(this.player);
			this.fullscreen.render(this.el);
		}

		if (!B.IS_MOBILE) {
			this.volume = new Volume(this.player);
			this.volume.render(this.el);
		}

		if(this.options.videoSource.definitions.length > 1 && !B.IS_MOBILE){
			this.claritySwitcher = new ClaritySwitcher(this.player);
			this.claritySwitcher.render(this.el);
		}

		return super.render(owner);
	}
	setup() {// add play toggle, progress, time label, volume/mute, fullscreen
		var handler = util.bind(this, this.handleMsg);
		// todo 可以批量添加事件
		this.sub(SliderMSG.Changing, this.volume, handler);
		this.sub(SliderMSG.Changed, this.timeline.progress, handler);
		this.sub(PlayerMSG.TimeUpdate, this.player.video, handler);
		this.sub(PlayerMSG.Progress, this.player.video, handler);
		this.sub(PlayerMSG.MetaLoaded, this.player.video, handler);
		this.sub(PlayerMSG.Pause, this.player.video, handler);
		this.sub(PlayerMSG.Play, this.player.video, handler);
		this.sub(PlayerMSG.Ended, this.player.video, handler);
	}
	handleMsg(msg) {
		switch (msg.type) {
			case PlayerMSG.MetaLoaded:
				this.timeline.percent(this.player.percent());
				this.timeline.buffered(this.player.buffered());
				this.player.volume(typeof this.options.volume === 'undefined' ? 0.5 : this.options.volume);
				!this.options.autoplay && this.show();
				break;
			case PlayerMSG.TimeUpdate:
				if (!this.timeline.scrubbing)
					this.timeline.percent(this.player.percent());
				break;
			case PlayerMSG.Pause:
				this.show();
				break;
			case PlayerMSG.Play:
				this.hide();
				break;
			case PlayerMSG.Progress:
				this.timeline.buffered(this.player.buffered()); // todo IE9 会最后一段时间就不触发progress了
				break;
			case SliderMSG.Changed:
				if (msg.src === this.timeline.progress) {
					this.player.percent(this.timeline.percent());
				}
				break;
			case PlayerMSG.Ended:
				this.show();
				break;
		}
	}
	toggle() {
		if (this.el.style.display === 'block') this.hide();
		else this.show();
	}
	show() {
		if (this.el.style.display === 'block') return;

		dom.removeClass(this.el, 'fadeOut');
		dom.addClass(this.el, 'fadeIn');
		var self = this;
		setTimeout(function() {
			self.el.style.display = "block";
		}, 500);
	}
	hide() {
		dom.removeClass(this.el, 'fadeIn');
		dom.addClass(this.el, 'fadeOut');
		var self = this;
		setTimeout(function() {
			self.el.style.display = "none";
		}, 500);
	}
}<commit_msg>bugfix<commit_after>import Component from '../Component'
import PlayToggle from './PlayToggle'
import FullscreenToggle from './FullscreenToggle'
import {MSG as SliderMSG} from './Slider'
import Timeline from './Timeline'
import Timelabel from './Timelabel'
import Volume from './Volume'
import ClaritySwitcher from './ClaritySwitcher'
import {MSG as PlayerMSG} from '../message'
import * as dom from '../dom'
import * as util from '../util'
import * as B from '../browser'

/**
 *
 * @property {Timeline} timeline
 * @property {Volume} volume
 * @property {FullscreenToggle} fullscreen
 * @property {Player} player
 */
export default class Panel extends Component {
	constructor(player) {
		super(player, 'Panel');
	}
	render(owner) {
		this.createEl('div', {'class': 'vcp-controls-panel'});
		this.el.appendChild(dom.createEl('div', {'class': 'vcp-panel-bg'}));
		this.playToggle = new PlayToggle(this.player);
		this.playToggle.render(this.el);

		this.timelabel = new Timelabel(this.player);
		this.timelabel.render(this.el);

		this.timeline = new Timeline(this.player);
		this.timeline.render(this.el);

		if (!B.IS_X5TBS) {
			this.fullscreen = new FullscreenToggle(this.player);
			this.fullscreen.render(this.el);
		}

		if (!B.IS_MOBILE) {
			this.volume = new Volume(this.player);
			this.volume.render(this.el);
		}

		if(this.options.videoSource && this.options.videoSource.definitions.length > 1 && !B.IS_MOBILE){
			this.claritySwitcher = new ClaritySwitcher(this.player);
			this.claritySwitcher.render(this.el);
		}

		return super.render(owner);
	}
	setup() {// add play toggle, progress, time label, volume/mute, fullscreen
		var handler = util.bind(this, this.handleMsg);
		// todo 可以批量添加事件
		this.sub(SliderMSG.Changing, this.volume, handler);
		this.sub(SliderMSG.Changed, this.timeline.progress, handler);
		this.sub(PlayerMSG.TimeUpdate, this.player.video, handler);
		this.sub(PlayerMSG.Progress, this.player.video, handler);
		this.sub(PlayerMSG.MetaLoaded, this.player.video, handler);
		this.sub(PlayerMSG.Pause, this.player.video, handler);
		this.sub(PlayerMSG.Play, this.player.video, handler);
		this.sub(PlayerMSG.Ended, this.player.video, handler);
	}
	handleMsg(msg) {
		switch (msg.type) {
			case PlayerMSG.MetaLoaded:
				this.timeline.percent(this.player.percent());
				this.timeline.buffered(this.player.buffered());
				this.player.volume(typeof this.options.volume === 'undefined' ? 0.5 : this.options.volume);
				!this.options.autoplay && this.show();
				break;
			case PlayerMSG.TimeUpdate:
				if (!this.timeline.scrubbing)
					this.timeline.percent(this.player.percent());
				break;
			case PlayerMSG.Pause:
				this.show();
				break;
			case PlayerMSG.Play:
				this.hide();
				break;
			case PlayerMSG.Progress:
				this.timeline.buffered(this.player.buffered()); // todo IE9 会最后一段时间就不触发progress了
				break;
			case SliderMSG.Changed:
				if (msg.src === this.timeline.progress) {
					this.player.percent(this.timeline.percent());
				}
				break;
			case PlayerMSG.Ended:
				this.show();
				break;
		}
	}
	toggle() {
		if (this.el.style.display === 'block') this.hide();
		else this.show();
	}
	show() {
		if (this.el.style.display === 'block') return;

		dom.removeClass(this.el, 'fadeOut');
		dom.addClass(this.el, 'fadeIn');
		var self = this;
		setTimeout(function() {
			self.el.style.display = "block";
		}, 500);
	}
	hide() {
		dom.removeClass(this.el, 'fadeIn');
		dom.addClass(this.el, 'fadeOut');
		var self = this;
		setTimeout(function() {
			self.el.style.display = "none";
		}, 500);
	}
}<|endoftext|>