keep keep keep keep replace keep keep keep

const Service = require('../lib/Service')

const LANGS = ['css', 'sass', 'scss', 'less', 'styl', 'stylus']
const extractLoaderPath = require('mini-css-extract-plugin').loader

const LOADERS = {
  css: 'css',
  sass: 'sass',
  scss: 'sass',
  less: 'less',
  styl: 'stylus',
  stylus: 'stylus'
}

const genConfig = (pkg = {}, env) => {
  const prevEnv = process.env.NODE_ENV
  if (env) process.env.NODE_ENV = env
  const service = new Service('/', { pkg })
  service.init()
  const config = service.resolveWebpackConfig()
  process.env.NODE_ENV = prevEnv
  return config
}

const findRule = (config, lang, index = 3) => {
  const baseRule = config.module.rules.find(rule => {
    return rule.test.test(`.${lang}`)
  })
  // all CSS rules have 4 oneOf rules:
  // 0 - <style lang="module"> in Vue files
  // 1 - <style> in Vue files
  // 2 - *.modules.css imports from JS
  // 3 - *.css imports from JS
  return baseRule.oneOf[index]
}

const findLoaders = (config, lang, index) => {
  const rule = findRule(config, lang, index)
  if (!rule) {
    throw new Error(`rule not found for ${lang}`)
  }
  return rule.use.map(({ loader }) => loader.replace(/-loader$/, ''))
}

const findOptions = (config, lang, _loader, index) => {
  const rule = findRule(config, lang, index)
  const use = rule.use.find(({ loader }) => loader.includes(`${_loader}-loader`))
  return use.options || {}
}

test('default loaders', () => {
  const config = genConfig({ postcss: {}})

  LANGS.forEach(lang => {
    const loader = lang === 'css' ? [] : LOADERS[lang]
    expect(findLoaders(config, lang)).toEqual(['vue-style', 'css', 'postcss'].concat(loader))
    // assert css-loader options
    expect(findOptions(config, lang, 'css')).toEqual({
      sourceMap: false,
      importLoaders: lang === 'css' ? 2 : 3
    })
  })
  // sass indented syntax
  expect(findOptions(config, 'sass', 'sass')).toEqual({ indentedSyntax: true, sourceMap: false })
})

test('production defaults', () => {
  const config = genConfig({ postcss: {}}, 'production')
  LANGS.forEach(lang => {
    const loader = lang === 'css' ? [] : LOADERS[lang]
    expect(findLoaders(config, lang)).toEqual([extractLoaderPath, 'css', 'postcss'].concat(loader))
    expect(findOptions(config, lang, 'css')).toEqual({
      sourceMap: false,
      importLoaders: lang === 'css' ? 2 : 3
    })
  })
})

test('CSS Modules rules', () => {
  const config = genConfig({
    vue: {
      css: {
        modules: true
      }
    }
  })
  LANGS.forEach(lang => {
    const expected = {
      importLoaders: lang === 'css' ? 1 : 2, // no postcss-loader
      localIdentName: `[name]_[local]_[hash:base64:5]`,
      sourceMap: false,
      modules: true
    }
    // vue-modules rules
    expect(findOptions(config, lang, 'css', 0)).toEqual(expected)
    // normal-modules rules
    expect(findOptions(config, lang, 'css', 2)).toEqual(expected)
    // normal rules
    expect(findOptions(config, lang, 'css', 3)).toEqual(expected)
  })
})

test('css.extract', () => {
  const config = genConfig({
    vue: {
      css: {
        extract: false
      }
    }
  }, 'production')
  LANGS.forEach(lang => {
    expect(findLoaders(config, lang)).not.toContain(extractLoaderPath)
  })
})

test('css.sourceMap', () => {
  const config = genConfig({
    postcss: {},
    vue: {
      css: {
        sourceMap: true
      }
    }
  })
  LANGS.forEach(lang => {
    expect(findOptions(config, lang, 'css').sourceMap).toBe(true)
    expect(findOptions(config, lang, 'postcss').sourceMap).toBe(true)
    expect(findOptions(config, lang, LOADERS[lang]).sourceMap).toBe(true)
  })
})

test('css-loader options', () => {
  const localIdentName = '[name]__[local]--[hash:base64:5]'
  const config = genConfig({
    vue: {
      css: {
        loaderOptions: {
          css: {
            localIdentName,
            camelCase: 'only'
          }
        }
      }
    }
  })
  LANGS.forEach(lang => {
    const vueOptions = findOptions(config, lang, 'css', 0)
    expect(vueOptions.localIdentName).toBe(localIdentName)
    expect(vueOptions.camelCase).toBe('only')

    const extOptions = findOptions(config, lang, 'css', 2)
    expect(extOptions.localIdentName).toBe(localIdentName)
    expect(extOptions.camelCase).toBe('only')
  })
})

test('css.loaderOptions', () => {
  const data = '$env: production;'
  const config = genConfig({
    vue: {
      css: {
        loaderOptions: {
          sass: {
            data
          }
        }
      }
    }
  })

  expect(findOptions(config, 'scss', 'sass')).toEqual({ data, sourceMap: false })
  expect(findOptions(config, 'sass', 'sass')).toEqual({ data, indentedSyntax: true, sourceMap: false })
})

test('skip postcss-loader if no postcss config found', () => {
  const config = genConfig()
  LANGS.forEach(lang => {
    const loader = lang === 'css' ? [] : LOADERS[lang]
    expect(findLoaders(config, lang)).toEqual(['vue-style', 'css'].concat(loader))
  })
})


module.exports = cli => {
  cli.injectFeature({
    name: 'E2E Testing',
    value: 'e2e',
    short: 'E2E',
    description: 'Add an End-to-End testing solution to the app like Cypress or Nightwatch',
    link: 'https://github.com/vuejs/vue-cli/tree/dev/docs#e2e-testing',
    plugins: ['e2e-cypress', 'e2e-nightwatch']
  })

  cli.injectPrompt({
    name: 'e2e',
    when: answers => answers.features.includes('e2e'),
    type: 'list',
    message: 'Pick a E2E testing solution:',
    choices: [
      {
        name: 'Cypress (Chrome only)',
        value: 'cypress',
        short: 'Cypress'
      },
      {
        name: 'Nightwatch (Selenium-based)',
        value: 'nightwatch',
        short: 'Nightwatch'
      }
    ]
  })

  cli.onPromptComplete((answers, options) => {
    if (answers.e2e === 'cypress') {
      options.plugins['@vue/cli-plugin-e2e-cypress'] = {}
    } else if (answers.e2e === 'nightwatch') {
      options.plugins['@vue/cli-plugin-e2e-nightwatch'] = {}
    }
  })
}


<template>

      <VueIcon icon="dashboard"/>

{{ $t('org.vue.vue-webpack.dashboard.title') }}

      <template
        v-if="mode === 'serve'"
      >
        <VueButton
          icon-left="open_in_browser"
          :label="$t('org.vue.vue-webpack.dashboard.open-app')"
          :disabled="!serveUrl"
          :href="serveUrl"
          target="_blank"
        />
        <VueIcon
          icon="lens"
          class="separator"
        />
      </template>

      <VueSwitch
        v-if="mode !== 'serve' && modernMode"
        v-model="showModernBuild"
      >
        {{ $t('org.vue.vue-webpack.modern-mode') }}
      </VueSwitch>

      <VueSelect v-model="sizeField">
        <VueSelectButton value="stats" :label="`${$t('org.vue.vue-webpack.sizes.stats')}`"/>
        <VueSelectButton value="parsed" :label="`${$t('org.vue.vue-webpack.sizes.parsed')}`"/>
        <VueSelectButton value="gzip" :label="`${$t('org.vue.vue-webpack.sizes.gzip')}`"/>
      </VueSelect>

      <VueButton
        class="icon-button"
        icon-left="help"
        v-tooltip="$t('org.vue.vue-webpack.sizes.help')"
      />

      <BuildStatus />
      <BuildProgress />
      <SpeedStats class="span-2"/>
      <AssetList />
      <ModuleList />

</template>


const jsonServer = require('json-server')

const defaultData = () => ({
  'posts': [
    { 'id': 1, 'title': 'json-server', 'author': 'typicode' }
  ],
  'comments': [
    { 'id': 1, 'body': 'some comment', 'postId': 1 }
  ],
  'profile': { 'name': 'typicode' }
})

module.exports = function createJSONServer (data = defaultData()) {
  const server = jsonServer.create()
  const router = jsonServer.router(data)
  const middlewares = jsonServer.defaults()

  server.use(middlewares)
  server.use(router)
  return server
}


importLoaders: lang === 'css' ? 1 : 2, // no postcss-loader

keep keep keep keep keep replace keep keep keep keep


#import "./taskFragment.gql"

mutation taskStop ($id: ID!) {
  taskStop (id: $id) {
    ...task
  }
}


const gql = require('graphql-tag')
// Connectors
const prompts = require('../connectors/prompts')

exports.types = gql`
extend type Mutation {
  promptAnswer (input: PromptInput!): [Prompt]
}

type Prompt implements DescribedEntity {
  id: ID!
  type: PromptType!
  visible: Boolean!
  enabled: Boolean
  name: String
  message: String
  group: String
  description: String
  link: String
  choices: [PromptChoice]
  value: String
  valueChanged: Boolean
  error: PromptError
  tabId: String
}

input PromptInput {
  id: ID!
  value: String!
}

type PromptChoice {
  value: String!
  name: String
  checked: Boolean
  disabled: Boolean
  isDefault: Boolean
}

type PromptError {
  message: String!
  link: String
}

enum PromptType {
  input
  confirm
  list
  rawlist
  expand
  checkbox
  password
  editor
  color
}
`

exports.resolvers = {
  Mutation: {
    promptAnswer: (root, { input }, context) => prompts.answerPrompt(input, context)
  }
}


const ipc = require('node-ipc')
// Utils
const { log, dumpObject } = require('../util/logger')

ipc.config.id = process.env.VUE_CLI_IPC || 'vue-cli'
ipc.config.retry = 1500
ipc.config.silent = true

const listeners = []

ipc.serve(() => {
  ipc.server.on('message', (data, socket) => {
    log('IPC message', dumpObject(data))
    for (const listener of listeners) {
      listener({
        data,
        emit: data => {
          ipc.server.emit(socket, 'message', data)
        }
      })
    }
  })

  ipc.server.on('ack', (data, socket) => {
    log('IPC ack', dumpObject(data))
    if (data.done) {
      ipc.server.emit(socket, 'ack', { ok: true })
    }
  })
})

ipc.server.start()

function on (cb) {
  listeners.push(cb)
  return () => off(cb)
}

function off (cb) {
  const index = listeners.indexOf(cb)
  if (index !== -1) listeners.splice(index, 1)
}

function send (data) {
  log('IPC send', dumpObject(data))
  ipc.server.broadcast('message', data)
}

