<commit_before>import genActionTypes from './actionTypes'

function deleteIndex(array, index)
{
  array.splice(index, 1)
}

function idStrictEqual(element)
{
  return element.id === this.toString()
}

const defaultOnRollback = console.error.bind(console)

function reducer(basePath, options={})
{
  const childReducer = options.childReducer
  const onRollback   = options.onRollback || defaultOnRollback

  const baseType = basePath.slice(0, basePath.length-1).toUpperCase()
  const actionTypes = genActionTypes(baseType)

  return function(state = [], action)
  {
    const {meta, payload, type} = action
    if(!type.startsWith(baseType)) return state

    let result = [...state]
    let index
    let item

    if(meta)
    {
      const {func, id} = meta

      if(func)
      {
        if(type.endsWith('commit'))
          func(null, payload)
        else
          func(payload)

        return state
      }

      if(id)
      {
        index = result.findIndex(idStrictEqual, id)
        item = result[index]
      }
    }

    switch(type)
    {
      // Add

      case actionTypes.add:
        result.push({...payload})
      break

      case actionTypes.add_commit:
        result[index] = {...item, id: payload}
      break

      case actionTypes.add_rollback:
        onRollback(payload)

        deleteIndex(result, index)
      break

      // Read

      case actionTypes.read:
        console.info(actionTypes.read)

        return state

      case actionTypes.read_commit:
        // Collection
        if(id == null)
          result = [...payload]

        // Non-existing resource
        else if(index == -1)
          result.push({...payload})

        // Existing resource
        else
          result[index] = {...payload}
      break

      case actionTypes.read_rollback:
        onRollback(payload)

        return state

      // Update & Patch

      case actionTypes.update:
        result[index] = {...payload, _rollback: item}
      break

      case actionTypes.patch:
        result[index] = {...item, ...payload, _rollback: item}
      break

      case actionTypes.update_commit:
      case actionTypes.patch_commit:
        result[index] = {...item, _rollback: undefined}
      break

      case actionTypes.update_rollback:
      case actionTypes.patch_rollback:
        onRollback(payload)

        result[index] = {...item._rollback}
      break

      // Delete

      case actionTypes.delete:
        result[index] = {...item, _pendingDeletion: true}
      break

      case actionTypes.delete_commit:
        deleteIndex(result, index)
      break

      case actionTypes.delete_rollback:
        onRollback(payload)

        result[index] = {...item, _pendingDeletion: undefined}
      break

      // Unknown action, use child redurec or return untouched current state

      default:
        if(!childReducer) return state

        if(!item)
        {
          // TODO notify user

          return state
        }

        item.projects = childReducer(item.projects,
        {
          ...action,
          meta: {...meta, id: meta.project_id, project_id: undefined}
        })
    }

    // Return modified state
    return result
  }
}

module.exports = reducer
<commit_msg>Reducer's namespaces<commit_after>import genActionTypes from './actionTypes'

function deleteIndex(array, index)
{
  array.splice(index, 1)
}

function idStrictEqual(element)
{
  return element.id === this.toString()
}

function reduceNamespaces(acum, name)
{
  return {...acum, [name]: reducer(name, this)}
}

const defaultOnRollback = console.error.bind(console)

function reducer(basePath, options={})
{
  const childReducer = options.childReducer
  const onRollback   = options.onRollback || defaultOnRollback

  const baseType = basePath.slice(0, basePath.length-1).toUpperCase()
  const actionTypes = genActionTypes(baseType)

  return function(state = [], action)
  {
    const {meta, payload, type} = action
    if(!type.startsWith(baseType)) return state

    let result = [...state]
    let index
    let item

    if(meta)
    {
      const {func, id} = meta

      if(func)
      {
        if(type.endsWith('commit'))
          func(null, payload)
        else
          func(payload)

        return state
      }

      if(id)
      {
        index = result.findIndex(idStrictEqual, id)
        item = result[index]
      }
    }

    switch(type)
    {
      // Add

      case actionTypes.add:
        result.push({...payload})
      break

      case actionTypes.add_commit:
        result[index] = {...item, id: payload}
      break

      case actionTypes.add_rollback:
        onRollback(payload)

        deleteIndex(result, index)
      break

      // Read

      case actionTypes.read:
        console.info(actionTypes.read)

        return state

      case actionTypes.read_commit:
        // Collection
        if(id == null)
          result = [...payload]

        // Non-existing resource
        else if(index == -1)
          result.push({...payload})

        // Existing resource
        else
          result[index] = {...payload}
      break

      case actionTypes.read_rollback:
        onRollback(payload)

        return state

      // Update & Patch

      case actionTypes.update:
        result[index] = {...payload, _rollback: item}
      break

      case actionTypes.patch:
        result[index] = {...item, ...payload, _rollback: item}
      break

      case actionTypes.update_commit:
      case actionTypes.patch_commit:
        result[index] = {...item, _rollback: undefined}
      break

      case actionTypes.update_rollback:
      case actionTypes.patch_rollback:
        onRollback(payload)

        result[index] = {...item._rollback}
      break

      // Delete

      case actionTypes.delete:
        result[index] = {...item, _pendingDeletion: true}
      break

      case actionTypes.delete_commit:
        deleteIndex(result, index)
      break

      case actionTypes.delete_rollback:
        onRollback(payload)

        result[index] = {...item, _pendingDeletion: undefined}
      break

      // Unknown action, use child redurec or return untouched current state

      default:
        if(!childReducer) return state

        if(!item)
        {
          // TODO notify user

          return state
        }

        item.projects = childReducer(item.projects,
        {
          ...action,
          meta: {...meta, id: meta.project_id, project_id: undefined}
        })
    }

    // Return modified state
    return result
  }
}

function namespaces(namespaces, options)
{
  return namespaces.reduce(reduceNamespaces.bind(options), {})
}

export default reducer
export {namespaces}
<|endoftext|>


<commit_before>const pify = require('pify')
const rimraf = require('rimraf')
const fs = require('fs')
const assert = require('assert')
const getGitStatus = require('./utils/get-git-status')
const execute = require('./utils/execute')

const log = console.log
const removeDir = pify(rimraf)
const readFile = pify(fs.readFile)
const writeFile = pify(fs.writeFile)

const dryRun = process.argv.includes('--dry-run')

const semver = process.argv[2]
assert.ok(['patch', 'minor', 'major'].includes(semver), 'Must specify the valid semver version: patch | minor | major')