module.exports = {
  on,
  off,
  send
}


1 + // stylePostLoader injected by vue-loader

keep keep keep replace replace keep keep keep


<template>
  <div class="configuration-tab">
    <PromptsList
      :prompts="visiblePrompts"
      @answer="answerPrompt"
    />
  </div>
</template>

<script>
import Prompts from '../mixins/Prompts'

import CONFIGURATION from '../graphql/configuration.gql'

export default {
  mixins: [
    Prompts({
      field: 'tab',
      query: CONFIGURATION,
      variables () {
        return {
          id: this.configuration.id
        }
      },
      updateQuery (data, prompts) {
        const result = {}
        for (const prompt of prompts) {
          const list = result[prompt.tabId] || (result[prompt.tabId] = [])
          list.push(prompt)
        }
        for (const tabId in result) {
          data.configuration.tabs.find(t => t.id === tabId).prompts = result[tabId]
        }
      }
    })
  ],

  props: {
    configuration: {
      type: Object,
      required: true
    },

    tab: {
      type: Object,
      required: true
    }
  }
}
</script>


module.exports = (api, options = {}) => {
  api.injectImports(api.entryFile, `import router from './router'`)
  api.injectRootOptions(api.entryFile, `router`)
  api.extendPackage({
    dependencies: {
      'vue-router': '^3.0.1'
    }
  })
  api.render('./template', {
    historyMode: options.routerHistoryMode
  })

  if (api.invoking) {
    api.postProcessFiles(files => {
      const appFile = files[`src/App.vue`]
      if (appFile) {
        files[`src/App.vue`] = appFile.replace(/^<template>[^]+<\/script>/, `
<template>

      <router-link to="/">Home</router-link> |
      <router-link to="/about">About</router-link>

    <router-view/>

</template>
        `.trim())
      }
    })

    if (api.hasPlugin('typescript')) {
      /* eslint-disable-next-line node/no-extraneous-require */
      const convertFiles = require('@vue/cli-plugin-typescript/generator/convert')
      convertFiles(api)
    }
  }
}


<template functional>

{{ props.asset.name }}

{{ props.asset.size | size('B') }}

{{ props.asset.speeds.global.totalDownloadTime | round(100) }}s

{{ props.asset.speeds['3gs'].totalDownloadTime | round(100) }}s

{{ props.asset.speeds['3gf'].totalDownloadTime | round(100) }}s

        <VueIcon
          v-if="!props.asset.secondary && props.asset.big"
          icon="warning"
          class="icon"
          v-tooltip="parent.$t('org.vue.vue-webpack.dashboard.asset-list.size-warning')"
        />

</template>


//  Create a Tween Timeline and add it to the active Tween list

var timeline = TimelineBuilder(this, config);

if (!timeline.paused)
{
this._add.push(timeline);

var Class = require('../../utils/Class');
var TweenBuilder = require('../builder/TweenBuilder');
var TWEEN_CONST = require('../tween/const');

var Timeline = new Class({

    initialize:

    function Timeline (manager)
    {
        this.manager = manager;

        this.isTimeline = true;

        //  An array of Tween objects, each containing a unique property and target being tweened.
        this.data = [];

        //  data array doesn't usually change, so we can cache the length
        this.totalData = 0;

        //  If true then duration, delay, etc values are all frame totals
        this.useFrames = false;

        //  Scales the time applied to this Tween. A value of 1 runs in real-time. A value of 0.5 runs 50% slower, and so on.
        //  Value isn't used when calculating total duration of the tween, it's a run-time delta adjustment only.
        this.timeScale = 1;

        //  Loop this tween? Can be -1 for an infinite loop, or an integer.
        //  When enabled it will play through ALL TweenDatas again (use TweenData.repeat to loop a single TD)
        this.loop = 0;

        //  Time in ms/frames before the tween loops.
        this.loopDelay = 0;

        //  How many loops are left to run?
        this.loopCounter = 0;

        //  Time in ms/frames before the 'onComplete' event fires. This never fires if loop = true (as it never completes)
        this.completeDelay = 0;

        //  Countdown timer (used by loopDelay and completeDelay)
        this.countdown = 0;

        //  The current state of the tween
        this.state = TWEEN_CONST.PENDING_ADD;

        //  The state of the tween when it was paused (used by Resume)
        this._pausedState = TWEEN_CONST.PENDING_ADD;

        //  Does the Tween start off paused? (if so it needs to be started with Tween.play)
        this.paused = false;

        //  Elapsed time in ms/frames of this run through the Tween.
        this.elapsed = 0;

        //  Total elapsed time in ms/frames of the entire Tween, including looping.
        this.totalElapsed = 0;

        //  Time in ms/frames for the whole Tween to play through once, excluding loop amounts and loop delays
        this.duration = 0;

        //  Value between 0 and 1. The amount through the Tween, excluding loops.
        this.progress = 0;

        //  Time in ms/frames for all Tweens to complete (including looping)
        this.totalDuration = 0;

        //  Value between 0 and 1. The amount through the entire Tween, including looping.
        this.totalProgress = 0;

        this.callbacks = {
            onComplete: null,
            onLoop: null,
            onStart: null,
            onUpdate: null,
            onYoyo: null
        };

        this.callbackScope;
    },

    setTimeScale: function (value)
    {
        this.timeScale = value;

        return this;
    },

    getTimeScale: function ()
    {
        return this.timeScale;
    },

    isPlaying: function ()
    {
        return (this.state === TWEEN_CONST.ACTIVE);
    },

    add: function (config)
    {
        return this.queue(TweenBuilder(this, config));
    },

    queue: function (tween)
    {
        tween.parent = this;
        tween.parentIsTimeline = true;

        this.data.push(tween);

        this.totalData = this.data.length;

        return this;
    },

    hasOffset: function (tween)
    {
        return (tween.offset !== null);
    },

    isOffsetAbsolute: function (value)
    {
        return (typeof(value) === 'number');
    },

    isOffsetRelative: function (value)
    {
        var t = typeof(value);

        if (t === 'string')
        {
            var op = value[0];

            if (op === '-' || op === '+')
            {
                return true;
            }
        }

        return false;
    },

    getRelativeOffset: function (value, base)
    {
        var op = value[0];
        var num = parseFloat(value.substr(2));
        var result = base;

        switch (op)
        {
            case '+':
                result += num;
                break;

            case '-':
                result -= num;
                break;
        }

        //  Cannot ever be < 0
        return Math.max(0, result);
    },

    calcDuration: function ()
    {
        var prevEnd = 0;
        var totalDuration = 0;
        var offsetDuration = 0;

        for (var i = 0; i < this.totalData; i++)
        {
            var tween = this.data[i];

            tween.init();

            if (this.hasOffset(tween))
            {
                if (this.isOffsetAbsolute(tween.offset))
                {
                    //  An actual number, so it defines the start point from the beginning of the timeline
                    tween.calculatedOffset = tween.offset;

                    if (tween.offset === 0)
                    {
                        offsetDuration = 0;
                    }

                    // console.log('Timeline.calcDuration', i, 'absolute', tween.calculatedOffset);
                }
                else if (this.isOffsetRelative(tween.offset))
                {
                    //  A relative offset (i.e. '-=1000', so starts at 'offset' ms relative to the PREVIOUS Tweens ending time)
                    tween.calculatedOffset = this.getRelativeOffset(tween.offset, prevEnd);

                    // console.log('Timeline.calcDuration', i, 'relative', tween.calculatedOffset);
                }
            }
            else
            {
                //  Sequential
                tween.calculatedOffset = offsetDuration;

                // console.log('Timeline.calcDuration', i, 'sequential', tween.calculatedOffset);
            }

            prevEnd = tween.totalDuration + tween.calculatedOffset;

            // console.log('Span', i, tween.calculatedOffset, 'to', prevEnd);

            totalDuration += tween.totalDuration;
            offsetDuration += tween.totalDuration;
        }

        //  Excludes loop values
        this.duration = totalDuration;

        this.loopCounter = (this.loop === -1) ? 999999999999 : this.loop;

        if (this.loopCounter > 0)
        {
            this.totalDuration = this.duration + this.completeDelay + ((this.duration + this.loopDelay) * this.loopCounter);
        }
        else
        {
            this.totalDuration = this.duration + this.completeDelay;
        }
    },

    init: function ()
    {
        this.calcDuration();

        this.progress = 0;
        this.totalProgress = 0;

        if (this.paused)
        {
            this.state = TWEEN_CONST.PAUSED;

            return false;
        }
        else
        {
            return true;
        }
    },

    resetTweens: function (resetFromLoop)
    {
        for (var i = 0; i < this.totalData; i++)
        {
            var tween = this.data[i];

            tween.play(resetFromLoop);
        }
    },

    setCallback: function (type, callback, params, scope)
    {
        if (Timeline.TYPES.indexOf(type) !== -1)
        {
            this.callbacks[type] = { func: callback, scope: scope, params: params };
        }

        return this;
    },

    play: function ()
    {
        if (this.state === TWEEN_CONST.ACTIVE)
        {
            return;
        }

        if (this.paused)
        {
            this.paused = false;

            this.manager.makeActive(this);

            return;
        }
        else
        {
            this.resetTweens(false);

            this.state = TWEEN_CONST.ACTIVE;
        }

        var onStart = this.callbacks.onStart;

        if (onStart)
        {
            onStart.func.apply(onStart.scope, onStart.params);
        }
    },

    nextState: function ()
    {
        if (this.loopCounter > 0)
        {
            this.resetTweens(true);

            //  Reset the elapsed time
            //  TODO: Probably ought to be set to the remainder from elapsed - duration
            //  as the tweens nearly always over-run by a few ms due to rAf

            this.elapsed = 0;
            this.progress = 0;

            this.loopCounter--;

            var onLoop = this.callbacks.onLoop;

            if (onLoop)
            {
                onLoop.func.apply(onLoop.scope, onLoop.params);
            }

            if (this.loopDelay > 0)
            {
                this.countdown = this.loopDelay;
                this.state = TWEEN_CONST.LOOP_DELAY;
            }
            else
            {
                this.state = TWEEN_CONST.ACTIVE;
            }
        }
        else if (this.completeDelay > 0)
        {
            this.countdown = this.completeDelay;
            this.state = TWEEN_CONST.COMPLETE_DELAY;
        }
        else
        {
            var onComplete = this.callbacks.onComplete;

            if (onComplete)
            {
                onComplete.func.apply(onComplete.scope, onComplete.params);
            }

            this.state = TWEEN_CONST.PENDING_REMOVE;
        }
    },

    //  Returns 'true' if this Timeline has finished and should be removed from the Tween Manager
    //  Otherwise, returns false
    update: function (timestamp, delta)
    {
        if (this.state === TWEEN_CONST.PAUSED)
        {
            return;
        }

        var rawDelta = delta;

        if (this.useFrames)
        {
            delta = 1 * this.manager.timeScale;
        }

        delta *= this.timeScale;

        this.elapsed += delta;
        this.progress = Math.min(this.elapsed / this.duration, 1);

        this.totalElapsed += delta;
        this.totalProgress = Math.min(this.totalElapsed / this.totalDuration, 1);

        switch (this.state)
        {
            case TWEEN_CONST.ACTIVE:

                var stillRunning = this.totalData;

                for (var i = 0; i < this.totalData; i++)
                {
                    var tween = this.data[i];

                    if (tween.update(timestamp, rawDelta))
                    {
                        stillRunning--;
                    }
                }

                var onUpdate = this.callbacks.onUpdate;

                if (onUpdate)
                {
                    onUpdate.func.apply(onUpdate.scope, onUpdate.params);
                }

                //  Anything still running? If not, we're done
                if (stillRunning === 0)
                {
                    this.nextState();
                }

                break;

            case TWEEN_CONST.LOOP_DELAY:

                this.countdown -= delta;

                if (this.countdown <= 0)
                {
                    this.state = TWEEN_CONST.ACTIVE;
                }

                break;

            case TWEEN_CONST.COMPLETE_DELAY:

                this.countdown -= delta;

                if (this.countdown <= 0)
                {
                    var onComplete = this.callbacks.onComplete;

                    if (onComplete)
                    {
                        onComplete.func.apply(onComplete.scope, onComplete.params);
                    }

                    this.state = TWEEN_CONST.PENDING_REMOVE;
                }

                break;
        }

        return (this.state === TWEEN_CONST.PENDING_REMOVE);
    },

    //  Stops the Tween immediately, whatever stage of progress it is at and flags it for removal by the TweenManager
    stop: function ()
    {
        this.state = TWEEN_CONST.PENDING_REMOVE;
    },

    pause: function ()
    {
        if (this.state === TWEEN_CONST.PAUSED)
        {
            return;
        }

        this.paused = true;

        this._pausedState = this.state;

        this.state = TWEEN_CONST.PAUSED;

        return this;
    },

    resume: function ()
    {
        if (this.state === TWEEN_CONST.PAUSED)
        {
            this.paused = false;

            this.state = this._pausedState;
        }

        return this;
    },

    hasTarget: function ()
    {

    },

    destroy: function ()
    {

    }
});

Timeline.TYPES = [ 'onStart', 'onUpdate', 'onLoop', 'onComplete', 'onYoyo' ];

module.exports = Timeline;


/**
* @author       Richard Davey <rich@photonstorm.com>
* @copyright    2016 Photon Storm Ltd.
* @license      {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/

/**
* @class Phaser.Utils
* @static
*/
Phaser.Utils = {

    /**
    * Takes the given string and reverses it, returning the reversed string.
    * For example if given the string `Atari 520ST` it would return `TS025 iratA`.
    *
    * @method Phaser.Utils.reverseString
    * @param {string} string - The string to be reversed.
    * @return {string} The reversed string.
    */
    reverseString: function (string) {

        return string.split('').reverse().join('');

    },

    /**
     * Gets an objects property by string.
     *
     * @method Phaser.Utils.getProperty
     * @param {object} obj - The object to traverse.
     * @param {string} prop - The property whose value will be returned.
     * @return {*} the value of the property or null if property isn't found .
     */
    getProperty: function(obj, prop) {

        var parts = prop.split('.'),
            last = parts.pop(),
            l = parts.length,
            i = 1,
            current = parts[0];

        while (i < l && (obj = obj[current]))
        {
            current = parts[i];
            i++;
        }

        if (obj)
        {
            return obj[last];
        }
        else
        {
            return null;
        }

    },

    /**
     * Sets an objects property by string.
     *
     * @method Phaser.Utils.setProperty
     * @param {object} obj - The object to traverse
     * @param {string} prop - The property whose value will be changed
     * @return {object} The object on which the property was set.
     */
    setProperty: function(obj, prop, value) {

        var parts = prop.split('.'),
            last = parts.pop(),
            l = parts.length,
            i = 1,
            current = parts[0];

        while (i < l && (obj = obj[current]))
        {
            current = parts[i];
            i++;
        }

        if (obj)
        {
            obj[last] = value;
        }

        return obj;

    },

    /**
    * Generate a random bool result based on the chance value.
    *
    * Returns true or false based on the chance value (default 50%). For example if you wanted a player to have a 30% chance
    * of getting a bonus, call chanceRoll(30) - true means the chance passed, false means it failed.
    *
    * @method Phaser.Utils#chanceRoll
    * @param {number} chance - The chance of receiving the value. A number between 0 and 100 (effectively 0% to 100%).
    * @return {boolean} True if the roll passed, or false otherwise.
    */
    chanceRoll: function (chance) {
        if (chance === undefined) { chance = 50; }
        return chance > 0 && (Math.random() * 100 <= chance);
    },

    /**
    * Choose between one of two values randomly.
    *
    * @method Phaser.Utils#randomChoice
    * @param {any} choice1
    * @param {any} choice2
    * @return {any} The randomly selected choice
    */
    randomChoice: function (choice1, choice2) {
        return (Math.random() < 0.5) ? choice1 : choice2;
    },

    /**
    * Get a unit dimension from a string.
    *
    * @method Phaser.Utils.parseDimension
    * @param {string|number} size - The size to parse.
    * @param {number} dimension - The window dimension to check.
    * @return {number} The parsed dimension.
    */
    parseDimension: function (size, dimension) {

        var f = 0;
        var px = 0;

        if (typeof size === 'string')
        {
            //  %?
            if (size.substr(-1) === '%')
            {
                f = parseInt(size, 10) / 100;

                if (dimension === 0)
                {
                    px = window.innerWidth * f;
                }
                else
                {
                    px = window.innerHeight * f;
                }
            }
            else
            {
                px = parseInt(size, 10);
            }
        }
        else
        {
            px = size;
        }

        return px;

    },

    /**
    * Takes the given string and pads it out, to the length required, using the character
    * specified. For example if you need a string to be 6 characters long, you can call:
    *
    * `pad('bob', 6, '-', 2)`
    *
    * This would return: `bob---` as it has padded it out to 6 characters, using the `-` on the right.
    *
    * You can also use it to pad numbers (they are always returned as strings):
    *
    * `pad(512, 6, '0', 1)`
    *
    * Would return: `000512` with the string padded to the left.
    *
    * If you don't specify a direction it'll pad to both sides:
    *
    * `pad('c64', 7, '*')`
    *
    * Would return: `**c64**`
    *
    * @method Phaser.Utils.pad
    * @param {string} str - The target string. `toString()` will be called on the string, which means you can also pass in common data types like numbers.
    * @param {integer} [len=0] - The number of characters to be added.
    * @param {string} [pad=" "] - The string to pad it out with (defaults to a space).
    * @param {integer} [dir=3] - The direction dir = 1 (left), 2 (right), 3 (both).
    * @return {string} The padded string.
    */
    pad: function (str, len, pad, dir) {

        if (len === undefined) { var len = 0; }
        if (pad === undefined) { var pad = ' '; }
        if (dir === undefined) { var dir = 3; }

        str = str.toString();

        var padlen = 0;

        if (len + 1 >= str.length)
        {
            switch (dir)
            {
                case 1:
                    str = new Array(len + 1 - str.length).join(pad) + str;
                    break;

                case 3:
                    var right = Math.ceil((padlen = len - str.length) / 2);
                    var left = padlen - right;
                    str = new Array(left+1).join(pad) + str + new Array(right+1).join(pad);
                    break;

                default:
                    str = str + new Array(len + 1 - str.length).join(pad);
                    break;
            }
        }

        return str;

    },

    /**
    * This is a slightly modified version of jQuery.isPlainObject.
    * A plain object is an object whose internal class property is [object Object].
    * @method Phaser.Utils.isPlainObject
    * @param {object} obj - The object to inspect.
    * @return {boolean} - true if the object is plain, otherwise false.
    */
    isPlainObject: function (obj) {

        // Not plain objects:
        // - Any object or value whose internal [[Class]] property is not "[object Object]"
        // - DOM nodes
        // - window
        if (typeof(obj) !== "object" || obj.nodeType || obj === obj.window)
        {
            return false;
        }

        // Support: Firefox <20
        // The try/catch suppresses exceptions thrown when attempting to access
        // the "constructor" property of certain host objects, ie. |window.location|
        // https://bugzilla.mozilla.org/show_bug.cgi?id=814622
        try {
            if (obj.constructor && !({}).hasOwnProperty.call(obj.constructor.prototype, "isPrototypeOf"))
            {
                return false;
            }
        } catch (e) {
            return false;
        }

        // If the function hasn't returned already, we're confident that
        // |obj| is a plain object, created by {} or constructed with new Object
        return true;
    },

    /**
    * This is a slightly modified version of http://api.jquery.com/jQuery.extend/
    *
    * @method Phaser.Utils.extend
    * @param {boolean} deep - Perform a deep copy?
    * @param {object} target - The target object to copy to.
    * @return {object} The extended object.
    */
    extend: function () {

        var options, name, src, copy, copyIsArray, clone,
            target = arguments[0] || {},
            i = 1,
            length = arguments.length,
            deep = false;

        // Handle a deep copy situation
        if (typeof target === "boolean")
        {
            deep = target;
            target = arguments[1] || {};
            // skip the boolean and the target
            i = 2;
        }

        // extend Phaser if only one argument is passed
        if (length === i)
        {
            target = this;
            --i;
        }

        for (; i < length; i++)
        {
            // Only deal with non-null/undefined values
            if ((options = arguments[i]) != null)
            {
                // Extend the base object
                for (name in options)
                {
                    src = target[name];
                    copy = options[name];

                    // Prevent never-ending loop
                    if (target === copy)
                    {
                        continue;
                    }

                    // Recurse if we're merging plain objects or arrays
                    if (deep && copy && (Phaser.Utils.isPlainObject(copy) || (copyIsArray = Array.isArray(copy))))
                    {
                        if (copyIsArray)
                        {
                            copyIsArray = false;
                            clone = src && Array.isArray(src) ? src : [];
                        }
                        else
                        {
                            clone = src && Phaser.Utils.isPlainObject(src) ? src : {};
                        }

                        // Never move original objects, clone them
                        target[name] = Phaser.Utils.extend(deep, clone, copy);

                    // Don't bring in undefined values
                    }
                    else if (copy !== undefined)
                    {
                        target[name] = copy;
                    }
                }
            }
        }

        // Return the modified object
        return target;

    },

    /**
    * Mixes in an existing mixin object with the target.
    *
    * Values in the mixin that have either `get` or `set` functions are created as properties via `defineProperty`
    * _except_ if they also define a `clone` method - if a clone method is defined that is called instead and
    * the result is assigned directly.
    *
    * @method Phaser.Utils.mixinPrototype
    * @param {object} target - The target object to receive the new functions.
    * @param {object} mixin - The object to copy the functions from.
    * @param {boolean} [replace=false] - If the target object already has a matching function should it be overwritten or not?
    */
    mixinPrototype: function (target, mixin, replace) {

        if (replace === undefined) { replace = false; }

        var mixinKeys = Object.keys(mixin);

        for (var i = 0; i < mixinKeys.length; i++)
        {
            var key = mixinKeys[i];
            var value = mixin[key];

            if (!replace && (key in target))
            {
                //  Not overwriting existing property
                continue;
            }
            else
            {
                if (value &&
                    (typeof value.get === 'function' || typeof value.set === 'function'))
                {
                    //  Special case for classes like Phaser.Point which has a 'set' function!
                    if (typeof value.clone === 'function')
                    {
                        target[key] = value.clone();
                    }
                    else
                    {
                        Object.defineProperty(target, key, value);
                    }
                }
                else
                {
                    target[key] = value;
                }
            }
        }

    },

    /**
    * Mixes the source object into the destination object, returning the newly modified destination object.
    * Based on original code by @mudcube
    *
    * @method Phaser.Utils.mixin
    * @param {object} from - The object to copy (the source object).
    * @param {object} to - The object to copy to (the destination object).
    * @return {object} The modified destination object.
    */
    mixin: function (from, to) {

        if (!from || typeof (from) !== "object")
        {
            return to;
        }

        for (var key in from)
        {
            var o = from[key];

            if (o.childNodes || o.cloneNode)
            {
                continue;
            }

            var type = typeof (from[key]);

            if (!from[key] || type !== "object")
            {
                to[key] = from[key];
            }
            else
            {
                //  Clone sub-object
                if (typeof (to[key]) === type)
                {
                    to[key] = Phaser.Utils.mixin(from[key], to[key]);
                }
                else
                {
                    to[key] = Phaser.Utils.mixin(from[key], new o.constructor());
                }
            }
        }

        return to;

    }

};


/**
 * Sunlight
 *    Intelligent syntax highlighting
 *
 * http://sunlightjs.com/
 *
 * by Tommy Montgomery <http://tmont.com>
 * Licensed under WTFPL <http://sam.zoy.org/wtfpl/>
 */
(function(window, document, undefined){

	var
		//http://webreflection.blogspot.com/2009/01/32-bytes-to-know-if-your-browser-is-ie.html
		//we have to sniff this because IE requires \r
		isIe = !+"\v1",
		EOL = isIe ? "\r" : "\n",
		EMPTY = function() { return null; },
		HIGHLIGHTED_NODE_COUNT = 0,
		DEFAULT_LANGUAGE = "plaintext",
		DEFAULT_CLASS_PREFIX = "sunlight-",

		//global sunlight variables
		defaultAnalyzer,
		getComputedStyle,
		globalOptions = {
			tabWidth: 4,
			classPrefix: DEFAULT_CLASS_PREFIX,
			showWhitespace: false,
			maxHeight: false
		},
		languages = {},
		languageDefaults = {},
		events = {
			beforeHighlightNode: [],
			beforeHighlight: [],
			beforeTokenize: [],
			afterTokenize: [],
			beforeAnalyze: [],
			afterAnalyze: [],
			afterHighlight: [],
			afterHighlightNode: []
		};

	defaultAnalyzer = (function() {
		function defaultHandleToken(suffix) {
			return function(context) {
				var element = document.createElement("span");
				element.className = context.options.classPrefix + suffix;
				element.appendChild(context.createTextNode(context.tokens[context.index]));
				return context.addNode(element) || true;
			};
		}

		return {
			handleToken: function(context) {
				return defaultHandleToken(context.tokens[context.index].name)(context);
			},

			//just append default content as a text node
			handle_default: function(context) {
				return context.addNode(context.createTextNode(context.tokens[context.index]));
			},

			//this handles the named ident mayhem
			handle_ident: function(context) {
				var evaluate = function(rules, createRule) {
					var i;
					rules = rules || [];
					for (i = 0; i < rules.length; i++) {
						if (typeof(rules[i]) === "function") {
							if (rules[i](context)) {
								return defaultHandleToken("named-ident")(context);
							}
						} else if (createRule && createRule(rules[i])(context.tokens)) {
							return defaultHandleToken("named-ident")(context);
						}
					}

					return false;
				};

				return evaluate(context.language.namedIdentRules.custom)
					|| evaluate(context.language.namedIdentRules.follows, function(ruleData) { return createProceduralRule(context.index - 1, -1, ruleData, context.language.caseInsensitive); })
					|| evaluate(context.language.namedIdentRules.precedes, function(ruleData) { return createProceduralRule(context.index + 1, 1, ruleData, context.language.caseInsensitive); })
					|| evaluate(context.language.namedIdentRules.between, function(ruleData) { return createBetweenRule(context.index, ruleData.opener, ruleData.closer, context.language.caseInsensitive); })
					|| defaultHandleToken("ident")(context);
			}
		};
	}());

	languageDefaults = {
		analyzer: create(defaultAnalyzer),
		customTokens: [],
		namedIdentRules: {},
		punctuation: /[^\w\s]/,
		numberParser: defaultNumberParser,
		caseInsensitive: false,
		doNotParse: /\s/,
		contextItems: {},
		embeddedLanguages: {}
	};

	//adapted from http://blargh.tommymontgomery.com/2010/04/get-computed-style-in-javascript/
	getComputedStyle = (function() {
		var func = null;
		if (document.defaultView && document.defaultView.getComputedStyle) {
			func = document.defaultView.getComputedStyle;
		} else {
			func = function(element, anything) {
				return element["currentStyle"] || {};
			};
		}

		return function(element, style) {
			return func(element, null)[style];
		}
	}());

	//-----------
	//FUNCTIONS
	//-----------

	function createCodeReader(text) {
		var index = 0,
			line = 1,
			column = 1,
			length,
			EOF = undefined,
			currentChar,
			nextReadBeginsLine;

		text = text.replace(/\r\n/g, "\n").replace(/\r/g, "\n"); //normalize line endings to unix

		length = text.length;
		currentChar = length > 0 ? text.charAt(0) : EOF;

		function getCharacters(count) {
			var value;
			if (count === 0) {
				return "";
			}

			count = count || 1;

			value = text.substring(index + 1, index + count + 1);
			return value === "" ? EOF : value;
		}

		return {
			toString: function() {
				return "length: " + length + ", index: " + index + ", line: " + line + ", column: " + column + ", current: [" + currentChar + "]";
			},

			peek: function(count) {
				return getCharacters(count);
			},

			substring: function() {
				return text.substring(index);
			},

			peekSubstring: function() {
				return text.substring(index + 1);
			},

			read: function(count) {
				var value = getCharacters(count),
					newlineCount,
					lastChar;

				if (value === "") {
					//this is a result of reading/peeking/doing nothing
					return value;
				}

				if (value !== EOF) {
					//advance index
					index += value.length;
					column += value.length;

					//update line count
					if (nextReadBeginsLine) {
						line++;
						column = 1;
						nextReadBeginsLine = false;
					}

					newlineCount = value.substring(0, value.length - 1).replace(/[^\n]/g, "").length;
					if (newlineCount > 0) {
						line += newlineCount;
						column = 1;
					}

					lastChar = last(value);
					if (lastChar === "\n") {
						nextReadBeginsLine = true;
					}

					currentChar = lastChar;
				} else {
					index = length;
					currentChar = EOF;
				}

				return value;
			},

			text: function() { return text; },

			getLine: function() { return line; },
			getColumn: function() { return column; },
			isEof: function() { return index >= length; },
			isSol: function() { return column === 1; },
			isSolWs: function() {
				var temp = index,
					c;
				if (column === 1) {
					return true;
				}

				//look backward until we find a newline or a non-whitespace character
				while ((c = text.charAt(--temp)) !== "") {
					if (c === "\n") {
						return true;
					}
					if (!/\s/.test(c)) {
						return false;
					}
				}

				return true;
			},
			isEol: function() { return nextReadBeginsLine; },
			EOF: EOF,
			current: function() { return currentChar; }
		};
	}

	//http://javascript.crockford.com/prototypal.html
	function create(o) {
		function F() {}
		F.prototype = o;
		return new F();
	}

	function appendAll(parent, children) {
		var i;
		for (i = 0; i < children.length; i++) {
			parent.appendChild(children[i]);
		}
	}

	//gets the last character in a string or the last element in an array
	function last(thing) {
		return thing.charAt ? thing.charAt(thing.length - 1) : thing[thing.length - 1];
	}

	//array.contains()
	function contains(arr, value, caseInsensitive) {
		var i;
		if (arr.indexOf && !caseInsensitive) {
			return arr.indexOf(value) >= 0;
		}

		for (i = 0; i < arr.length; i++) {
			if (arr[i] === value) {
				return true;
			}

			if (caseInsensitive && typeof(arr[i]) === "string" && typeof(value) === "string" && arr[i].toUpperCase() === value.toUpperCase()) {
				return true;
			}
		}

		return false;
	}

	//non-recursively merges one object into the other
	function merge(defaultObject, objectToMerge) {
		var key;
		if (!objectToMerge) {
			return defaultObject;
		}

		for (key in objectToMerge) {
			defaultObject[key] = objectToMerge[key];
		}

		return defaultObject;
	}

	function clone(object) {
		return merge({}, object);
	}

	//http://stackoverflow.com/questions/3561493/is-there-a-regexp-escape-function-in-javascript/3561711#3561711
	function regexEscape(s) {
		return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, "\\$&");
	}

	function createProceduralRule(startIndex, direction, tokenRequirements, caseInsensitive) {
		tokenRequirements = tokenRequirements.slice(0);
		return function(tokens) {
			var tokenIndexStart = startIndex,
				j,
				expected,
				actual;

			if (direction === 1) {
				tokenRequirements.reverse();
			}

			for (j = 0; j < tokenRequirements.length; j++) {
				actual = tokens[tokenIndexStart + (j * direction)];
				expected = tokenRequirements[tokenRequirements.length - 1 - j];

				if (actual === undefined) {
					if (expected["optional"] !== undefined && expected.optional) {
						tokenIndexStart -= direction;
					} else {
						return false;
					}
				} else if (actual.name === expected.token && (expected["values"] === undefined || contains(expected.values, actual.value, caseInsensitive))) {
					//derp
					continue;
				} else if (expected["optional"] !== undefined && expected.optional) {
					tokenIndexStart -= direction; //we need to reevaluate against this token again
				} else {
					return false;
				}
			}

			return true;
		};
	}

	function createBetweenRule(startIndex, opener, closer, caseInsensitive) {
		return function(tokens) {
			var index = startIndex,
				token,
				success = false;

			//check to the left: if we run into a closer or never run into an opener, fail
			while ((token = tokens[--index]) !== undefined) {
				if (token.name === closer.token && contains(closer.values, token.value)) {
					if (token.name === opener.token && contains(opener.values, token.value, caseInsensitive)) {
						//if the closer is the same as the opener that's okay
						success = true;
						break;
					}

					return false;
				}

				if (token.name === opener.token && contains(opener.values, token.value, caseInsensitive)) {
					success = true;
					break;
				}
			}

			if (!success) {
				return false;
			}

			//check to the right for the closer
			index = startIndex;
			while ((token = tokens[++index]) !== undefined) {
				if (token.name === opener.token && contains(opener.values, token.value, caseInsensitive)) {
					if (token.name === closer.token && contains(closer.values, token.value, caseInsensitive)) {
						//if the closer is the same as the opener that's okay
						success = true;
						break;
					}

					return false;
				}

				if (token.name === closer.token && contains(closer.values, token.value, caseInsensitive)) {
					success = true;
					break;
				}
			}

			return success;
		};
	}

	function matchWord(context, wordMap, tokenName, doNotRead) {
		var current = context.reader.current(),
			i,
			word,
			peek,
			line = context.reader.getLine(),
			column = context.reader.getColumn();

		wordMap = wordMap || [];
		if (context.language.caseInsensitive) {
			current = current.toUpperCase();
		}

		if (!wordMap[current]) {
			return null;
		}

		wordMap = wordMap[current];
		for (i = 0; i < wordMap.length; i++) {
			word = wordMap[i].value;

			peek = current + context.reader.peek(word.length);
			if (word === peek || wordMap[i].regex.test(peek)) {
				return context.createToken(
					tokenName,
					context.reader.current() + context.reader[doNotRead ? "peek" : "read"](word.length - 1),
					line,
					column
				);
			}
		}

		return null;
	}

	//gets the next token in the specified direction while matcher matches the current token
	function getNextWhile(tokens, index, direction, matcher) {
		var count = 1,
			token;

		direction = direction || 1;
		while (token = tokens[index + (direction * count++)]) {
			if (!matcher(token)) {
				return token;
			}
		}

		return undefined;
	}

	//this is crucial for performance
	function createHashMap(wordMap, boundary, caseInsensitive) {
		//creates a hash table where the hash is the first character of the word
		var newMap = { },
			i,
			word,
			firstChar;

		for (i = 0; i < wordMap.length; i++) {
			word = caseInsensitive ? wordMap[i].toUpperCase() : wordMap[i];
			firstChar = word.charAt(0);
			if (!newMap[firstChar]) {
				newMap[firstChar] = [];
			}

			newMap[firstChar].push({ value: word, regex: new RegExp("^" + regexEscape(word) + boundary, caseInsensitive ? "i" : "") });
		}

		return newMap;
	}

	function defaultNumberParser(context) {
		var current = context.reader.current(),
			number,
			line = context.reader.getLine(),
			column = context.reader.getColumn(),
			allowDecimal = true,
			peek;

		if (!/\d/.test(current)) {
			//is it a decimal followed by a number?
			if (current !== "." || !/\d/.test(context.reader.peek())) {
				return null;
			}

			//decimal without leading zero
			number = current + context.reader.read();
			allowDecimal = false;
		} else {
			number = current;
			if (current === "0" && context.reader.peek() !== ".") {
				//hex or octal
				allowDecimal = false;
			}
		}

		//easy way out: read until it's not a number or letter
		//this will work for hex (0xef), octal (012), decimal and scientific notation (1e3)
		//anything else and you're on your own

		while ((peek = context.reader.peek()) !== context.reader.EOF) {
			if (!/[A-Za-z0-9]/.test(peek)) {
				if (peek === "." && allowDecimal && /\d$/.test(context.reader.peek(2))) {
					number += context.reader.read();
					allowDecimal = false;
					continue;
				}

				break;
			}

			number += context.reader.read();
		}

		return context.createToken("number", number, line, column);
	}

	function fireEvent(eventName, highlighter, eventContext) {
		var delegates = events[eventName] || [],
			i;

		for (i = 0; i < delegates.length; i++) {
			delegates[i].call(highlighter, eventContext);
		}
	}

	function Highlighter(options) {
		this.options = merge(clone(globalOptions), options);
	}

	Highlighter.prototype = (function() {
		var parseNextToken = (function() {
			function isIdentMatch(context) {
				return context.language.identFirstLetter && context.language.identFirstLetter.test(context.reader.current());
			}

			//token parsing functions
			function parseKeyword(context) {
				return matchWord(context, context.language.keywords, "keyword");
			}

			function parseCustomTokens(context) {
				var tokenName,
					token;
				if (context.language.customTokens === undefined) {
					return null;
				}

				for (tokenName in context.language.customTokens) {
					token = matchWord(context, context.language.customTokens[tokenName], tokenName);
					if (token !== null) {
						return token;
					}
				}

				return null;
			}

			function parseOperator(context) {
				return matchWord(context, context.language.operators, "operator");
			}

			function parsePunctuation(context) {
				var current = context.reader.current();
				if (context.language.punctuation.test(regexEscape(current))) {
					return context.createToken("punctuation", current, context.reader.getLine(), context.reader.getColumn());
				}

				return null;
			}

			function parseIdent(context) {
				var ident,
					peek,
					line = context.reader.getLine(),
					column = context.reader.getColumn();

				if (!isIdentMatch(context)) {
					return null;
				}

				ident = context.reader.current();
				while ((peek = context.reader.peek()) !== context.reader.EOF) {
					if (!context.language.identAfterFirstLetter.test(peek)) {
						break;
					}

					ident += context.reader.read();
				}

				return context.createToken("ident", ident, line, column);
			}

			function parseDefault(context) {
				if (context.defaultData.text === "") {
					//new default token
					context.defaultData.line = context.reader.getLine();
					context.defaultData.column = context.reader.getColumn();
				}

				context.defaultData.text += context.reader.current();
				return null;
			}

			function parseScopes(context) {
				var current = context.reader.current(),
					tokenName,
					specificScopes,
					j,
					opener,
					line,
					column,
					continuation,
					value;

				for (tokenName in context.language.scopes) {
					specificScopes = context.language.scopes[tokenName];
					for (j = 0; j < specificScopes.length; j++) {
						opener = specificScopes[j][0];

						value = current + context.reader.peek(opener.length - 1);

						if (opener !== value && (!context.language.caseInsensitive || value.toUpperCase() !== opener.toUpperCase())) {
							continue;
						}

						line = context.reader.getLine(), column = context.reader.getColumn();
						context.reader.read(opener.length - 1);
						continuation = getScopeReaderFunction(specificScopes[j], tokenName);
						return continuation(context, continuation, value, line, column);
					}
				}

				return null;
			}

			function parseNumber(context) {
				return context.language.numberParser(context);
			}

			function parseCustomRules(context) {
				var customRules = context.language.customParseRules,
					i,
					token;

				if (customRules === undefined) {
					return null;
				}

				for (i = 0; i < customRules.length; i++) {
					token = customRules[i](context);
					if (token) {
						return token;
					}
				}

				return null;
			}

			return function(context) {
				if (context.language.doNotParse.test(context.reader.current())) {
					return parseDefault(context);
				}

				return parseCustomRules(context)
					|| parseCustomTokens(context)
					|| parseKeyword(context)
					|| parseScopes(context)
					|| parseIdent(context)
					|| parseNumber(context)
					|| parseOperator(context)
					|| parsePunctuation(context)
					|| parseDefault(context);
			}
		}());

		function getScopeReaderFunction(scope, tokenName) {
			var escapeSequences = scope[2] || [],
				closerLength = scope[1].length,
				closer = typeof(scope[1]) === "string" ? new RegExp(regexEscape(scope[1])) : scope[1].regex,
				zeroWidth = scope[3] || false;

			//processCurrent indicates that this is being called from a continuation
			//which means that we need to process the current char, rather than peeking at the next
			return function(context, continuation, buffer, line, column, processCurrent) {
				var foundCloser = false;
				buffer = buffer || "";

				processCurrent = processCurrent ? 1 : 0;

				function process(processCurrent) {
					//check for escape sequences
					var peekValue,
						current = context.reader.current(),
						i;

					for (i = 0; i < escapeSequences.length; i++) {
						peekValue = (processCurrent ? current : "") + context.reader.peek(escapeSequences[i].length - processCurrent);
						if (peekValue === escapeSequences[i]) {
							buffer += context.reader.read(peekValue.length - processCurrent);
							return true;
						}
					}

					peekValue = (processCurrent ? current : "") + context.reader.peek(closerLength - processCurrent);
					if (closer.test(peekValue)) {
						foundCloser = true;
						return false;
					}

					buffer += processCurrent ? current : context.reader.read();
					return true;
				};

				if (!processCurrent || process(true)) {
					while (context.reader.peek() !== context.reader.EOF && process(false)) { }
				}

				if (processCurrent) {
					buffer += context.reader.current();
					context.reader.read();
				} else {
					buffer += zeroWidth || context.reader.peek() === context.reader.EOF ? "" : context.reader.read(closerLength);
				}

				if (!foundCloser) {
					//we need to signal to the context that this scope was never properly closed
					//this has significance for partial parses (e.g. for nested languages)
					context.continuation = continuation;
				}

				return context.createToken(tokenName, buffer, line, column);
			};
		}

		//called before processing the current
		function switchToEmbeddedLanguageIfNecessary(context) {
			var i,
				embeddedLanguage;

			for (i = 0; i < context.language.embeddedLanguages.length; i++) {
				if (!languages[context.language.embeddedLanguages[i].language]) {
					//unregistered language
					continue;
				}

				embeddedLanguage = clone(context.language.embeddedLanguages[i]);

				if (embeddedLanguage.switchTo(context)) {
					embeddedLanguage.oldItems = clone(context.items);
					context.embeddedLanguageStack.push(embeddedLanguage);
					context.language = languages[embeddedLanguage.language];
					context.items = merge(context.items, clone(context.language.contextItems));
					break;
				}
			}
		}

		//called after processing the current
		function switchBackFromEmbeddedLanguageIfNecessary(context) {
			var current = last(context.embeddedLanguageStack),
				lang;

			if (current && current.switchBack(context)) {
				context.language = languages[current.parentLanguage];
				lang = context.embeddedLanguageStack.pop();

				//restore old items
				context.items = clone(lang.oldItems);
				lang.oldItems = {};
			}
		}

		function tokenize(unhighlightedCode, language, partialContext, options) {
			var tokens = [],
				context,
				continuation,
				token;

			fireEvent("beforeTokenize", this, { code: unhighlightedCode, language: language });
			context = {
				reader: createCodeReader(unhighlightedCode),
				language: language,
				items: clone(language.contextItems),
				token: function(index) { return tokens[index]; },
				getAllTokens: function() { return tokens.slice(0); },
				count: function() { return tokens.length; },
				options: options,
				embeddedLanguageStack: [],

				defaultData: {
					text: "",
					line: 1,
					column: 1
				},
				createToken: function(name, value, line, column) {
					return {
						name: name,
						line: line,
						value: isIe ? value.replace(/\n/g, "\r") : value,
						column: column,
						language: this.language.name
					};
				}
			};

			//if continuation is given, then we need to pick up where we left off from a previous parse
			//basically it indicates that a scope was never closed, so we need to continue that scope
			if (partialContext.continuation) {
				continuation = partialContext.continuation;
				partialContext.continuation = null;
				tokens.push(continuation(context, continuation, "", context.reader.getLine(), context.reader.getColumn(), true));
			}

			while (!context.reader.isEof()) {
				switchToEmbeddedLanguageIfNecessary(context);
				token = parseNextToken(context);

				//flush default data if needed (in pretty much all languages this is just whitespace)
				if (token !== null) {
					if (context.defaultData.text !== "") {
						tokens.push(context.createToken("default", context.defaultData.text, context.defaultData.line, context.defaultData.column));
						context.defaultData.text = "";
					}

					if (token[0] !== undefined) {
						//multiple tokens
						tokens = tokens.concat(token);
					} else {
						//single token
						tokens.push(token);
					}
				}

				switchBackFromEmbeddedLanguageIfNecessary(context);
				context.reader.read();
			}

			//append the last default token, if necessary
			if (context.defaultData.text !== "") {
				tokens.push(context.createToken("default", context.defaultData.text, context.defaultData.line, context.defaultData.column));
			}

			fireEvent("afterTokenize", this, { code: unhighlightedCode, parserContext: context });
			return context;
		}

		function createAnalyzerContext(parserContext, partialContext, options) {
			var nodes = [],
				prepareText = function() {
					var nbsp, tab;
					if (options.showWhitespace) {
						nbsp = String.fromCharCode(0xB7);
						tab = new Array(options.tabWidth).join(String.fromCharCode(0x2014)) + String.fromCharCode(0x2192);
					} else {
						nbsp = String.fromCharCode(0xA0);
						tab = new Array(options.tabWidth + 1).join(nbsp);
					}

					return function(token) {
						var value = token.value.split(" ").join(nbsp),
							tabIndex,
							lastNewlineColumn,
							actualColumn,
							tabLength;

						//tabstop madness: replace \t with the appropriate number of characters, depending on the tabWidth option and its relative position in the line
						while ((tabIndex = value.indexOf("\t")) >= 0) {
							lastNewlineColumn = value.lastIndexOf(EOL, tabIndex);
							actualColumn = lastNewlineColumn === -1 ? tabIndex : tabIndex - lastNewlineColumn - 1;
							tabLength = options.tabWidth - (actualColumn % options.tabWidth); //actual length of the TAB character

							value = value.substring(0, tabIndex) + tab.substring(options.tabWidth - tabLength) + value.substring(tabIndex + 1);
						}

						return value;
					};
				}();

			return {
				tokens: (partialContext.tokens || []).concat(parserContext.getAllTokens()),
				index: partialContext.index ? partialContext.index + 1 : 0,
				language: null,
				getAnalyzer: EMPTY,
				options: options,
				continuation: parserContext.continuation,
				addNode: function(node) { nodes.push(node); },
				createTextNode: function(token) { return document.createTextNode(prepareText(token)); },
				getNodes: function() { return nodes; },
				resetNodes: function() { nodes = []; },
				items: parserContext.items
			};
		}

		//partialContext allows us to perform a partial parse, and then pick up where we left off at a later time
		//this functionality enables nested highlights (language within a language, e.g. PHP within HTML followed by more PHP)
		function highlightText(unhighlightedCode, languageId, partialContext) {
			var language = languages[languageId],
				analyzerContext;

			partialContext = partialContext || { };
			if (language === undefined) {
				//use default language if one wasn't specified or hasn't been registered
				language = languages[DEFAULT_LANGUAGE];
			}

			fireEvent("beforeHighlight", this, { code: unhighlightedCode, language: language, previousContext: partialContext });

			analyzerContext = createAnalyzerContext(
				tokenize.call(this, unhighlightedCode, language, partialContext, this.options),
				partialContext,
				this.options
			);

			analyze.call(this, analyzerContext, partialContext.index ? partialContext.index + 1 : 0);

			fireEvent("afterHighlight", this, { analyzerContext: analyzerContext });

			return analyzerContext;
		}

		function createContainer(ctx) {
			var container = document.createElement("span");
			container.className = ctx.options.classPrefix + ctx.language.name;
			return container;
		}

		function analyze(analyzerContext, startIndex) {
			var nodes,
				lastIndex,
				container,
				i,
				tokenName,
				func,
				language,
				analyzer;

			fireEvent("beforeAnalyze", this, { analyzerContext: analyzerContext });

			if (analyzerContext.tokens.length > 0) {
				analyzerContext.language = languages[analyzerContext.tokens[0].language] || languages[DEFAULT_LANGUAGE];;
				nodes = [];
				lastIndex = 0;
				container = createContainer(analyzerContext);

				for (i = startIndex; i < analyzerContext.tokens.length; i++) {
					language = languages[analyzerContext.tokens[i].language] || languages[DEFAULT_LANGUAGE];
					if (language.name !== analyzerContext.language.name) {
						appendAll(container, analyzerContext.getNodes());
						analyzerContext.resetNodes();

						nodes.push(container);
						analyzerContext.language = language;
						container = createContainer(analyzerContext);
					}

					analyzerContext.index = i;
					tokenName = analyzerContext.tokens[i].name;
					func = "handle_" + tokenName;

					analyzer = analyzerContext.getAnalyzer.call(analyzerContext) || analyzerContext.language.analyzer;
					analyzer[func] ? analyzer[func](analyzerContext) : analyzer.handleToken(analyzerContext);
				}

				//append the last nodes, and add the final nodes to the context
				appendAll(container, analyzerContext.getNodes());
				nodes.push(container);
				analyzerContext.resetNodes();
				for (i = 0; i < nodes.length; i++) {
					analyzerContext.addNode(nodes[i]);
				}
			}

			fireEvent("afterAnalyze", this, { analyzerContext: analyzerContext });
		}

		return {
			//matches the language of the node to highlight
			matchSunlightNode: function() {
				var regex;

				return function(node) {
					if (!regex) {
						regex = new RegExp("(?:\\s|^)" + this.options.classPrefix + "highlight-(\\S+)(?:\\s|$)");
					}

					return regex.exec(node.className);
				};
			}(),

			//determines if the node has already been highlighted
			isAlreadyHighlighted: function() {
				var regex;
				return function(node) {
					if (!regex) {
						regex = new RegExp("(?:\\s|^)" + this.options.classPrefix + "highlighted(?:\\s|$)");
					}

					return regex.test(node.className);
				};
			}(),

			//highlights a block of text
			highlight: function(code, languageId) { return highlightText.call(this, code, languageId); },

			//recursively highlights a DOM node
			highlightNode: function highlightRecursive(node) {
				var match,
					languageId,
					currentNodeCount,
					j,
					nodes,
					k,
					partialContext,
					container,
					codeContainer;

				if (this.isAlreadyHighlighted(node) || (match = this.matchSunlightNode(node)) === null) {
					return;
				}

				languageId = match[1];
				currentNodeCount = 0;
				fireEvent("beforeHighlightNode", this, { node: node });
				for (j = 0; j < node.childNodes.length; j++) {
					if (node.childNodes[j].nodeType === 3) {
						//text nodes
						partialContext = highlightText.call(this, node.childNodes[j].nodeValue, languageId, partialContext);
						HIGHLIGHTED_NODE_COUNT++;
						currentNodeCount = currentNodeCount || HIGHLIGHTED_NODE_COUNT;
						nodes = partialContext.getNodes();

						node.replaceChild(nodes[0], node.childNodes[j]);
						for (k = 1; k < nodes.length; k++) {
							node.insertBefore(nodes[k], nodes[k - 1].nextSibling);
						}
					} else if (node.childNodes[j].nodeType === 1) {
						//element nodes
						highlightRecursive.call(this, node.childNodes[j]);
					}
				}

				//indicate that this node has been highlighted
				node.className += " " + this.options.classPrefix + "highlighted";

				//if the node is block level, we put it in a container, otherwise we just leave it alone
				if (getComputedStyle(node, "display") === "block") {
					container = document.createElement("div");
					container.className = this.options.classPrefix + "container";

					codeContainer = document.createElement("div");
					codeContainer.className = this.options.classPrefix + "code-container";

					//apply max height if specified in options
					if (this.options.maxHeight !== false) {
						codeContainer.style.overflowY = "auto";
						codeContainer.style.maxHeight = this.options.maxHeight + (/^\d+$/.test(this.options.maxHeight) ? "px" : "");
					}

					container.appendChild(codeContainer);

					node.parentNode.insertBefore(codeContainer, node);
					node.parentNode.removeChild(node);
					codeContainer.appendChild(node);

					codeContainer.parentNode.insertBefore(container, codeContainer);
					codeContainer.parentNode.removeChild(codeContainer);
					container.appendChild(codeContainer);
				}

				fireEvent("afterHighlightNode", this, {
					container: container,
					codeContainer: codeContainer,
					node: node,
					count: currentNodeCount
				});
			}
		};
	}());

	//public facing object
	window.Sunlight = {
		version: "1.18",
		Highlighter: Highlighter,
		createAnalyzer: function() { return create(defaultAnalyzer); },
		globalOptions: globalOptions,

		highlightAll: function(options) {
			var highlighter = new Highlighter(options),
				tags = document.getElementsByTagName("*"),
				i;

			for (i = 0; i < tags.length; i++) {
				highlighter.highlightNode(tags[i]);
			}
		},

		registerLanguage: function(languageId, languageData) {
			var tokenName,
				embeddedLanguages,
				languageName;

			if (!languageId) {
				throw "Languages must be registered with an identifier, e.g. \"php\" for PHP";
			}

			languageData = merge(merge({}, languageDefaults), languageData);
			languageData.name = languageId;

			//transform keywords, operators and custom tokens into a hash map
			languageData.keywords = createHashMap(languageData.keywords || [], "\\b", languageData.caseInsensitive);
			languageData.operators = createHashMap(languageData.operators || [], "", languageData.caseInsensitive);
			for (tokenName in languageData.customTokens) {
				languageData.customTokens[tokenName] = createHashMap(
					languageData.customTokens[tokenName].values,
					languageData.customTokens[tokenName].boundary,
					languageData.caseInsensitive
				);
			}

			//convert the embedded language object to an easier-to-use array
			embeddedLanguages = [];
			for (languageName in languageData.embeddedLanguages) {
				embeddedLanguages.push({
					parentLanguage: languageData.name,
					language: languageName,
					switchTo: languageData.embeddedLanguages[languageName].switchTo,
					switchBack: languageData.embeddedLanguages[languageName].switchBack
				});
			}

			languageData.embeddedLanguages = embeddedLanguages;

			languages[languageData.name] = languageData;
		},

		isRegistered: function(languageId) { return languages[languageId] !== undefined; },

		bind: function(event, callback) {
			if (!events[event]) {
				throw "Unknown event \"" + event + "\"";
			}

			events[event].push(callback);
		},

		util: {
			last: last,
			regexEscape: regexEscape,
			eol: EOL,
			clone: clone,
			escapeSequences: ["\\n", "\\t", "\\r", "\\\\", "\\v", "\\f"],
			contains: contains,
			matchWord: matchWord,
			createHashMap: createHashMap,
			createBetweenRule: createBetweenRule,
			createProceduralRule: createProceduralRule,
			getNextNonWsToken: function(tokens, index) { return getNextWhile(tokens, index, 1, function(token) { return token.name === "default"; }); },
			getPreviousNonWsToken: function(tokens, index) { return getNextWhile(tokens, index, -1, function(token) { return token.name === "default"; }); },
			getNextWhile: function(tokens, index, matcher) { return getNextWhile(tokens, index, 1, matcher); },
			getPreviousWhile: function(tokens, index, matcher) { return getNextWhile(tokens, index, -1, matcher); },
			whitespace: { token: "default", optional: true },
			getComputedStyle: getComputedStyle
		}
	};

	//register the default language
	window.Sunlight.registerLanguage(DEFAULT_LANGUAGE, { punctuation: /(?!x)x/, numberParser: EMPTY });

}(this, document));