;(async () => {
  log('Doing sanity checks...')
  const {currentBranch: branchToPublish, isCleanWorkingTree} = await getGitStatus()
  if (!dryRun) {
    assert.equal(branchToPublish, 'master', 'Must be on master branch')
  }
  assert.equal(isCleanWorkingTree, true, 'Must have clean working tree')

  log(`Running npm version ${semver}...`)
  await execute(`npm version --no-git-tag-version ${semver}`)
  const {version} = require('./package.json')

  log(`Making a version change commit...`)
  await execute(`git add package.json && git commit -m "${version}"`)

  log('Deleting the dist folder (it will conflict with the next step)...')
  await removeDir('dist')

  log('Switching to the dist branch...')
  await execute('git checkout dist')

  log(`Merging from "${branchToPublish}" branch...`)
  await execute(`git merge ${branchToPublish}`)

  log('Running the build...')
  await execute('npm run build')

  log('Running the checks...')
  await execute('npm run check')

  if (dryRun) {
    log('Skipping publishing on npm...')
  } else {
    log('Publishing on npm...')
    await execute('npm publish')
  }

  log('Removing "dist" from .gitignore...')
  const gitignore = await readFile('.gitignore', 'utf8')
  const gitignoreWithoutDist = gitignore.split(/\r?\n/).filter(line => line !== 'dist').join('\n')
  await writeFile('.gitignore', gitignoreWithoutDist)

  log('Committing the dist dir...')
  await execute(`git add dist/ && git commit -m "Release v${version}"`)

  log('Reverting the change to .gitignore...')
  await execute('git reset --hard HEAD')

  log(`Tagging commit as "v${version}"...`)
  await execute(`git tag "v${version}"`)

  if (dryRun) {
    log('Skipping pushing to Github...')
  } else {
    log('Pushing to Github both master and dist branches...')
    await execute('git push origin master:master dist:dist --tags')
  }

  log(`Switching back to "${branchToPublish}" (so you can continue to work)...`)
  await execute(`git checkout "${branchToPublish}"`)

  log('OK!')
})().catch(console.error)
<commit_msg>(release script) Handle pulling latest master and dist from Github (#883)<commit_after>const pify = require('pify')
const rimraf = require('rimraf')
const fs = require('fs')
const assert = require('assert')
const getGitStatus = require('./utils/get-git-status')
const execute = require('./utils/execute')

const log = console.log
const removeDir = pify(rimraf)
const readFile = pify(fs.readFile)
const writeFile = pify(fs.writeFile)

const dryRun = process.argv.includes('--dry-run')

const semver = process.argv[2]
assert.ok(['patch', 'minor', 'major'].includes(semver), 'Must specify the valid semver version: patch | minor | major')

;(async () => {
  log('Doing sanity checks...')
  const {currentBranch: branchToPublish, isCleanWorkingTree} = await getGitStatus()
  if (!dryRun) {
    assert.equal(branchToPublish, 'master', 'Must be on master branch')
  }
  assert.equal(isCleanWorkingTree, true, 'Must have clean working tree')

  log(`Pulling the latest ${branchToPublish} branch from Github...`)
  await execute('git pull origin')

  log(`Running npm version ${semver}...`)
  await execute(`npm version --no-git-tag-version ${semver}`)
  const {version} = require('./package.json')

  log(`Making a version change commit...`)
  await execute(`git add package.json && git commit -m "${version}"`)

  log('Deleting the dist folder (it will conflict with the next step)...')
  await removeDir('dist')

  log('Switching to the dist branch...')
  await execute('git checkout dist')

  log('Pulling the latest dist branch from Github...')
  await execute('git pull origin')

  log(`Merging from "${branchToPublish}" branch...`)
  await execute(`git merge ${branchToPublish}`)

  log('Running the build...')
  await execute('npm run build')

  log('Running the checks...')
  await execute('npm run check')

  if (dryRun) {
    log('Skipping publishing on npm...')
  } else {
    log('Publishing on npm...')
    await execute('npm publish')
  }

  log('Removing "dist" from .gitignore...')
  const gitignore = await readFile('.gitignore', 'utf8')
  const gitignoreWithoutDist = gitignore.split(/\r?\n/).filter(line => line !== 'dist').join('\n')
  await writeFile('.gitignore', gitignoreWithoutDist)

  log('Committing the dist dir...')
  await execute(`git add dist/ && git commit -m "Release v${version}"`)

  log('Reverting the change to .gitignore...')
  await execute('git reset --hard HEAD')

  log(`Tagging commit as "v${version}"...`)
  await execute(`git tag "v${version}"`)

  if (dryRun) {
    log('Skipping pushing to Github...')
  } else {
    log('Pushing to Github both master and dist branches...')
    await execute('git push origin master:master dist:dist --tags')
  }

  log(`Switching back to "${branchToPublish}" (so you can continue to work)...`)
  await execute(`git checkout "${branchToPublish}"`)

  log('OK!')
})().catch(console.error)
<|endoftext|>


<commit_before>const pify = require('pify')
const rimraf = require('rimraf')
const fs = require('fs')
const assert = require('assert')
const getGitStatus = require('./utils/get-git-status')
const execute = require('./utils/execute')

const log = console.log
const removeDir = pify(rimraf)
const readFile = pify(fs.readFile)
const writeFile = pify(fs.writeFile)

const dryRun = process.argv.includes('--dry-run')

const semver = process.argv[2]
assert.ok(['patch', 'minor', 'major'].includes(semver), 'Must specify the valid semver version: patch | minor | major')

;(async () => {
  log('Doing sanity checks...')
  const {currentBranch: branchToPublish, isCleanWorkingTree} = await getGitStatus()
  if (!dryRun) {
    assert.equal(branchToPublish, 'master', 'Must be on master branch')
  }
  assert.equal(isCleanWorkingTree, true, 'Must have clean working tree')

  log(`Pulling the latest ${branchToPublish} branch from Github...`)
  await execute('git pull origin')

  log(`Running npm version ${semver}...`)
  await execute(`npm version --no-git-tag-version ${semver}`)
  const {version} = require('./package.json')

  log(`Making a version change commit...`)
  await execute(`git add package.json && git commit -m "${version}"`)

  log('Deleting the dist folder (it will conflict with the next step)...')
  await removeDir('dist')

  log('Switching to the dist branch...')
  await execute('git checkout dist')

  log('Pulling the latest dist branch from Github...')
  await execute('git pull origin')

  log(`Merging from "${branchToPublish}" branch...`)
  await execute(`git merge ${branchToPublish}`)

  log('Installing npm dependencies...')
  await execute('yarn')

  log('Running the build...')
  await execute('npm run build')

  log('Running the checks...')
  await execute('npm run check')

  if (dryRun) {
    log('Skipping publishing on npm...')
  } else {
    log('Publishing on npm...')
    await execute('npm publish')
  }

  log('Removing "dist" from .gitignore...')
  const gitignore = await readFile('.gitignore', 'utf8')
  const gitignoreWithoutDist = gitignore.split(/\r?\n/).filter(line => line !== 'dist').join('\n')
  await writeFile('.gitignore', gitignoreWithoutDist)

  log('Committing the dist dir...')
  await execute(`git add dist/ && git commit -m "Release v${version}"`)

  log('Reverting the change to .gitignore...')
  await execute('git reset --hard HEAD')

  log(`Tagging commit as "v${version}"...`)
  await execute(`git tag "v${version}"`)

  if (dryRun) {
    log('Skipping pushing to Github...')
  } else {
    log('Pushing to Github both master and dist branches...')
    await execute('git push origin master:master dist:dist --tags')
  }

  log(`Switching back to "${branchToPublish}" (so you can continue to work)...`)
  await execute(`git checkout "${branchToPublish}"`)

  log('OK!')
})().catch(console.error)
<commit_msg>fix(release-script): add --strategy-option=theirs to git merge master (#1061)<commit_after>const pify = require('pify')
const rimraf = require('rimraf')
const fs = require('fs')
const assert = require('assert')
const getGitStatus = require('./utils/get-git-status')
const execute = require('./utils/execute')

const log = console.log
const removeDir = pify(rimraf)
const readFile = pify(fs.readFile)
const writeFile = pify(fs.writeFile)

const dryRun = process.argv.includes('--dry-run')

const semver = process.argv[2]
assert.ok(['patch', 'minor', 'major'].includes(semver), 'Must specify the valid semver version: patch | minor | major')

;(async () => {
  log('Doing sanity checks...')
  const {currentBranch: branchToPublish, isCleanWorkingTree} = await getGitStatus()
  if (!dryRun) {
    assert.equal(branchToPublish, 'master', 'Must be on master branch')
  }
  assert.equal(isCleanWorkingTree, true, 'Must have clean working tree')

  log(`Pulling the latest ${branchToPublish} branch from Github...`)
  await execute('git pull origin')

  log(`Running npm version ${semver}...`)
  await execute(`npm version --no-git-tag-version ${semver}`)
  const {version} = require('./package.json')

  log(`Making a version change commit...`)
  await execute(`git add package.json && git commit -m "${version}"`)

  log('Deleting the dist folder (it will conflict with the next step)...')
  await removeDir('dist')

  log('Switching to the dist branch...')
  await execute('git checkout dist')

  log('Pulling the latest dist branch from Github...')
  await execute('git pull origin')

  log(`Merging from "${branchToPublish}" branch...`)
  await execute(`git merge --strategy-option=theirs ${branchToPublish}`)

  log('Installing npm dependencies...')
  await execute('yarn')

  log('Running the build...')
  await execute('npm run build')

  log('Running the checks...')
  await execute('npm run check')

  if (dryRun) {
    log('Skipping publishing on npm...')
  } else {
    log('Publishing on npm...')
    await execute('npm publish')
  }

  log('Removing "dist" from .gitignore...')
  const gitignore = await readFile('.gitignore', 'utf8')
  const gitignoreWithoutDist = gitignore.split(/\r?\n/).filter(line => line !== 'dist').join('\n')
  await writeFile('.gitignore', gitignoreWithoutDist)

  log('Committing the dist dir...')
  await execute(`git add dist/ && git commit -m "Release v${version}"`)

  log('Reverting the change to .gitignore...')
  await execute('git reset --hard HEAD')

  log(`Tagging commit as "v${version}"...`)
  await execute(`git tag "v${version}"`)

  if (dryRun) {
    log('Skipping pushing to Github...')
  } else {
    log('Pushing to Github both master and dist branches...')
    await execute('git push origin master:master dist:dist --tags')
  }

  log(`Switching back to "${branchToPublish}" (so you can continue to work)...`)
  await execute(`git checkout "${branchToPublish}"`)

  log('OK!')
})().catch(console.error)
<|endoftext|>


<commit_before>/**
 * @class Relation
 *
 * In graph terminology a relation represents a directed edge, a
 * reference from one asset to another. For the purpose of being able
 * to treat all relations equally, there's a subclass for each
 * supported relation type, encapsulating the details of how to
 * retrieve, update, and (optionally) inline the asset being pointed
 * to.
 *
 * These are some examples of included subclasses:
 *
 *    - `relations.HtmlAnchor`         An anchor tag in an HTML document `<a href='...'>`.
 *    - `relations.HtmlImage`          An `<img src='...'>` tag in an HTML document.
 *    - `relations.CssImport`          An `@import` declaration in a CSS asset.
 *    - `relations.CacheManifestEntry` A line in a cache manifest.
 */
const _ = require('lodash');
const extendDefined = require('../util/extendDefined');
const urlTools = require('urltools');
const urlModule = require('url');

/**
 * new Relation(options)
 * =====================
 *
 * Create a new Relation instance. For existing assets the
 * instantiation of relations happens automatically if you use the
 * `populate` transform. You only need to create relations manually
 * when you need to introduce new ones.
 *
 * Note that the base Relation class should be considered
 * abstract. Please instantiate the appropriate subclass.
 *
 * Options:
 *
 *  - `from` The source asset of the relation.
 *  - `to`   The target asset of the relation, or an asset configuration
 *           object if the target asset hasn't yet been resolved and created.
 */
class Relation {
    constructor(config) {
        if (config.hrefType) {
            this._hrefType = config.hrefType;
            config.hrefType = undefined;
        }
        if (config.to) {
            this._to = config.to;
            config.to = undefined;
        }
        extendDefined(this, config);
        this.id = '' + _.uniqueId();
    }

    /**
     * relation.from (Asset)
     * =====================
     *
     * The source asset of the relation.
     */

    /**
     * relation.to (Asset)
     * ===================
     *
     * The target asset of the relation.
     */
    get to() {
        return this._to;
    }

    set to(to) {
        this._to = this.from.assetGraph.addAsset(to, this);
        this.refreshHref();
    }

    /**
     * relation.href (getter/setter)
     * =============================
     *
     * Get or set the href of the relation. The relation must be
     * attached to an asset.
     *
     * What is actually retrieved or updated depends on the relation
     * type. For `HtmlImage` the `src` attribute of the HTML element
     * is changed, for `CssImport` the parsed representation of
     * the @import rule is updated, etc.
     *
     * Most of the time you don't need to think about this property,
     * as the href is automatically updated when the url of the source
     * or target asset is changed, or an intermediate asset is
     * inlined.
     *
     * @api public
     */

    /**
     * relation.fragment (getter/setter)
     * =================================
     *
     * Get or set the fragment identifier part of the href of the relation.
     */
    get fragment() {
        const href = this.href;
        if (typeof href === 'string') {
            const indexOfFragment = href.indexOf('#');
            if (indexOfFragment === -1) {
                return '';
            } else {
                return href.substr(indexOfFragment);
            }
        }
    }

    set fragment(fragment) {
        if (fragment && !/^#/.test(fragment)) {
            throw new Error('The fragment must begin with a # or be empty');
        }
        const href = this.href;
        if (href) {
            let currentHrefWithoutFragment;
            const indexOfFragment = href.indexOf('#');
            if (indexOfFragment === -1) {
                currentHrefWithoutFragment = href;
            } else {
                currentHrefWithoutFragment = href.substr(0, indexOfFragment);
            }
            this.href = currentHrefWithoutFragment + (fragment || '');
            this.from.markDirty();
        }
    }

    /**
     * relation.refreshHref
     * ====================
     *
     * Update `href` of a relation to make sure it points at the
     * current url of its target asset.
     *
     * It's not necessary to call this function manually as long as
     * the source and target assets of the relation have only been
     * moved by having their `url` property changed (the recommended
     * way), but some transforms will need this after some low-level
     * surgery, such as attaching an existing relation to a different
     * asset.
     *
     * @return {Relation} The relation itself (chaining-friendly).
     * @api public
     */
    refreshHref() {
        const hrefType = this.hrefType;
        if (hrefType === 'inline') {
            this.inline();
        } else {
            const currentHref = this.href;
            if (/^#/.test(currentHref)) {
                // Pure fragment url, nothing to do
                return;
            }
            const targetUrl = this.to.url;
            const assetGraph = this.from.assetGraph;
            const canonical = this.canonical;
            let href;
            if (hrefType === 'rootRelative' && !canonical) {
                href = urlTools.buildRootRelativeUrl(this.baseUrl, targetUrl, assetGraph.root);
            } else if (hrefType === 'relative' && !canonical) {
                href = urlTools.buildRelativeUrl(this.baseUrl, targetUrl);
            } else if (hrefType === 'protocolRelative') {
                href = urlTools.buildProtocolRelativeUrl(this.baseUrl, targetUrl);
            } else {
                // Absolute
                href = targetUrl;
            }
            // Hack: Avoid adding index.html to an href pointing at file://.../index.html if it's not already there:
            if (/^file:\/\/.*\/index\.html(?:[?#]|$)/.test(targetUrl) && !/(?:^|\/)index\.html(?:[?#]:|$)/.test(this.href)) {
                href = href.replace(/(^|\/)index\.html(?=[?#]|$)/, '$1');
            }
            if (canonical) {
                href = href.replace(assetGraph.root, assetGraph.canonicalRoot);
            }
            const currentFragment = this.fragment;
            if (currentFragment) {
                href += currentFragment;
            }
            if (currentHref !== href) {
                this.href = href;
                this.from.markDirty();
            }
        }
        return this;
    }

    get crossorigin() {
        const fromUrl = this.from.nonInlineAncestor.url;
        const toUrl = this.to.url;
        if (!toUrl) {
            // Inline
            return false;
        }
        if (this.canonical) {
            return false;
        }
        const fromUrlObj = urlModule.parse(fromUrl);
        const toUrlObj = urlModule.parse(urlModule.resolve(fromUrl, toUrl));
        if (fromUrlObj.protocol !== toUrlObj.protocol || fromUrlObj.hostname !== toUrlObj.hostname) {
            return true;
        }
        const fromPort = fromUrlObj.port ? parseInt(fromUrlObj.port, 10) : {'http:': 80, 'https:': 443}[fromUrlObj.protocol];
        const toPort = toUrlObj.port ? parseInt(toUrlObj.port, 10) : {'http:': 80, 'https:': 443}[toUrlObj.protocol];
        return fromPort !== toPort;
    }

    get canonical() {
        if (typeof this._canonical === 'undefined') {
            let canonical = false;

            if (this.href && this.from && this.from.assetGraph && this.from.assetGraph.canonicalRoot) {
                const canonicalRootObj = urlModule.parse(this.from.assetGraph.canonicalRoot, false, true);
                const hrefObj = urlModule.parse(this.href, false, true);

                canonical = hrefObj.slashes === true &&
                    ['http:', 'https:', null].includes(hrefObj.protocol) &&
                    canonicalRootObj.host === hrefObj.host &&
                    hrefObj.path.startsWith(canonicalRootObj.path) &&
                    (canonicalRootObj.protocol === hrefObj.protocol || canonicalRootObj.protocol === null);
            }

            this._canonical = canonical;
        }

        return this._canonical;
    }

    set canonical(isCanonical) {
        if (this.from && this.from.assetGraph && this.from.assetGraph.canonicalRoot) {
            isCanonical = !!isCanonical;
            if (this._canonical !== isCanonical) {
                this._canonical = isCanonical;

                if (!isCanonical && (this._hrefType === 'absolute' || this._hrefType !== 'protocolRelative')) {
                    // We're switching to non-canonical mode. Degrade the href type
                    // to rootRelative so we won't issue absolute file:// urls
                    // This is based on guesswork, though.
                    this._hrefType = 'rootRelative';
                }
                this.refreshHref();
            }
        }
    }

    get baseUrl() {
        const nonInlineAncestor = this.from.nonInlineAncestor;
        return nonInlineAncestor && nonInlineAncestor.url;
    }

    /**
     * relation.hrefType (getter/setter)
        * =================================
     *
     * Either 'absolute', 'rootRelative', 'protocolRelative', or 'relative'. Decides what "degree" of
     * relative url refreshHref tries to issue.
     */

    get hrefType() {
        const to = this.to;
        if (to.isInline && to !== this.from) {
            return 'inline';
        }
        if (!this._hrefType) {
            const href = (this.href || '').trim();
            if (/^\/\//.test(href)) {
                this._hrefType = 'protocolRelative';
            } else if (/^\//.test(href)) {
                this._hrefType = 'rootRelative';
            } else if (/^[a-z\+]+:/i.test(href)) {
                this._hrefType = 'absolute';
            } else {
                this._hrefType = 'relative';
            }
        }
        return this._hrefType;
    }

    set hrefType(hrefType) {
        if (hrefType === 'inline') {
            this.inline();
        } else if (hrefType !== this._hrefType) {
            this._hrefType = hrefType;
            this.refreshHref();
        }
    }

    /**
     * relation.inline()
     * =================
     *
     * Inline the relation. This is only supported by certain relation
     * types and will produce different results depending on the type
     * (`data:` url, inline script, inline stylesheet...).
     *
     * Will make a clone of the target asset if it has more incoming
     * relations than this one.
     *
     * @return {Relation} The relation itself (chaining-friendly).
     * @api public
     */
    inline() {
        if (this.to.incomingRelations.filter(r => r._to !== r.from).length > 1) {
            // This isn't the only incoming relation to the asset, clone before inlining.
            this.to.clone(this);
        }
        this.to.incomingInlineRelation = this;
        if (!this.to.isInline) {
            this.to.url = null;
        }
        return this;
    }

    /**
     * relation.attach(position[, adjacentRelation])
     * ====================================================
     *
     * Attaches the relation to an asset.
     *
     * The ordering of certain relation types is significant
     * (`HtmlScript`, for instance), so it's important that the order
     * isn't scrambled in the indices. Therefore the caller must
     * explicitly specify a position at which to insert the object.
     *
     * @param {Asset} asset The asset to attach the relation to.
     * @param {String} position "first", "last", "before", or "after".
     * @param {Relation} adjacentRelation The adjacent relation, mandatory if the position is "before" or "after".
     * @return {Relation} The relation itself (chaining-friendly).
     * @api public
     */
    attach(position, adjacentRelation) {
        this.from.markDirty();
        this.addToOutgoingRelations(position, adjacentRelation);
        if (this.to && this.to.url) {
            this.refreshHref();
        }
        return this;
    }

    addToOutgoingRelations(position, adjacentRelation) {
        const outgoingRelations = this.from.outgoingRelations;
        const existingIndex = outgoingRelations.indexOf(this);
        if (existingIndex !== -1) {
            outgoingRelations.splice(existingIndex, 1);
        }
        if (position === 'last') {
            outgoingRelations.push(this);
        } else if (position === 'first') {
            outgoingRelations.unshift(this);
        } else if (position === 'before' || position === 'after') { // Assume 'before' or 'after'
            if (!adjacentRelation || !adjacentRelation.isRelation) {
                throw new Error(`addRelation: Adjacent relation is not a relation: ${adjacentRelation}`);
            }
            const i = outgoingRelations.indexOf(adjacentRelation) + (position === 'after' ? 1 : 0);
            if (i === -1) {
                throw new Error(`addRelation: Adjacent relation ${adjacentRelation.toString()} is not among the outgoing relations of ${this.urlOrDescription}`);
            }
            outgoingRelations.splice(i, 0, this);
        } else {
            throw new Error(`addRelation: Illegal 'position' argument: ${position}`);
        }
    }

    /**
     * relation.detach()
     * =================
     *
     * Detaches the relation from the asset it is currently attached
     * to. If the relation is currently part of a graph, it will
     * removed from it.
     *
     * Detaching implies that the tag/statement/declaration
     * representing the relation is physically removed from the
     * referring asset. Not all relation types support this.
     *
     * @return {Relation} The relation itself (chaining-friendly).
     * @api public
     */
    detach() {
        this.from.markDirty();
        this.remove();
        return this;
    }

    /**
     * relation.remove()
     * =================
     *
     * Removes the relation from the graph it's currently part
     * of. Doesn't detach the relation (compare with
     * `relation.detach()`).
     *
     * @return {Relation} The relation itself (chaining-friendly).
     * @api public
     */
    remove() {
        this.from.removeRelation(this);
        return this;
    }

    /**
     * relation.toString()
     * ===================
     *
     * Get a brief text containing the type, id of the relation. Will
     * also contain the `.toString()` of the relation's source and
     * target assets if available.
     *
     * @return {String} The string, eg. "[HtmlAnchor/141: [Html/40 file:///foo/bar/index.html] => [Html/76 file:///foo/bar/otherpage.html]]"
     * @api public
     */
    toString() {
        return '[' + this.type + '/' + this.id + ': ' + ((this.from && this.to) ? this.from.toString() + ' => ' + (this.to.isAsset ? this.to.toString() : this.to.url || this.to.type || '?') : 'unattached') + ']';
    }
};

Object.assign(Relation.prototype, {
    /**
     * relation.isRelation (boolean)
     * =============================
     *
     * Property that's true for all relation instances. Avoids
     * reliance on the `instanceof` operator.
     */
    isRelation: true
});

module.exports = Relation;
<commit_msg>Fix misindentation<commit_after>/**
 * @class Relation
 *
 * In graph terminology a relation represents a directed edge, a
 * reference from one asset to another. For the purpose of being able
 * to treat all relations equally, there's a subclass for each
 * supported relation type, encapsulating the details of how to
 * retrieve, update, and (optionally) inline the asset being pointed
 * to.
 *
 * These are some examples of included subclasses:
 *
 *    - `relations.HtmlAnchor`         An anchor tag in an HTML document `<a href='...'>`.
 *    - `relations.HtmlImage`          An `<img src='...'>` tag in an HTML document.
 *    - `relations.CssImport`          An `@import` declaration in a CSS asset.
 *    - `relations.CacheManifestEntry` A line in a cache manifest.
 */
const _ = require('lodash');
const extendDefined = require('../util/extendDefined');
const urlTools = require('urltools');
const urlModule = require('url');

/**
 * new Relation(options)
 * =====================
 *
 * Create a new Relation instance. For existing assets the
 * instantiation of relations happens automatically if you use the
 * `populate` transform. You only need to create relations manually
 * when you need to introduce new ones.
 *
 * Note that the base Relation class should be considered
 * abstract. Please instantiate the appropriate subclass.
 *
 * Options:
 *
 *  - `from` The source asset of the relation.
 *  - `to`   The target asset of the relation, or an asset configuration
 *           object if the target asset hasn't yet been resolved and created.
 */
class Relation {
    constructor(config) {
        if (config.hrefType) {
            this._hrefType = config.hrefType;
            config.hrefType = undefined;
        }
        if (config.to) {
            this._to = config.to;
            config.to = undefined;
        }
        extendDefined(this, config);
        this.id = '' + _.uniqueId();
    }

    /**
     * relation.from (Asset)
     * =====================
     *
     * The source asset of the relation.
     */

    /**
     * relation.to (Asset)
     * ===================
     *
     * The target asset of the relation.
     */
    get to() {
        return this._to;
    }

    set to(to) {
        this._to = this.from.assetGraph.addAsset(to, this);
        this.refreshHref();
    }

    /**
     * relation.href (getter/setter)
     * =============================
     *
     * Get or set the href of the relation. The relation must be
     * attached to an asset.
     *
     * What is actually retrieved or updated depends on the relation
     * type. For `HtmlImage` the `src` attribute of the HTML element
     * is changed, for `CssImport` the parsed representation of
     * the @import rule is updated, etc.
     *
     * Most of the time you don't need to think about this property,
     * as the href is automatically updated when the url of the source
     * or target asset is changed, or an intermediate asset is
     * inlined.
     *
     * @api public
     */

    /**
     * relation.fragment (getter/setter)
     * =================================
     *
     * Get or set the fragment identifier part of the href of the relation.
     */
    get fragment() {
        const href = this.href;
        if (typeof href === 'string') {
            const indexOfFragment = href.indexOf('#');
            if (indexOfFragment === -1) {
                return '';
            } else {
                return href.substr(indexOfFragment);
            }
        }
    }

    set fragment(fragment) {
        if (fragment && !/^#/.test(fragment)) {
            throw new Error('The fragment must begin with a # or be empty');
        }
        const href = this.href;
        if (href) {
            let currentHrefWithoutFragment;
            const indexOfFragment = href.indexOf('#');
            if (indexOfFragment === -1) {
                currentHrefWithoutFragment = href;
            } else {
                currentHrefWithoutFragment = href.substr(0, indexOfFragment);
            }
            this.href = currentHrefWithoutFragment + (fragment || '');
            this.from.markDirty();
        }
    }

    /**
     * relation.refreshHref
     * ====================
     *
     * Update `href` of a relation to make sure it points at the
     * current url of its target asset.
     *
     * It's not necessary to call this function manually as long as
     * the source and target assets of the relation have only been
     * moved by having their `url` property changed (the recommended
     * way), but some transforms will need this after some low-level
     * surgery, such as attaching an existing relation to a different
     * asset.
     *
     * @return {Relation} The relation itself (chaining-friendly).
     * @api public
     */
    refreshHref() {
        const hrefType = this.hrefType;
        if (hrefType === 'inline') {
            this.inline();
        } else {
            const currentHref = this.href;
            if (/^#/.test(currentHref)) {
                // Pure fragment url, nothing to do
                return;
            }
            const targetUrl = this.to.url;
            const assetGraph = this.from.assetGraph;
            const canonical = this.canonical;
            let href;
            if (hrefType === 'rootRelative' && !canonical) {
                href = urlTools.buildRootRelativeUrl(this.baseUrl, targetUrl, assetGraph.root);
            } else if (hrefType === 'relative' && !canonical) {
                href = urlTools.buildRelativeUrl(this.baseUrl, targetUrl);
            } else if (hrefType === 'protocolRelative') {
                href = urlTools.buildProtocolRelativeUrl(this.baseUrl, targetUrl);
            } else {
                // Absolute
                href = targetUrl;
            }
            // Hack: Avoid adding index.html to an href pointing at file://.../index.html if it's not already there:
            if (/^file:\/\/.*\/index\.html(?:[?#]|$)/.test(targetUrl) && !/(?:^|\/)index\.html(?:[?#]:|$)/.test(this.href)) {
                href = href.replace(/(^|\/)index\.html(?=[?#]|$)/, '$1');
            }
            if (canonical) {
                href = href.replace(assetGraph.root, assetGraph.canonicalRoot);
            }
            const currentFragment = this.fragment;
            if (currentFragment) {
                href += currentFragment;
            }
            if (currentHref !== href) {
                this.href = href;
                this.from.markDirty();
            }
        }
        return this;
    }

    get crossorigin() {
        const fromUrl = this.from.nonInlineAncestor.url;
        const toUrl = this.to.url;
        if (!toUrl) {
            // Inline
            return false;
        }
        if (this.canonical) {
            return false;
        }
        const fromUrlObj = urlModule.parse(fromUrl);
        const toUrlObj = urlModule.parse(urlModule.resolve(fromUrl, toUrl));
        if (fromUrlObj.protocol !== toUrlObj.protocol || fromUrlObj.hostname !== toUrlObj.hostname) {
            return true;
        }
        const fromPort = fromUrlObj.port ? parseInt(fromUrlObj.port, 10) : {'http:': 80, 'https:': 443}[fromUrlObj.protocol];
        const toPort = toUrlObj.port ? parseInt(toUrlObj.port, 10) : {'http:': 80, 'https:': 443}[toUrlObj.protocol];
        return fromPort !== toPort;
    }

    get canonical() {
        if (typeof this._canonical === 'undefined') {
            let canonical = false;

            if (this.href && this.from && this.from.assetGraph && this.from.assetGraph.canonicalRoot) {
                const canonicalRootObj = urlModule.parse(this.from.assetGraph.canonicalRoot, false, true);
                const hrefObj = urlModule.parse(this.href, false, true);

                canonical = hrefObj.slashes === true &&
                    ['http:', 'https:', null].includes(hrefObj.protocol) &&
                    canonicalRootObj.host === hrefObj.host &&
                    hrefObj.path.startsWith(canonicalRootObj.path) &&
                    (canonicalRootObj.protocol === hrefObj.protocol || canonicalRootObj.protocol === null);
            }

            this._canonical = canonical;
        }

        return this._canonical;
    }

    set canonical(isCanonical) {
        if (this.from && this.from.assetGraph && this.from.assetGraph.canonicalRoot) {
            isCanonical = !!isCanonical;
            if (this._canonical !== isCanonical) {
                this._canonical = isCanonical;

                if (!isCanonical && (this._hrefType === 'absolute' || this._hrefType !== 'protocolRelative')) {
                    // We're switching to non-canonical mode. Degrade the href type
                    // to rootRelative so we won't issue absolute file:// urls
                    // This is based on guesswork, though.
                    this._hrefType = 'rootRelative';
                }
                this.refreshHref();
            }
        }
    }

    get baseUrl() {
        const nonInlineAncestor = this.from.nonInlineAncestor;
        return nonInlineAncestor && nonInlineAncestor.url;
    }

    /**
     * relation.hrefType (getter/setter)
     * =================================
     *
     * Either 'absolute', 'rootRelative', 'protocolRelative', or 'relative'. Decides what "degree" of
     * relative url refreshHref tries to issue.
     */

    get hrefType() {
        const to = this.to;
        if (to.isInline && to !== this.from) {
            return 'inline';
        }
        if (!this._hrefType) {
            const href = (this.href || '').trim();
            if (/^\/\//.test(href)) {
                this._hrefType = 'protocolRelative';
            } else if (/^\//.test(href)) {
                this._hrefType = 'rootRelative';
            } else if (/^[a-z\+]+:/i.test(href)) {
                this._hrefType = 'absolute';
            } else {
                this._hrefType = 'relative';
            }
        }
        return this._hrefType;
    }

    set hrefType(hrefType) {
        if (hrefType === 'inline') {
            this.inline();
        } else if (hrefType !== this._hrefType) {
            this._hrefType = hrefType;
            this.refreshHref();
        }
    }

    /**
     * relation.inline()
     * =================
     *
     * Inline the relation. This is only supported by certain relation
     * types and will produce different results depending on the type
     * (`data:` url, inline script, inline stylesheet...).
     *
     * Will make a clone of the target asset if it has more incoming
     * relations than this one.
     *
     * @return {Relation} The relation itself (chaining-friendly).
     * @api public
     */
    inline() {
        if (this.to.incomingRelations.filter(r => r._to !== r.from).length > 1) {
            // This isn't the only incoming relation to the asset, clone before inlining.
            this.to.clone(this);
        }
        this.to.incomingInlineRelation = this;
        if (!this.to.isInline) {
            this.to.url = null;
        }
        return this;
    }

    /**
     * relation.attach(position[, adjacentRelation])
     * ====================================================
     *
     * Attaches the relation to an asset.
     *
     * The ordering of certain relation types is significant
     * (`HtmlScript`, for instance), so it's important that the order
     * isn't scrambled in the indices. Therefore the caller must
     * explicitly specify a position at which to insert the object.
     *
     * @param {Asset} asset The asset to attach the relation to.
     * @param {String} position "first", "last", "before", or "after".
     * @param {Relation} adjacentRelation The adjacent relation, mandatory if the position is "before" or "after".
     * @return {Relation} The relation itself (chaining-friendly).
     * @api public
     */
    attach(position, adjacentRelation) {
        this.from.markDirty();
        this.addToOutgoingRelations(position, adjacentRelation);
        if (this.to && this.to.url) {
            this.refreshHref();
        }
        return this;
    }

    addToOutgoingRelations(position, adjacentRelation) {
        const outgoingRelations = this.from.outgoingRelations;
        const existingIndex = outgoingRelations.indexOf(this);
        if (existingIndex !== -1) {
            outgoingRelations.splice(existingIndex, 1);
        }
        if (position === 'last') {
            outgoingRelations.push(this);
        } else if (position === 'first') {
            outgoingRelations.unshift(this);
        } else if (position === 'before' || position === 'after') { // Assume 'before' or 'after'
            if (!adjacentRelation || !adjacentRelation.isRelation) {
                throw new Error(`addRelation: Adjacent relation is not a relation: ${adjacentRelation}`);
            }
            const i = outgoingRelations.indexOf(adjacentRelation) + (position === 'after' ? 1 : 0);
            if (i === -1) {
                throw new Error(`addRelation: Adjacent relation ${adjacentRelation.toString()} is not among the outgoing relations of ${this.urlOrDescription}`);
            }
            outgoingRelations.splice(i, 0, this);
        } else {
            throw new Error(`addRelation: Illegal 'position' argument: ${position}`);
        }
    }

    /**
     * relation.detach()
     * =================
     *
     * Detaches the relation from the asset it is currently attached
     * to. If the relation is currently part of a graph, it will
     * removed from it.
     *
     * Detaching implies that the tag/statement/declaration
     * representing the relation is physically removed from the
     * referring asset. Not all relation types support this.
     *
     * @return {Relation} The relation itself (chaining-friendly).
     * @api public
     */
    detach() {
        this.from.markDirty();
        this.remove();
        return this;
    }

    /**
     * relation.remove()
     * =================
     *
     * Removes the relation from the graph it's currently part
     * of. Doesn't detach the relation (compare with
     * `relation.detach()`).
     *
     * @return {Relation} The relation itself (chaining-friendly).
     * @api public
     */
    remove() {
        this.from.removeRelation(this);
        return this;
    }

    /**
     * relation.toString()
     * ===================
     *
     * Get a brief text containing the type, id of the relation. Will
     * also contain the `.toString()` of the relation's source and
     * target assets if available.
     *
     * @return {String} The string, eg. "[HtmlAnchor/141: [Html/40 file:///foo/bar/index.html] => [Html/76 file:///foo/bar/otherpage.html]]"
     * @api public
     */
    toString() {
        return '[' + this.type + '/' + this.id + ': ' + ((this.from && this.to) ? this.from.toString() + ' => ' + (this.to.isAsset ? this.to.toString() : this.to.url || this.to.type || '?') : 'unattached') + ']';
    }
};

Object.assign(Relation.prototype, {
    /**
     * relation.isRelation (boolean)
     * =============================
     *
     * Property that's true for all relation instances. Avoids
     * reliance on the `instanceof` operator.
     */
    isRelation: true
});

module.exports = Relation;
<|endoftext|>


<commit_before>'use strict';

var _ = require('lodash');
var AttributeTrigger = require('../util/attribute_trigger');
var property = require('../util/property').fn;
var inflection = require('../util/inflection');

_.str = require('underscore.string');

var BoundQuery = require('../db/query/bound');

var HasManyRelationQuery = BoundQuery.extend({
  _create: function(model, relatedModel, options) {
    this._super();
    this._model = model;
    this._relatedModel = relatedModel;
    this._options = options;
  },

  _take: function(orig) {
    this._super(orig);
    this._model = orig._model;
    this._relatedModel = orig._relatedModel;
  },

  create: function() {
    var options = this._options;
    var modelClass = this._relatedModel;
    var result = modelClass.create.apply(modelClass, arguments);
    // TODO: what to do about inverse id? set it or no?
    result[options.inverse + 'Id'] = this._model.id;
    result[options.inverse] = this._model;
    return result;
  },

  clear: function() { return this.delete.apply(this, arguments); }
});
HasManyRelationQuery.reopenClass({ __name__: 'HasManyRelationQuery' });

/**
 * The has many property for models.
 *
 * Documentation forthcoming.
 *
 * For example:
 *
 *     var User = Model.extend({
 *       articles: hasMany('articles')
 *     });
 *
 * @since 1.0
 * @public
 * @constructor HasMany
 * @extends Property
 */
var HasMany = AttributeTrigger.extend({
  init: function(relatedModel, options) {
    this._relatedModel = relatedModel;
    this._options = options || {};
  },

  invoke: function(name, reopen/*, prototype*/) {
    var methods = {};
    var plural = name;
    var pluralCapitalized = _.str.capitalize(plural);
    var singular = inflection.singularize(name);
    var singularCapitalized = _.str.capitalize(singular);
    var relatedModel = this._relatedModel;
    var objects = relatedModel.objects;
    var relation = singular + 'Objects';
    var relationPrivate = '_' + singular + 'Objects';
    var options = this._options;
    var foreignKey = options.foreignKey;
    if (!foreignKey) {
      throw new Error('Not yet calculating foreign keys automatically');
    }
    var primaryKey = this._options.primaryKey;
    if (!primaryKey) {
      throw new Error('Not yet calculating primary keys automatically');
    }
    var inverse = this._options.inverse;
    if (!inverse) {
      throw new Error('Not yet calculating inverse automatically');
    }

    methods[plural] = property(function() {
      // TODO: make this a property that throws or has data in it
    });
    methods[relation] = property(function() {
      if (!this[relationPrivate]) {
        var where = {};
        where[foreignKey] = this[primaryKey];
        // TODO: remove use of private method
        this[relationPrivate] =
          objects.where(where)._spawn(HasManyRelationQuery,
            [this, relatedModel, options]);
      }
      return this[relationPrivate];
    });
    methods['create' + singularCapitalized] = function() {
      return this[relation].create.apply(this[relation], arguments);
    };
    methods['add' + singularCapitalized] =
    methods['add' + pluralCapitalized] = function() {
      return this[relation].add.apply(this[relation], arguments);
    };
    methods['remove' + singularCapitalized] =
    methods['remove' + pluralCapitalized] = function() {
      return this[relation].remove.apply(this[relation], arguments);
    };
    methods['clear' + pluralCapitalized] = function() {
      return this[relation].clear.apply(this[relation], arguments);
    };

    reopen(methods);
  }
});

module.exports = HasMany.reopenClass({ __name__: 'HasMany' });
<commit_msg>Setting foreign key.<commit_after>'use strict';

var _ = require('lodash');
var AttributeTrigger = require('../util/attribute_trigger');
var property = require('../util/property').fn;
var inflection = require('../util/inflection');

_.str = require('underscore.string');

var BoundQuery = require('../db/query/bound');

var HasManyRelationQuery = BoundQuery.extend({
  _create: function(model, relatedModel, options) {
    this._super();
    this._model = model;
    this._relatedModel = relatedModel;
    this._options = options;
  },

  _take: function(orig) {
    this._super(orig);
    this._model = orig._model;
    this._relatedModel = orig._relatedModel;
  },

  create: function() {
    var options = this._options;
    var modelClass = this._relatedModel;
    var result = modelClass.create.apply(modelClass, arguments);
    result[_.str.camelize(options.foreignKey)] = this._model.id;
    result[options.inverse] = this._model;
    return result;
  },

  clear: function() { return this.delete.apply(this, arguments); }
});
HasManyRelationQuery.reopenClass({ __name__: 'HasManyRelationQuery' });

/**
 * The has many property for models.
 *
 * Documentation forthcoming.
 *
 * For example:
 *
 *     var User = Model.extend({
 *       articles: hasMany('articles')
 *     });
 *
 * @since 1.0
 * @public
 * @constructor HasMany
 * @extends Property
 */
var HasMany = AttributeTrigger.extend({
  init: function(relatedModel, options) {
    this._relatedModel = relatedModel;
    this._options = options || {};
  },

  invoke: function(name, reopen/*, prototype*/) {
    var methods = {};
    var plural = name;
    var pluralCapitalized = _.str.capitalize(plural);
    var singular = inflection.singularize(name);
    var singularCapitalized = _.str.capitalize(singular);
    var relatedModel = this._relatedModel;
    var objects = relatedModel.objects;
    var relation = singular + 'Objects';
    var relationPrivate = '_' + singular + 'Objects';
    var options = this._options;
    var foreignKey = options.foreignKey;
    if (!foreignKey) {
      throw new Error('Not yet calculating foreign keys automatically');
    }
    var primaryKey = this._options.primaryKey;
    if (!primaryKey) {
      throw new Error('Not yet calculating primary keys automatically');
    }
    var inverse = this._options.inverse;
    if (!inverse) {
      throw new Error('Not yet calculating inverse automatically');
    }

    methods[plural] = property(function() {
      // TODO: make this a property that throws or has data in it
    });
    methods[relation] = property(function() {
      if (!this[relationPrivate]) {
        var where = {};
        where[foreignKey] = this[primaryKey];
        // TODO: remove use of private method
        this[relationPrivate] =
          objects.where(where)._spawn(HasManyRelationQuery,
            [this, relatedModel, options]);
      }
      return this[relationPrivate];
    });
    methods['create' + singularCapitalized] = function() {
      return this[relation].create.apply(this[relation], arguments);
    };
    methods['add' + singularCapitalized] =
    methods['add' + pluralCapitalized] = function() {
      return this[relation].add.apply(this[relation], arguments);
    };
    methods['remove' + singularCapitalized] =
    methods['remove' + pluralCapitalized] = function() {
      return this[relation].remove.apply(this[relation], arguments);
    };
    methods['clear' + pluralCapitalized] = function() {
      return this[relation].clear.apply(this[relation], arguments);
    };

    reopen(methods);
  }
});

module.exports = HasMany.reopenClass({ __name__: 'HasMany' });
<|endoftext|>


<commit_before>'use strict';

var
  path = require('path'),
  utils = require('./util.js'),
  _ = require('lodash'),
  xmlWriter = require('./xmlWriter.js'),
  screenshotSaver = require('./screenshotSaver.js'),
  Q = require('q');

function normalizeMetadataDetails(details) {
  if (_.isUndefined(details)) {
    return [];
  }
  if (_.isArray(details) || !_.isObject(details)) {
    throw new Error('Step metadata details must be an object!');
  }

  var mappedDetails = [];
  _.forEach(details, function (value, key) {
    mappedDetails.push({key: key, value: value});
  });

  return {entry: mappedDetails};
}

/**
 * we save the step counter on the jasmine suite object.
 * this method returns the increased step counter.
 */
function increaseAndGetStepCounter() {
  var spec = jasmine.getEnv().currentSpec;
  if (utils.isDefined(spec.scenariooStepCounter)) {
    spec.scenariooStepCounter++;
  } else {
    spec.scenariooStepCounter = 0;
  }
  return spec.scenariooStepCounter;
}

/**
 * returns an object containing information about the current scenario
 */
function getCurrentScenario() {
  var spec , suite;
  spec = jasmine.getEnv().currentSpec;
  suite = spec.suite;

  return {
    useCaseName: suite.description,
    scenarioName: spec.description,
    stepCounter: increaseAndGetStepCounter()
  }
}

/**
 * writes scenario.xml to the configured output folder
 */
function writeScenarioXml(currentScenario, absScenarioPath) {
  var data = {
    name: utils.getSafeForFileName(currentScenario.scenarioName),
    description: currentScenario.scenarioDescription,
    status: currentScenario.status
  };
  return xmlWriter.writeXmlFile('scenario', data, path.join(absScenarioPath, 'scenario.xml'));
}

/**
 * writes useCase.xml to the configured output folder
 */
function writeUseCaseXml(useCase, absUseCasePath) {
  var data = {
    name: utils.getSafeForFileName(useCase.name),
    description: useCase.description,
    status: useCase.status,
    details: {}
  };
  return xmlWriter.writeXmlFile('useCase', data, path.join(absUseCasePath, 'usecase.xml'));
}

function getStepDataFromWebpage() {

  var deferred = Q.defer();

  browser
    .getCurrentUrl()
    .then(function (currentUrl) {
      element(by.css('body')).getOuterHtml().then(function (pageHtmlSource) {
        deferred.resolve({url: currentUrl, source: pageHtmlSource});
      }, deferred.reject);
    }, deferred.reject);

  return deferred.promise;
}

/**
 * writes step xml file (000.xml, 001.xml, etc.)
 */
function writeStepXml(stepName, currentScenario, absScenarioPath, details) {
  var deferred = Q.defer();

  getStepDataFromWebpage()
    .then(function (browserData) {
      var currentStepCounter = utils.leadingZeros(currentScenario.stepCounter);
      var stepData = {
        page: {
          name: utils.sanitizeIdentifier(browserData.url)
        },
        stepDescription: {
          index: currentScenario.stepCounter,
          title: stepName,
          screenshotFileName: currentStepCounter + '.png',
          details: {}
        },
        html: {
          htmlSource: browserData.source
        },
        metadata: {
          visibleText: '',
          details: normalizeMetadataDetails(details)
        }
      };

      var xmlFileName = path.join(absScenarioPath, 'steps', currentStepCounter + '.xml');

      xmlWriter
        .writeXmlFile('step', stepData, xmlFileName)
        .then(function () {
          deferred.resolve(stepData);
        }, deferred.reject);
    }, deferred.reject)
    .catch(function (err) {
      deferred.reject(err);
    });

  return deferred.promise;
}

var ScenarioDocuWriter = {

  targetDir: '',

  buildOutputDir: undefined,

  start: function (branch, build, scenariooTargetDirectory) {
    this.branch = branch;
    this.branch.name = utils.getSafeForFileName(this.branch.name);
    this.targetDir = scenariooTargetDirectory;
    build.name = utils.getSafeForFileName(build.name);

    // generate directories and write branch.xml
    this.buildOutputDir = path.join(this.targetDir, this.branch.name, build.name);
    return xmlWriter.writeXmlFile('branch', this.branch, path.resolve(path.join(this.targetDir, this.branch.name), 'branch.xml'));
  },

  saveBuild: function (build) {
    this.build = build;
    this.build.name = utils.getSafeForFileName(this.build.name);
    return xmlWriter.writeXmlFile('build', this.build, path.join(this.buildOutputDir, 'build.xml'));
  },

  /**
   * invoked by the jasmine reporter
   */
  saveUseCase: function (useCase) {
    if (!utils.isDefined(this.buildOutputDir)) {
      throw 'cannot save use case. No outputDirectory specified. docuWriter.start(branch, build, targetDir) not invoked?';
    }

    var absUseCasePath = path.resolve(this.buildOutputDir,
      utils.getSafeForFileName(useCase.name));
    return writeUseCaseXml(useCase, absUseCasePath);
  },

  /**
   * invoked by the jasmine reporter
   */
  saveScenario: function (currentScenario) {
    if (!utils.isDefined(this.buildOutputDir)) {
      throw 'cannot save use case. No outputDirectory specified. docuWriter.start(branch, build, targetDir) not invoked?';
    }

    var absScenarioPath = path.resolve(this.buildOutputDir,
      utils.getSafeForFileName(currentScenario.useCaseName),
      utils.getSafeForFileName(currentScenario.scenarioName));

    return writeScenarioXml(currentScenario, absScenarioPath);
  },

  /**
   * to be invoked by your e2e tests (protractor, jasmine)
   */
  saveStep: function (stepName, details) {
    if (!utils.isDefined(this.buildOutputDir)) {
      throw 'cannot save use case. No outputDirectory specified. docuWriter.start(branch, build, targetDir) not invoked?';
    }

    var currentScenario = getCurrentScenario();
    var absScenarioPath = path.resolve(this.buildOutputDir,
      utils.getSafeForFileName(currentScenario.useCaseName),
      utils.getSafeForFileName(currentScenario.scenarioName));

    var screenshotPromise = screenshotSaver.saveScreenshot(currentScenario, absScenarioPath);
    var stepXmlPromise = writeStepXml(stepName, currentScenario, absScenarioPath, details);
    return  Q.all([stepXmlPromise, screenshotPromise]);
  }

};

module.exports = ScenarioDocuWriter;
<commit_msg>fix eslint errors<commit_after>'use strict';

var
  path = require('path'),
  utils = require('./util.js'),
  _ = require('lodash'),
  xmlWriter = require('./xmlWriter.js'),
  screenshotSaver = require('./screenshotSaver.js'),
  Q = require('q');

function normalizeMetadataDetails(details) {
  if (_.isUndefined(details)) {
    return [];
  }
  if (_.isArray(details) || !_.isObject(details)) {
    throw new Error('Step metadata details must be an object!');
  }

  var mappedDetails = [];
  _.forEach(details, function (value, key) {
    mappedDetails.push({key: key, value: value});
  });

  return {entry: mappedDetails};
}

/**
 * we save the step counter on the jasmine suite object.
 * this method returns the increased step counter.
 */
function increaseAndGetStepCounter() {
  var spec = jasmine.getEnv().currentSpec;
  if (utils.isDefined(spec.scenariooStepCounter)) {
    spec.scenariooStepCounter++;
  } else {
    spec.scenariooStepCounter = 0;
  }
  return spec.scenariooStepCounter;
}

/**
 * returns an object containing information about the current scenario
 */
function getCurrentScenario() {
  var spec, suite;
  spec = jasmine.getEnv().currentSpec;
  suite = spec.suite;

  return {
    useCaseName: suite.description,
    scenarioName: spec.description,
    stepCounter: increaseAndGetStepCounter()
  };
}

/**
 * writes scenario.xml to the configured output folder
 */
function writeScenarioXml(currentScenario, absScenarioPath) {
  var data = {
    name: utils.getSafeForFileName(currentScenario.scenarioName),
    description: currentScenario.scenarioDescription,
    status: currentScenario.status
  };
  return xmlWriter.writeXmlFile('scenario', data, path.join(absScenarioPath, 'scenario.xml'));
}

/**
 * writes useCase.xml to the configured output folder
 */
function writeUseCaseXml(useCase, absUseCasePath) {
  var data = {
    name: utils.getSafeForFileName(useCase.name),
    description: useCase.description,
    status: useCase.status,
    details: {}
  };
  return xmlWriter.writeXmlFile('useCase', data, path.join(absUseCasePath, 'usecase.xml'));
}

function getStepDataFromWebpage() {

  var deferred = Q.defer();

  browser
    .getCurrentUrl()
    .then(function (currentUrl) {
      element(by.css('body')).getOuterHtml().then(function (pageHtmlSource) {
        deferred.resolve({url: currentUrl, source: pageHtmlSource});
      }, deferred.reject);
    }, deferred.reject);

  return deferred.promise;
}

/**
 * writes step xml file (000.xml, 001.xml, etc.)
 */
function writeStepXml(stepName, currentScenario, absScenarioPath, details) {
  var deferred = Q.defer();

  getStepDataFromWebpage()
    .then(function (browserData) {
      var currentStepCounter = utils.leadingZeros(currentScenario.stepCounter);
      var stepData = {
        page: {
          name: utils.sanitizeIdentifier(browserData.url)
        },
        stepDescription: {
          index: currentScenario.stepCounter,
          title: stepName,
          screenshotFileName: currentStepCounter + '.png',
          details: {}
        },
        html: {
          htmlSource: browserData.source
        },
        metadata: {
          visibleText: '',
          details: normalizeMetadataDetails(details)
        }
      };

      var xmlFileName = path.join(absScenarioPath, 'steps', currentStepCounter + '.xml');

      xmlWriter
        .writeXmlFile('step', stepData, xmlFileName)
        .then(function () {
          deferred.resolve(stepData);
        }, deferred.reject);
    }, deferred.reject)
    .catch(function (err) {
      deferred.reject(err);
    });

  return deferred.promise;
}

var ScenarioDocuWriter = {

  targetDir: '',

  buildOutputDir: undefined,

  start: function (branch, build, scenariooTargetDirectory) {
    this.branch = branch;
    this.branch.name = utils.getSafeForFileName(this.branch.name);
    this.targetDir = scenariooTargetDirectory;
    build.name = utils.getSafeForFileName(build.name);

    // generate directories and write branch.xml
    this.buildOutputDir = path.join(this.targetDir, this.branch.name, build.name);
    return xmlWriter.writeXmlFile('branch', this.branch, path.resolve(path.join(this.targetDir, this.branch.name), 'branch.xml'));
  },

  saveBuild: function (build) {
    this.build = build;
    this.build.name = utils.getSafeForFileName(this.build.name);
    return xmlWriter.writeXmlFile('build', this.build, path.join(this.buildOutputDir, 'build.xml'));
  },

  /**
   * invoked by the jasmine reporter
   */
  saveUseCase: function (useCase) {
    if (!utils.isDefined(this.buildOutputDir)) {
      throw 'cannot save use case. No outputDirectory specified. docuWriter.start(branch, build, targetDir) not invoked?';
    }

    var absUseCasePath = path.resolve(this.buildOutputDir,
      utils.getSafeForFileName(useCase.name));
    return writeUseCaseXml(useCase, absUseCasePath);
  },

  /**
   * invoked by the jasmine reporter
   */
  saveScenario: function (currentScenario) {
    if (!utils.isDefined(this.buildOutputDir)) {
      throw 'cannot save use case. No outputDirectory specified. docuWriter.start(branch, build, targetDir) not invoked?';
    }

    var absScenarioPath = path.resolve(this.buildOutputDir,
      utils.getSafeForFileName(currentScenario.useCaseName),
      utils.getSafeForFileName(currentScenario.scenarioName));

    return writeScenarioXml(currentScenario, absScenarioPath);
  },

  /**
   * to be invoked by your e2e tests (protractor, jasmine)
   */
  saveStep: function (stepName, details) {
    if (!utils.isDefined(this.buildOutputDir)) {
      throw 'cannot save use case. No outputDirectory specified. docuWriter.start(branch, build, targetDir) not invoked?';
    }

    var currentScenario = getCurrentScenario();
    var absScenarioPath = path.resolve(this.buildOutputDir,
      utils.getSafeForFileName(currentScenario.useCaseName),
      utils.getSafeForFileName(currentScenario.scenarioName));

    var screenshotPromise = screenshotSaver.saveScreenshot(currentScenario, absScenarioPath);
    var stepXmlPromise = writeStepXml(stepName, currentScenario, absScenarioPath, details);
    return  Q.all([stepXmlPromise, screenshotPromise]);
  }

};

module.exports = ScenarioDocuWriter;
<|endoftext|>


<commit_before>var crypto = require('crypto');
var config = require('./../config/smart_pay.js').config;

module.exports = {

    generateHmac: function generateHmac(hmacKey,params) {

        if (typeof hmacKey === 'undefined' || hmacKey.length < 1) {
            throw new Error("hmacKey is missing or empty");
        }

        var orderedData = {};

        Object.keys(params).sort().forEach(function(key) {
            orderedData[key] = params[key];
        });

        var keys = [],
            values = [];

        for (var key in orderedData) {
            keys.push(key);
            values.push(orderedData[key].toString().replace(/:/g,'\\:'));
        }

        var signingString = keys.concat(values).join(":");

        var decodeHexString = function (hexString) {
            return hexString.replace(/([0-9A-Fa-f]{2})/g, function () {
                return String.fromCharCode(parseInt(arguments[1], 16))
            })
        };

        var hash = crypto.createHmac('sha256', decodeHexString(hmacKey)).update(signingString, 'utf-8').digest('base64');

        return hash;
    },

    testMode: function testMode () {

        return config.testMode;

    },

    requestUrl: function requestUrl () {

        //var testURL = 'https://ca-test.barclaycardsmartpay.com/ca/ca/skin/checkhmac.shtml';
        var testURL = 'https://test.barclaycardsmartpay.com/hpp/select.shtml';
        var liveURL = 'https://live.barclaycardsmartpay.com/hpp/select.shtml';

        return this.testMode ? testURL : liveURL;

    },

    getVerifiedStatus: function getVerifiedStatus (merchantSig, hmac) {
        if (merchantSig === hmac) return true;
    }

};<commit_msg>Change encryption syntax so it now works as expected in Node v6<commit_after>var crypto = require('crypto');
var config = require('./../config/smart_pay.js').config;

module.exports = {

    generateHmac: function generateHmac(hmacKey,params) {

        if (typeof hmacKey === 'undefined' || hmacKey.length < 1) {
            throw new Error("hmacKey is missing or empty");
        }

        var orderedData = {};

        Object.keys(params).sort().forEach(function(key) {
            orderedData[key] = params[key];
        });

        var keys = [],
            values = [];

        for (var key in orderedData) {
            keys.push(key);
            values.push(orderedData[key].toString().replace(/:/g,'\\:'));
        }

        var signingString = keys.concat(values).join(":");

        var hash = crypto.createHmac('sha256', new Buffer(hmacKey, 'hex')).update(new Buffer(signingString, 'utf-8')).digest('base64');

        return hash;
    },

    testMode: function testMode () {

        return config.testMode;

    },

    requestUrl: function requestUrl () {

        //var testURL = 'https://ca-test.barclaycardsmartpay.com/ca/ca/skin/checkhmac.shtml';
        var testURL = 'https://test.barclaycardsmartpay.com/hpp/select.shtml';
        var liveURL = 'https://live.barclaycardsmartpay.com/hpp/select.shtml';

        return this.testMode ? testURL : liveURL;

    },

    getVerifiedStatus: function getVerifiedStatus (merchantSig, hmac) {
        if (merchantSig === hmac) return true;
    }

};<|endoftext|>


<commit_before>/**
 *  Modified by Humbug, Inc.
 */

/**
 *  File: jquery.idle.js
 *  Title:  JQuery Idle.
 *  A dead simple jQuery plugin that executes a callback function if the user is idle.
 *  About: Author
 *  Henrique Boaventura (hboaventura@gmail.com).
 *  About: Version
 *  1.0.0
 *  About: License
 *  Copyright (C) 2012, Henrique Boaventura (hboaventura@gmail.com).
 *  MIT License:
 *  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
 *  - The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
 *  - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 **/

(function( $ ){
  $.fn.idle = function(options) {

    var defaults = {
      idle: 60000, //idle time in ms
      events: 'mousemove keydown DOMMouseScroll mousewheel mousedown touchstart touchmove', //events that will trigger the idle resetter
      onIdle: function(){}, //callback function to be executed after idle time
      onActive: function(){}, //callback function to be executed after back from idleness
      keepTracking: false //if you want to keep tracking user even after the first time, set this to true
    };

    var idle = false;

    var settings = $.extend( {}, defaults, options );
    var timerId;
    var elem = $(this);

    // We need this variable so that if the timer is canceled during
    // an event handler we're also listening to.  Otherwise, our
    // handler for that event might run even though we're supposed to
    // be canceled
    var canceled = false;

    var handler = function(e){
        if (canceled) {
            return;
        }
        if(idle){
            settings.onActive.call();
            idle = false;
        }

        resetTimeout();
    };

    var cancel = function() {
      elem.off(settings.events, handler);
      clearTimeout(timerId);
      canceled = true;
    }

    var resetTimeout = function() {
      idle = false;
      clearTimeout(timerId);
      createTimeout();
    }

    var createTimeout = function() {
      timerId = setTimeout(function(){
        idle = true;
        cancel();
        settings.onIdle.call();
        if(settings.keepTracking){
          timerId = createTimeout(settings);
        }
      }, settings.idle);
      control.timerId = timerId;
    }

    var control = {
        'cancel': cancel,
        'reset': resetTimeout,
    };

    createTimeout(settings);
    elem.on(settings.events, handler);

    return control;
  };
})( jQuery );
<commit_msg>jquery.idle.js: fix keepTracking option<commit_after>/**
 *  Modified by Humbug, Inc.
 */

/**
 *  File: jquery.idle.js
 *  Title:  JQuery Idle.
 *  A dead simple jQuery plugin that executes a callback function if the user is idle.
 *  About: Author
 *  Henrique Boaventura (hboaventura@gmail.com).
 *  About: Version
 *  1.0.0
 *  About: License
 *  Copyright (C) 2012, Henrique Boaventura (hboaventura@gmail.com).
 *  MIT License:
 *  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
 *  - The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
 *  - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 **/

(function( $ ){
  $.fn.idle = function(options) {

    var defaults = {
      idle: 60000, //idle time in ms
      events: 'mousemove keydown DOMMouseScroll mousewheel mousedown touchstart touchmove', //events that will trigger the idle resetter
      onIdle: function(){}, //callback function to be executed after idle time
      onActive: function(){}, //callback function to be executed after back from idleness
      keepTracking: false //if you want to keep tracking user even after the first time, set this to true
    };

    var idle = false;

    var settings = $.extend( {}, defaults, options );
    var timerId;
    var elem = $(this);

    // We need this variable so that if the timer is canceled during
    // an event handler we're also listening to.  Otherwise, our
    // handler for that event might run even though we're supposed to
    // be canceled
    var canceled = false;

    var handler = function(e){
        if (canceled) {
            return;
        }
        if(idle){
            settings.onActive.call();
            idle = false;
        }

        resetTimeout();
    };

    var cancel = function() {
      elem.off(settings.events, handler);
      clearTimeout(timerId);
      canceled = true;
    }

    var resetTimeout = function() {
      idle = false;
      clearTimeout(timerId);
      createTimeout();
    }

    var createTimeout = function() {
      timerId = setTimeout(function(){
        idle = true;
        cancel();
        settings.onIdle.call();
        if(settings.keepTracking) {
            // We want the reset to occur after this event has been
            // completely handled
            setTimeout(function () {
                elem.on(settings.events, handler);
                canceled = false;
                createTimeout(settings);
            }, 0);
        }
      }, settings.idle);
      control.timerId = timerId;
    }

    var control = {
        'cancel': cancel,
        'reset': resetTimeout,
    };

    createTimeout(settings);
    elem.on(settings.events, handler);

    return control;
  };
})( jQuery );
<|endoftext|>


<commit_before>// global vars (yuck)
grid = [0, 0, 0, 0, 0, 0, 0, 0, 0];
player = 1; // X=>1, O=>5
player_moves_first = true;
number_of_moves = 0;
buttons = []; // HTML buttons
score_you = 0;
score_comp = 0;
game_restart = false;

set_player_label();
init_board();

// draw HTML board and attach event listeners
function init_board() {
  // draw 3x3 board of buttons
  ttt = document.getElementById("tictactoe");
  size = parseInt(ttt.offsetWidth / 6);
  board = '
'

  // building HTML for board
  for (i = 0; i < 9; i++) {
    if (i % 3 == 0) board += '
';
    board += '<input type="button" style="-webkit-appearance: none; font-size: ' + parseInt(2 * size / 3).toString() + 'px; width: ' + size.toString() + 'px; height: ' + size.toString() + 'px;" id="cell-' + i.toString() + '" />	';
    if (i % 3 == 2) board += '
';
  }
  board += '
';
  ttt.innerHTML = board;

  // attach event listeners to buttons
  for (i = 0; i < 9; i++) {
    buttons[i] = document.getElementById('cell-' + i.toString());
    buttons[i].addEventListener("click", function(e) {
      mark_square(this.id);
    });
  }
}

// handle button clicking
function mark_square(nn) {
  // special case: just restart the game
  if (game_restart == true) {
    game_restart = false;
    new_game();
    set_player_label();
    return
  }

  // player marks a button
  n = parseInt(nn.split('-')[1]);
  if (grid[n] != 0) return;
  buttons[n].value = player == 1 ? 'X' : 'O';
  grid[n] = player;
  number_of_moves++;
  end_game_test(n);

  // if valid, the AI moves
  if (number_of_moves > 0 && number_of_moves < 8) {
    if (number_of_moves == 1) {
      if (player_moves_first) {
        ai_move();
      }
    } else {
      if (!game_restart) {
        ai_move();
      }
    }
  }
}

// test if game is over
function has_won() {
  var way = [0, 1, 2, 0, 3, 6, 0, 4, 8, 1, 4, 7, 2, 4, 6, 2, 5, 8, 3, 4, 5, 6, 7, 8];
  for (var i = 0; i < 24; i++)
    way[i] = grid[way[i]];
  for (i = 0; i < 24; i += 3) {
    if (way[i] == 0) continue;
    if (way[i] == way[i + 1] && way[i] == way[i + 2])
      return way[i];
  }
  return -1;
}

// report win/loss/draw in marquee
function handle_endgame(winner) {
  var txt = 'Draw Game';
  if (winner == 1 && player_moves_first) {
    txt = 'You won!';
  } else if (winner == 5 && !player_moves_first) {
    txt = 'You won!';
  } else if (winner > -1) txt = 'You lost.';
  document.getElementById('marquee').innerHTML = txt;

  update_scoreboard(winner);
  game_restart = true;
}

// test if the game is over, and handle that
function end_game_test(z) {
  if (has_won() == player) {
    handle_endgame(player);
  } else if (number_of_moves == 8) {
    handle_endgame(-1);
  } else {
    player = player == 1 ? 5 : 1;
  }
}

// for resetting game, but no on initial load
function new_game() {
  grid = [0, 0, 0, 0, 0, 0, 0, 0, 0];
  player = 1;
  number_of_moves = 0;
  for (var i = 0; i < buttons.length; i++) {
    buttons[i].value = ' ';
  }
  player_moves_first = !player_moves_first;
  if (!player_moves_first) ai_move();
}

// Mostly Stupid AI
function ai_move() {
  var center = 4;
  var corners = [0, 2, 6, 8];
  var edges = [1, 3, 5, 7];
  var x = 0;
  if (number_of_moves == 0) {
    x = corners[Math.floor(Math.random() * 4)];
  } else if (number_of_moves == 1) {
    if (grid[center] == 0) {
      x = center;
    } else {
      x = corners[Math.floor(Math.random() * 4)];
    }
  } else {
    // check if we can win
    x = find_win_posi(player);
    if (x == -1) { // we can't win
      // can the other player win?
      x = find_win_posi(player == 1 ? 5 : 1);
      if (x == -1) { // they can't
        // move randomly
        x = Math.floor(Math.random() * 9);
        while (grid[x] != 0) {
          x = Math.floor(Math.random() * 9);
        }
      }
    }
  }
  grid[x] = player;
  buttons[x].value = player == 1 ? 'X' : 'O';
  number_of_moves++;
  end_game_test(x);
}

/** find if a position on the board can be a win for the given player 'v'
    Note, this can be used to find a place to move, or a place that
    needs to be defended.
*/
function find_win_posi(v) {
  var posi = [0, 1, 2, 0, 3, 6, 0, 4, 8, 1, 4, 7, 2, 4, 6, 2, 5, 8, 3, 4, 5, 6, 7, 8];
  var way = [0, 1, 2, 0, 3, 6, 0, 4, 8, 1, 4, 7, 2, 4, 6, 2, 5, 8, 3, 4, 5, 6, 7, 8];
  for (var i = 0; i < 24; i++)
    way[i] = grid[way[i]];
  for (i = 0; i < 24; i += 3) {
    if ((way[i] + way[i + 1] + way[i + 2]) == 2 * v) {
      if (way[i] == 0) {
        return posi[i];
      } else if (way[i + 1] == 0) {
        return posi[i + 1];
      } else {
        return posi[i + 2];
      }
    }
  }
  return -1;
}

// reset player ID label
function set_player_label() {
  document.getElementById("marquee").innerHTML = player_moves_first ? 'You are X.' : 'You are O.';
}

// update scoreboard, given winner user
function update_scoreboard(pid) {
  if (pid <= 0) {
    score_you += 5;
    score_comp += 5;
  } else if ((player_moves_first && pid == 1) || (!player_moves_first && pid == 5)) {
    score_you += 10;
  } else {
    score_comp += 10;
  }
  document.getElementById("score_you").innerHTML = score_you.toString();
  document.getElementById('score_comp').innerHTML = score_comp.toString();
}
<commit_msg>trying to solve iOS problems<commit_after>// global vars (yuck)
grid = [0, 0, 0, 0, 0, 0, 0, 0, 0];
player = 1; // X=>1, O=>5
player_moves_first = true;
number_of_moves = 0;
buttons = []; // HTML buttons
score_you = 0;
score_comp = 0;
game_restart = false;

set_player_label();
init_board();

// draw HTML board and attach event listeners
function init_board() {
  // draw 3x3 board of buttons
  ttt = document.getElementById("tictactoe");
  size = parseInt(ttt.offsetWidth / 6);
  board = '
'

  // building HTML for board
  for (i = 0; i < 9; i++) {
    if (i % 3 == 0) board += '
';
    board += '<input type="button" style="-webkit-appearance: none; margin: 0px; padding: 0px; font-size: ' + parseInt(2 * size / 3).toString() + 'px; width: ' + size.toString() + 'px; height: ' + size.toString() + 'px;" id="cell-' + i.toString() + '" value=" " />	';
    if (i % 3 == 2) board += '
';
  }
  board += '
';
  ttt.innerHTML = board;

  // attach event listeners to buttons
  for (i = 0; i < 9; i++) {
    buttons[i] = document.getElementById('cell-' + i.toString());
    buttons[i].addEventListener("click", function(e) {
      mark_square(this.id);
    });
  }
}

// handle button clicking
function mark_square(nn) {
  // special case: just restart the game
  if (game_restart == true) {
    game_restart = false;
    new_game();
    set_player_label();
    return
  }

  // player marks a button
  n = parseInt(nn.split('-')[1]);
  if (grid[n] != 0) return;
  buttons[n].value = player == 1 ? 'X' : 'O';
  grid[n] = player;
  number_of_moves++;
  end_game_test(n);

  // if valid, the AI moves
  if (number_of_moves > 0 && number_of_moves < 8) {
    if (number_of_moves == 1) {
      if (player_moves_first) {
        ai_move();
      }
    } else {
      if (!game_restart) {
        ai_move();
      }
    }
  }
}

// test if game is over
function has_won() {
  var way = [0, 1, 2, 0, 3, 6, 0, 4, 8, 1, 4, 7, 2, 4, 6, 2, 5, 8, 3, 4, 5, 6, 7, 8];
  for (var i = 0; i < 24; i++)
    way[i] = grid[way[i]];
  for (i = 0; i < 24; i += 3) {
    if (way[i] == 0) continue;
    if (way[i] == way[i + 1] && way[i] == way[i + 2])
      return way[i];
  }
  return -1;
}

// report win/loss/draw in marquee
function handle_endgame(winner) {
  var txt = 'Draw Game';
  if (winner == 1 && player_moves_first) {
    txt = 'You won!';
  } else if (winner == 5 && !player_moves_first) {
    txt = 'You won!';
  } else if (winner > -1) txt = 'You lost.';
  document.getElementById('marquee').innerHTML = txt;

  update_scoreboard(winner);
  game_restart = true;
}

// test if the game is over, and handle that
function end_game_test(z) {
  if (has_won() == player) {
    handle_endgame(player);
  } else if (number_of_moves == 8) {
    handle_endgame(-1);
  } else {
    player = player == 1 ? 5 : 1;
  }
}

// for resetting game, but no on initial load
function new_game() {
  grid = [0, 0, 0, 0, 0, 0, 0, 0, 0];
  player = 1;
  number_of_moves = 0;
  for (var i = 0; i < buttons.length; i++) {
    buttons[i].value = ' ';
  }
  player_moves_first = !player_moves_first;
  if (!player_moves_first) ai_move();
}

// Mostly Stupid AI
function ai_move() {
  var center = 4;
  var corners = [0, 2, 6, 8];
  var edges = [1, 3, 5, 7];
  var x = 0;
  if (number_of_moves == 0) {
    x = corners[Math.floor(Math.random() * 4)];
  } else if (number_of_moves == 1) {
    if (grid[center] == 0) {
      x = center;
    } else {
      x = corners[Math.floor(Math.random() * 4)];
    }
  } else {
    // check if we can win
    x = find_win_posi(player);
    if (x == -1) { // we can't win
      // can the other player win?
      x = find_win_posi(player == 1 ? 5 : 1);
      if (x == -1) { // they can't
        // move randomly
        x = Math.floor(Math.random() * 9);
        while (grid[x] != 0) {
          x = Math.floor(Math.random() * 9);
        }
      }
    }
  }
  grid[x] = player;
  buttons[x].value = player == 1 ? 'X' : 'O';
  number_of_moves++;
  end_game_test(x);
}

/** find if a position on the board can be a win for the given player 'v'
    Note, this can be used to find a place to move, or a place that
    needs to be defended.
*/
function find_win_posi(v) {
  var posi = [0, 1, 2, 0, 3, 6, 0, 4, 8, 1, 4, 7, 2, 4, 6, 2, 5, 8, 3, 4, 5, 6, 7, 8];
  var way = [0, 1, 2, 0, 3, 6, 0, 4, 8, 1, 4, 7, 2, 4, 6, 2, 5, 8, 3, 4, 5, 6, 7, 8];
  for (var i = 0; i < 24; i++)
    way[i] = grid[way[i]];
  for (i = 0; i < 24; i += 3) {
    if ((way[i] + way[i + 1] + way[i + 2]) == 2 * v) {
      if (way[i] == 0) {
        return posi[i];
      } else if (way[i + 1] == 0) {
        return posi[i + 1];
      } else {
        return posi[i + 2];
      }
    }
  }
  return -1;
}

// reset player ID label
function set_player_label() {
  document.getElementById("marquee").innerHTML = player_moves_first ? 'You are X.' : 'You are O.';
}

// update scoreboard, given winner user
function update_scoreboard(pid) {
  if (pid <= 0) {
    score_you += 5;
    score_comp += 5;
  } else if ((player_moves_first && pid == 1) || (!player_moves_first && pid == 5)) {
    score_you += 10;
  } else {
    score_comp += 10;
  }
  document.getElementById("score_you").innerHTML = score_you.toString();
  document.getElementById('score_comp').innerHTML = score_comp.toString();
}
<|endoftext|>


<commit_before>if (typeof(GE.Img) !== 'object')
	GE.Img = {};

GE.Img.AniImage = function(img, frames, loop, fps, reversed) {
	if (typeof(img) != 'undefined') {
		this.init(img, frames, loop, fps, reversed);
	}
};

GE.Img.AniImage.extend(GE.Img.Image);

GE.Img.AniImage.prototype.init = function(img, frames, loop, fps,
	reversed)
{
	GE.Img.AniImage.parent.init.call(this, img);
	this.fps = GE.ValueChecker.int(fps, 'fps');
	this.frames = GE.ValueChecker.int(frames, 'frames', 0);
	this.loop = GE.ValueChecker.bool(loop, 'loop');
	this.reversed = !!reversed || false;

	this.time_between_frames = 0;
	this.time_since_last_frame = 0;

	/* internal calcs */
	if (this.frames > 0)
		this.frame_height = this.image.height / this.frames;
	else
		this.frame_height = this.image.height;

	if (reversed) // if we're going backwards, start at the end..
		this.cur_frame = this.frames - 1;
	else
		this.cur_frame = 0;

	this.frame_width = this.image.width;
	this.time_between_frames = 1 / fps;
	this.time_since_last_frame = this.time_between_frames;

	this.height = this.frame_height;
	this.width = this.frame_width;
};

GE.Img.AniImage.prototype.draw_frame = function (context, x, y, game_time,
	frame_num)
{
	context.save();
	context.translate(x, y);

	if (this.flip_x) {
		context.save();
		context.scale(-1, 1);
		context.translate(-this.frame_width, 0);
	}

	context.drawImage(
		this.image,
		0, this.frame_height * frame_num,
		this.frame_width, this.frame_height,
		0, 0,
		this.frame_width, this.frame_height
	);

	if (this.flip_x) {
		context.restore();
	}

	context.restore();
}

GE.Img.AniImage.prototype.draw = function (context, x, y, game_time) {
	this.draw_frame(context, x, y, game_time, this.cur_frame);

	this.time_since_last_frame -= game_time;

	if (this.frames > 0) {
		if (this.time_since_last_frame <= 0) {
			this.time_since_last_frame = this.time_between_frames;

			if (this.loop ||
				(!this.reversed && this.cur_frame + 1 != this.frames)
				|| (this.reversed && this.cur_frame > 0))
			{
				if (this.reversed) {
					this.cur_frame--;
					if (this.cur_frame == 0)
						if (this.loop)
							this.cur_frame = this.frames - 1;
						else
							return false; // end of sequence

				}
				else {
					this.cur_frame++;
					if (this.cur_frame == this.frames)
						if (this.loop)
							this.cur_frame = 0;
						else
							return false; // end of sequence
				}
			}

		}
	}

	return true; // indicates more to come...
};

GE.Img.AniImage.prototype.get_frame_amout = function() {
	return this.frames;
};

GE.Img.AniImage.prototype.set_scale_factor = function(s) {
	this.scale_factor = GE.ValueChecker.float(s, 's');
	this.scaled_width = this.frame_width * s;
	this.scaled_height = this.frame_height * s;
};

GE.Img.AniImage.prototype.get_width = function() {
	return this.frame_width;
};

GE.Img.AniImage.prototype.get_height = function() {
	return this.frame_height;
};
<commit_msg>AniImages können nun mehrere Filmstreifen verwalten<commit_after>if (typeof(GE.Img) !== 'object')
	GE.Img = {};

GE.Img.AniImage = function(img, frames, loop, fps, reversed) {
	if (typeof(img) != 'undefined') {
		this.init(img, frames, loop, fps, reversed);
	}
};

GE.Img.AniImage.extend(GE.Img.Image);

/**
 * Initialisiert das Objekt
 *
 * @param Image img          Das Bild, das die Animationen enthält.
 * @param uint/uint[] frames Ist frames ein Array, dann bedeutet jedes Element
 *                           die Bildanzahl eines Filmstreifen, alle Streifen
 *                           werden nebeneinander erwartet.
 *                           Ist es ein Integer, gibt es nur einen Streifen.
 * @param bool loop          Ob geloopt werden darf oder one-shot.
 * @param uint fps           Frames per Seconds.
 * @param bool reversed      Ob die Animation rückwärts abgespielt werden soll.
 */
GE.Img.AniImage.prototype.init = function(img, frames, loop, fps,
	reversed)
{
	GE.Img.AniImage.parent.init.call(this, img);
	this.fps = GE.ValueChecker.int(fps, 'fps');
	if (frames instanceof Array) {
		for (var i = 0; i < frames.length; i++)
			GE.ValueChecker.int(frames[i], 'frames['+i+']', 0);
		this.frames = frames;
	}
	else {
		this.frames = [GE.ValueChecker.int(frames, 'frames', 0)];
	}
	this.loop = GE.ValueChecker.bool(loop, 'loop');
	this.reversed = reversed || false;

	this.time_between_frames = 0;
	this.time_since_last_frame = 0;

	this.cur_strip = 0;	// Filmstreifen, der abgespielt werden soll

	/* internal calcs */
	if (this.frames[this.cur_strip] > 0)
		this.frame_height = this.image.height / this.frames[this.cur_strip];
	else
		this.frame_height = this.image.height;

	if (reversed) // if we're going backwards, start at the end..
		this.cur_frame = this.frames - 1;
	else
		this.cur_frame = 0;

	this.frame_width = this.image.width / this.frames.length;
	this.time_between_frames = 1 / fps;
	this.time_since_last_frame = this.time_between_frames;

	this.height = this.frame_height;
	this.width = this.frame_width;
};

GE.Img.AniImage.prototype.draw_frame = function (context, x, y, game_time,
	frame_num)
{
	context.save();
	context.translate(x, y);

	if (this.flip_x) {
		context.save();
		context.scale(-1, 1);
		context.translate(-this.frame_width, 0);
	}

	context.drawImage(
		this.image,
		this.frame_width * this.cur_strip, this.frame_height * frame_num,
		this.frame_width, this.frame_height,
		0, 0,
		this.frame_width, this.frame_height
	);

	if (this.flip_x) {
		context.restore();
	}

	context.restore();
}

GE.Img.AniImage.prototype.draw = function (context, x, y, game_time) {
	this.draw_frame(context, x, y, game_time, this.cur_frame);

	this.time_since_last_frame -= game_time;

	if (this.frames[this.cur_strip] > 0) {
		if (this.time_since_last_frame <= 0) {
			this.time_since_last_frame = this.time_between_frames;

			if (this.loop ||
				(!this.reversed &&
					this.cur_frame + 1 != this.frames[this.cur_strip])
				|| (this.reversed && this.cur_frame > 0))
			{
				if (this.reversed) {
					this.cur_frame--;
					if (this.cur_frame == 0)
						if (this.loop)
							this.cur_frame = this.frames - 1;
						else
							return false; // end of sequence
				}
				else {
					this.cur_frame++;
					if (this.cur_frame == this.frames[this.cur_strip])
						if (this.loop)
							this.cur_frame = 0;
						else
							return false; // end of sequence
				}
			}
		}
	}

	return true; // indicates more to come...
};

GE.Img.AniImage.prototype.get_frame_amout = function() {
	return this.frames;
};

GE.Img.AniImage.prototype.set_scale_factor = function(s) {
	this.scale_factor = GE.ValueChecker.float(s, 's');
	this.scaled_width = this.frame_width * s;
	this.scaled_height = this.frame_height * s;
};

GE.Img.AniImage.prototype.get_width = function() {
	return this.frame_width;
};

GE.Img.AniImage.prototype.get_height = function() {
	return this.frame_height;
};

/**
 * Setzt den aktuellen Filmstreifen. Wechselt also die Filmrolle ;-)
 *
 * @param uint s Filmstreifennummer.
 */
GE.Img.AniImage.prototype.set_strip = function(strip) {
	this.cur_strip = GE.ValueChecker.int(
		strip, 'strip', 0, this.frames.length-1
	);
	this.cur_frame = (this.reversed) ? this.frames[this.cur_strip] : 0;
};
<|endoftext|>


<commit_before>var http = require("http")
var fs = require("fs")

http.createServer(function (req, res) {
  res.writeHead(200, {"Content-Type": "image/gif"})

  // In production, you'd want to use some sort of caching here.
  // Either via a module such as http://npm.im/lactate or a separate
  // tool such as Nginx or Varnish
  var file = fs.createReadStream("./stream-animation.gif")
    .pipe(res)

}).listen(1337, "127.0.0.1")
console.log("Server running at http://127.0.0.1:1337/")<commit_msg>syntax tweak<commit_after>var http = require("http")
var fs = require("fs")

http.createServer(function (req, res) {
  res.writeHead(200, {"Content-Type": "image/gif"})

  // In production, you'd want to use some sort of caching here.
  // Either via a module such as http://npm.im/lactate or a separate
  // tool such as Nginx or Varnish
  var file = fs.createReadStream("./stream-animation.gif")
  file.pipe(res)

}).listen(1337, "127.0.0.1")
console.log("Server running at http://127.0.0.1:1337/")<|endoftext|>