/**
* @author       Richard Davey <rich@photonstorm.com>
* @copyright    2016 Photon Storm Ltd.
* @license      {@link https://github.com/photonstorm/phaser/blob/master/license.txt|MIT License}
*/

/**
* FrameData is a container for Frame objects, which are the internal representation of animation data in Phaser.
*
* @class Phaser.FrameData
* @constructor
*/
Phaser.FrameData = function () {

    /**
    * @property {Array} _frames - Local array of frames.
    * @private
    */
    this._frames = [];

    /**
    * @property {Array} _frameNames - Local array of frame names for name to index conversions.
    * @private
    */
    this._frameNames = [];

};

Phaser.FrameData.prototype = {

    /**
    * Adds a new Frame to this FrameData collection. Typically called by the Animation.Parser and not directly.
    *
    * @method Phaser.FrameData#addFrame
    * @param {Phaser.Frame} frame - The frame to add to this FrameData set.
    * @return {Phaser.Frame} The frame that was just added.
    */
    addFrame: function (frame) {

        frame.index = this._frames.length;

        this._frames.push(frame);

        if (frame.name !== '')
        {
            this._frameNames[frame.name] = frame.index;
        }

        return frame;

    },

    /**
    * Get a Frame by its numerical index.
    *
    * @method Phaser.FrameData#getFrame
    * @param {number} index - The index of the frame you want to get.
    * @return {Phaser.Frame} The frame, if found.
    */
    getFrame: function (index) {

        if (index >= this._frames.length)
        {
            index = 0;
        }

        return this._frames[index];

    },

    /**
    * Get a Frame by its frame name.
    *
    * @method Phaser.FrameData#getFrameByName
    * @param {string} name - The name of the frame you want to get.
    * @return {Phaser.Frame} The frame, if found.
    */
    getFrameByName: function (name) {

        if (typeof this._frameNames[name] === 'number')
        {
            return this._frames[this._frameNames[name]];
        }

        return null;

    },

    /**
    * Check if there is a Frame with the given name.
    *
    * @method Phaser.FrameData#checkFrameName
    * @param {string} name - The name of the frame you want to check.
    * @return {boolean} True if the frame is found, otherwise false.
    */
    checkFrameName: function (name) {

        if (this._frameNames[name] == null)
        {
            return false;
        }

        return true;

    },

    /**
     * Makes a copy of this FrameData including copies (not references) to all of the Frames it contains.
     *
     * @method Phaser.FrameData#clone
     * @return {Phaser.FrameData} A clone of this object, including clones of the Frame objects it contains.
     */
    clone: function () {

        var output = new Phaser.FrameData();

        //  No input array, so we loop through all frames
        for (var i = 0; i < this._frames.length; i++)
        {
            output._frames.push(this._frames[i].clone());
        }

        for (var p in this._frameNames)
        {
            if (this._frameNames.hasOwnProperty(p))
            {
                output._frameNames.push(this._frameNames[p]);
            }
        }

        return output;

    },

    /**
    * Returns a range of frames based on the given start and end frame indexes and returns them in an Array.
    *
    * @method Phaser.FrameData#getFrameRange
    * @param {number} start - The starting frame index.
    * @param {number} end - The ending frame index.
    * @param {Array} [output] - If given the results will be appended to the end of this array otherwise a new array will be created.
    * @return {Array} An array of Frames between the start and end index values, or an empty array if none were found.
    */
    getFrameRange: function (start, end, output) {

        if (output === undefined) { output = []; }

        for (var i = start; i <= end; i++)
        {
            output.push(this._frames[i]);
        }

        return output;

    },

    /**
    * Returns all of the Frames in this FrameData set where the frame index is found in the input array.
    * The frames are returned in the output array, or if none is provided in a new Array object.
    *
    * @method Phaser.FrameData#getFrames
    * @param {Array} [frames] - An Array containing the indexes of the frames to retrieve. If the array is empty or undefined then all frames in the FrameData are returned.
    * @param {boolean} [useNumericIndex=true] - Are the given frames using numeric indexes (default) or strings? (false)
    * @param {Array} [output] - If given the results will be appended to the end of this array otherwise a new array will be created.
    * @return {Array} An array of all Frames in this FrameData set matching the given names or IDs.
    */
    getFrames: function (frames, useNumericIndex, output) {

        if (useNumericIndex === undefined) { useNumericIndex = true; }
        if (output === undefined) { output = []; }

        if (frames === undefined || frames.length === 0)
        {
            //  No input array, so we loop through all frames
            for (var i = 0; i < this._frames.length; i++)
            {
                //  We only need the indexes
                output.push(this._frames[i]);
            }
        }
        else
        {
            //  Input array given, loop through that instead
            for (var i = 0; i < frames.length; i++)
            {
                //  Does the input array contain names or indexes?
                if (useNumericIndex)
                {
                    //  The actual frame
                    output.push(this.getFrame(frames[i]));
                }
                else
                {
                    //  The actual frame
                    output.push(this.getFrameByName(frames[i]));
                }
            }
        }

        return output;

    },

    /**
    * Returns all of the Frame indexes in this FrameData set.
    * The frames indexes are returned in the output array, or if none is provided in a new Array object.
    *
    * @method Phaser.FrameData#getFrameIndexes
    * @param {Array} [frames] - An Array containing the indexes of the frames to retrieve. If undefined or the array is empty then all frames in the FrameData are returned.
    * @param {boolean} [useNumericIndex=true] - Are the given frames using numeric indexes (default) or strings? (false)
    * @param {Array} [output] - If given the results will be appended to the end of this array otherwise a new array will be created.
    * @return {Array} An array of all Frame indexes matching the given names or IDs.
    */
    getFrameIndexes: function (frames, useNumericIndex, output) {

        if (useNumericIndex === undefined) { useNumericIndex = true; }
        if (output === undefined) { output = []; }

        if (frames === undefined || frames.length === 0)
        {
            //  No frames array, so we loop through all frames
            for (var i = 0; i < this._frames.length; i++)
            {
                output.push(this._frames[i].index);
            }
        }
        else
        {
            //  Input array given, loop through that instead
            for (var i = 0; i < frames.length; i++)
            {
                //  Does the frames array contain names or indexes?
                if (useNumericIndex && this._frames[frames[i]])
                {
                    output.push(this._frames[frames[i]].index);
                }
                else
                {
                    if (this.getFrameByName(frames[i]))
                    {
                        output.push(this.getFrameByName(frames[i]).index);
                    }
                }
            }
        }

        return output;

    },

    /**
    * Destroys this FrameData collection by nulling the _frames and _frameNames arrays.
    *
    * @method Phaser.FrameData#destroy
    */
    destroy: function () {

        this._frames = null;
        this._frameNames = null;

    }

};

Phaser.FrameData.prototype.constructor = Phaser.FrameData;

/**
* @name Phaser.FrameData#total
* @property {number} total - The total number of frames in this FrameData set.
* @readonly
*/
Object.defineProperty(Phaser.FrameData.prototype, "total", {

    get: function () {
        return this._frames.length;
    }

});


var Class = require('../../utils/Class');
var TimelineBuilder = require('../builder/TimelineBuilder');
var TweenBuilder = require('../builder/TweenBuilder');

var TweenManager = new Class({

    initialize:

    function TweenManager (scene)
    {
        //  The Scene the Tween Manager belongs to (tweens are Scene specific, not Game global)
        this.scene = scene;

        this.timeScale = 1;

        this._add = [];
        this._pending = [];
        this._active = [];
        this._destroy = [];

        this._toProcess = 0;
    },

    //  Scene is starting up
    boot: function ()
    {
        this.timeScale = 1;
    },

    //  Create a Tween Timeline and return it, but do NOT add it to the active or pending Tween lists
    createTimeline: function (config)
    {
        return TimelineBuilder(this, config);
    },

    //  Create a Tween Timeline and add it to the active Tween list
    timeline: function (config)
    {
        var timeline = TimelineBuilder(this, config);

        this._add.push(timeline);

        this._toProcess++;

        return timeline;
    },

    //  Create a Tween and return it, but do NOT add it to the active or pending Tween lists
    create: function (config)
    {
        return TweenBuilder(this, config);
    },

    //  Create a Tween and add it to the active Tween list
    add: function (config)
    {
        var tween = TweenBuilder(this, config);

        this._add.push(tween);

        this._toProcess++;

        return tween;
    },

    //  Add an existing tween into the active Tween list
    existing: function (tween)
    {
        this._add.push(tween);

        this._toProcess++;

        return this;
    },

    begin: function ()
    {
        if (this._toProcess === 0)
        {
            //  Quick bail
            return;
        }

        var list = this._destroy;
        var active = this._active;
        var i;
        var tween;

        //  Clear the 'destroy' list
        for (i = 0; i < list.length; i++)
        {
            tween = list[i];

            //  Remove from the 'active' array
            var idx = active.indexOf(tween);

            if (idx !== -1)
            {
                active.splice(idx, 1);
            }
        }

        list.length = 0;

        //  Process the addition list
        //  This stops callbacks and out of sync events from populating the active array mid-way during the update

        list = this._add;

        for (i = 0; i < list.length; i++)
        {
            tween = list[i];

            //  Return true if the Tween should be started right away, otherwise false
            if (tween.init())
            {
                tween.play();

                this._active.push(tween);
            }
            else
            {
                this._pending.push(tween);
            }
        }

        list.length = 0;

        this._toProcess = 0;
    },

    update: function (timestamp, delta)
    {
        //  Process active tweens
        var list = this._active;
        var tween;

        //  Scale the delta
        delta *= this.timeScale;

        for (var i = 0; i < list.length; i++)
        {
            tween = list[i];

            //  If Tween.update returns 'true' then it means it has completed,
            //  so move it to the destroy list
            if (tween.update(timestamp, delta))
            {
                this._destroy.push(tween);
                this._toProcess++;
            }
        }
    },

    makeActive: function (tween)
    {
        var idx = this._pending.indexOf(tween);

        if (idx !== -1)
        {
            this._pending.splice(idx, 1);
        }

        this._add.push(tween);

        this._toProcess++;
    },

    setGlobalTimeScale: require('./components/SetGlobalTimeScale'),
    getGlobalTimeScale: require('./components/GetGlobalTimeScale'),
    getAllTweens: require('./components/GetAllTweens'),
    getTweensOf: require('./components/GetTweensOf'),
    isTweening: require('./components/IsTweening'),
    killAll: require('./components/KillAll'),
    killTweensOf: require('./components/KillTweensOf'),
    pauseAll: require('./components/PauseAll'),
    resumeAll: require('./components/ResumeAll'),
    each: require('./components/Each'),
    shutdown: require('./components/Shutdown'),
    destroy: require('./components/Destroy')

    // TODO: kill: function (vars, target)
});

module.exports = TweenManager;


<?php
    class Constant
    {
        public $processor;
        public $name; // TEXTURE_ATLAS_JSON_ARRAY, PHYSICS_PHASER_JSON, etc
        public $types = []; // an array containing the one single type the const can be
        public $help = [];
        public $line; // number, line number in the source file this is found on?

        public function __construct($processor, $block)
        {
            $this->processor = $processor;

            //  Because zero offset + allowing for final line
            $this->line = $block->end + 2;

            //  Phaser.Cache.TEXTURE = 3;
            $name = $block->code;
            $period = strrpos($name, '.');
            $equals = strrpos($name, '=');

            if ($period > 0 && $equals > 0)
            {
                $len = $equals - $period - 1;
                $name = substr($name, $period + 1, $len);
            }
            else if ($period > 0)
            {
                $name = substr($name, $period + 1, -1);
            }

            $this->name = trim($name);

            $line = $block->getLine('@type');

            if ($line && preg_match("/.*@type\s?{(\S*)}/", $line, $output))
            {
                $this->types = explode('|', $output[1]);
            }
        }

        public function getArray()
        {
            return array(
                'name' => $this->name,
                'type' => $this->types[0],
                'help' => implode('\n', $this->help),
                'line' => $this->line
            );

            return $a;

        }

        public function getJSON()
        {
            return json_encode($this->getArray());
        }

    }
?>


                                 Apache License
                           Version 2.0, January 2004
                        http://www.apache.org/licenses/

   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

   1. Definitions.

      "License" shall mean the terms and conditions for use, reproduction,
      and distribution as defined by Sections 1 through 9 of this document.

      "Licensor" shall mean the copyright owner or entity authorized by
      the copyright owner that is granting the License.

      "Legal Entity" shall mean the union of the acting entity and all
      other entities that control, are controlled by, or are under common
      control with that entity. For the purposes of this definition,
      "control" means (i) the power, direct or indirect, to cause the
      direction or management of such entity, whether by contract or
      otherwise, or (ii) ownership of fifty percent (50%) or more of the
      outstanding shares, or (iii) beneficial ownership of such entity.

      "You" (or "Your") shall mean an individual or Legal Entity
      exercising permissions granted by this License.

      "Source" form shall mean the preferred form for making modifications,
      including but not limited to software source code, documentation
      source, and configuration files.

      "Object" form shall mean any form resulting from mechanical
      transformation or translation of a Source form, including but
      not limited to compiled object code, generated documentation,
      and conversions to other media types.

      "Work" shall mean the work of authorship, whether in Source or
      Object form, made available under the License, as indicated by a
      copyright notice that is included in or attached to the work
      (an example is provided in the Appendix below).

      "Derivative Works" shall mean any work, whether in Source or Object
      form, that is based on (or derived from) the Work and for which the
      editorial revisions, annotations, elaborations, or other modifications
      represent, as a whole, an original work of authorship. For the purposes
      of this License, Derivative Works shall not include works that remain
      separable from, or merely link (or bind by name) to the interfaces of,
      the Work and Derivative Works thereof.

      "Contribution" shall mean any work of authorship, including
      the original version of the Work and any modifications or additions
      to that Work or Derivative Works thereof, that is intentionally
      submitted to Licensor for inclusion in the Work by the copyright owner
      or by an individual or Legal Entity authorized to submit on behalf of
      the copyright owner. For the purposes of this definition, "submitted"
      means any form of electronic, verbal, or written communication sent
      to the Licensor or its representatives, including but not limited to
      communication on electronic mailing lists, source code control systems,
      and issue tracking systems that are managed by, or on behalf of, the
      Licensor for the purpose of discussing and improving the Work, but
      excluding communication that is conspicuously marked or otherwise
      designated in writing by the copyright owner as "Not a Contribution."

      "Contributor" shall mean Licensor and any individual or Legal Entity
      on behalf of whom a Contribution has been received by Licensor and
      subsequently incorporated within the Work.

   2. Grant of Copyright License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      copyright license to reproduce, prepare Derivative Works of,
      publicly display, publicly perform, sublicense, and distribute the
      Work and such Derivative Works in Source or Object form.

   3. Grant of Patent License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      (except as stated in this section) patent license to make, have made,
      use, offer to sell, sell, import, and otherwise transfer the Work,
      where such license applies only to those patent claims licensable
      by such Contributor that are necessarily infringed by their
      Contribution(s) alone or by combination of their Contribution(s)
      with the Work to which such Contribution(s) was submitted. If You
      institute patent litigation against any entity (including a
      cross-claim or counterclaim in a lawsuit) alleging that the Work
      or a Contribution incorporated within the Work constitutes direct
      or contributory patent infringement, then any patent licenses
      granted to You under this License for that Work shall terminate
      as of the date such litigation is filed.

   4. Redistribution. You may reproduce and distribute copies of the
      Work or Derivative Works thereof in any medium, with or without
      modifications, and in Source or Object form, provided that You
      meet the following conditions:

      (a) You must give any other recipients of the Work or
          Derivative Works a copy of this License; and

      (b) You must cause any modified files to carry prominent notices
          stating that You changed the files; and

      (c) You must retain, in the Source form of any Derivative Works
          that You distribute, all copyright, patent, trademark, and
          attribution notices from the Source form of the Work,
          excluding those notices that do not pertain to any part of
          the Derivative Works; and

      (d) If the Work includes a "NOTICE" text file as part of its
          distribution, then any Derivative Works that You distribute must
          include a readable copy of the attribution notices contained
          within such NOTICE file, excluding those notices that do not
          pertain to any part of the Derivative Works, in at least one
          of the following places: within a NOTICE text file distributed
          as part of the Derivative Works; within the Source form or
          documentation, if provided along with the Derivative Works; or,
          within a display generated by the Derivative Works, if and
          wherever such third-party notices normally appear. The contents
          of the NOTICE file are for informational purposes only and
          do not modify the License. You may add Your own attribution
          notices within Derivative Works that You distribute, alongside
          or as an addendum to the NOTICE text from the Work, provided
          that such additional attribution notices cannot be construed
          as modifying the License.

      You may add Your own copyright statement to Your modifications and
      may provide additional or different license terms and conditions
      for use, reproduction, or distribution of Your modifications, or
      for any such Derivative Works as a whole, provided Your use,
      reproduction, and distribution of the Work otherwise complies with
      the conditions stated in this License.

   5. Submission of Contributions. Unless You explicitly state otherwise,
      any Contribution intentionally submitted for inclusion in the Work
      by You to the Licensor shall be under the terms and conditions of
      this License, without any additional terms or conditions.
      Notwithstanding the above, nothing herein shall supersede or modify
      the terms of any separate license agreement you may have executed
      with Licensor regarding such Contributions.

   6. Trademarks. This License does not grant permission to use the trade
      names, trademarks, service marks, or product names of the Licensor,
      except as required for reasonable and customary use in describing the
      origin of the Work and reproducing the content of the NOTICE file.

   7. Disclaimer of Warranty. Unless required by applicable law or
      agreed to in writing, Licensor provides the Work (and each
      Contributor provides its Contributions) on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
      implied, including, without limitation, any warranties or conditions
      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
      PARTICULAR PURPOSE. You are solely responsible for determining the
      appropriateness of using or redistributing the Work and assume any
      risks associated with Your exercise of permissions under this License.

   8. Limitation of Liability. In no event and under no legal theory,
      whether in tort (including negligence), contract, or otherwise,
      unless required by applicable law (such as deliberate and grossly
      negligent acts) or agreed to in writing, shall any Contributor be
      liable to You for damages, including any direct, indirect, special,
      incidental, or consequential damages of any character arising as a
      result of this License or out of the use or inability to use the
      Work (including but not limited to damages for loss of goodwill,
      work stoppage, computer failure or malfunction, or any and all
      other commercial damages or losses), even if such Contributor
      has been advised of the possibility of such damages.

   9. Accepting Warranty or Additional Liability. While redistributing
      the Work or Derivative Works thereof, You may choose to offer,
      and charge a fee for, acceptance of support, warranty, indemnity,
      or other liability obligations and/or rights consistent with this
      License. However, in accepting such obligations, You may act only
      on Your own behalf and on Your sole responsibility, not on behalf
      of any other Contributor, and only if You agree to indemnify,
      defend, and hold each Contributor harmless for any liability
      incurred by, or claims asserted against, such Contributor by reason
      of your accepting any such warranty or additional liability.

   END OF TERMS AND CONDITIONS

   APPENDIX: How to apply the Apache License to your work.

      To apply the Apache License to your work, attach the following
      boilerplate notice, with the fields enclosed by brackets "[]"
      replaced with your own identifying information. (Don't include
      the brackets!)  The text should be enclosed in the appropriate
      comment syntax for the file format. We also recommend that a
      file or class name and description of purpose be included on the
      same "printed page" as the copyright notice for easier
      identification within third-party archives.

   Copyright [yyyy] [name of copyright owner]

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.


var Width = function (line)
{
    return Math.abs(line.x1 - line.x2);
};

module.exports = Width;


queue: function (tween)
{
tween.parent = this;
tween.parentIsTimeline = true;