<commit_before>import { stringify, parse } from 'src/util'
import SharedData from 'src/shared-data'
import { set, get } from '../util'
import Vue from 'vue'

export function initVuexBackend (hook, bridge, isLegacy) {
  const store = hook.store

  let snapshotsVm = null
  const updateSnapshotsVm = (state) => {
    snapshotsVm = new Vue({
      data: {
        $$state: state || {}
      },
      computed: store._vm.$options.computed
    })
  }

  const getStateSnapshot = (_store = store) => stringify(_store.state)

  let baseStateSnapshot, stateSnapshots, mutations, lastState
  let registeredModules = {}
  let allTimeModules = {}

  const earlyModules = hook.flushStoreModules()

  // Init additional state
  earlyModules.forEach(({ path, module, options }) => {
    if (!options || options.preserveState !== true) {
      const state = typeof module.state === 'function' ? module.state() : module.state
      const parentState = path.length === 1 ? hook.initialStore.state : get(hook.initialStore.state, path.slice(0, -1))
      set(parentState, path[path.length - 1], state)
    }
  })

  updateSnapshotsVm()

  function reset () {
    baseStateSnapshot = getStateSnapshot(hook.initialStore)
    mutations = []
    resetSnapshotCache()
  }

  function resetSnapshotCache () {
    stateSnapshots = [
      { index: -1, state: baseStateSnapshot }
    ]
  }

  reset()

  function addModule (path, module, options) {
    if (typeof path === 'string') path = [path]

    let state
    if (options && options.preserveState) {
      state = get(store.state, path)
    }
    if (!state) {
      state = typeof module.state === 'function' ? module.state() : module.state
    }
    if (!state) {
      state = {}
    }

    const key = path.join('/')
    registeredModules[key] = allTimeModules[key] = {
      path,
      module,
      options: {
        ...options,
        preserveState: false
      },
      state: stringify(state)
    }

    if (SharedData.recordVuex) {
      addMutation(`Register module: ${key}`, {
        path,
        module,
        options
      }, {
        registerModule: true
      })
    }
  }

  let origRegisterModule, origUnregisterModule

  if (store.registerModule) {
    origRegisterModule = store.registerModule.bind(store)
    store.registerModule = (path, module, options) => {
      addModule(path, module, options)
      origRegisterModule(path, module, options)
    }

    origUnregisterModule = store.unregisterModule.bind(store)
    store.unregisterModule = (path) => {
      if (typeof path === 'string') path = [path]

      delete registeredModules[path.join('/')]

      if (SharedData.recordVuex) {
        addMutation(`Unregister module: ${path.join('/')}`, {
          path
        }, {
          unregisterModule: true
        })
      }

      origUnregisterModule(path)
    }
  } else {
    origRegisterModule = origUnregisterModule = () => {}
  }

  bridge.send('vuex:init')

  earlyModules.forEach(({ path, module, options }) => {
    addModule(path, module, options)
  })

  // deal with multiple backend injections
  hook.off('vuex:mutation')

  // application -> devtool
  hook.on('vuex:mutation', ({ type, payload }) => {
    if (!SharedData.recordVuex) return

    addMutation(type, payload)
  })

  function addMutation (type, payload, options = {}) {
    const index = mutations.length

    mutations.push({
      type,
      payload,
      index,
      handlers: store._mutations[type],
      registeredModules: Object.keys(registeredModules),
      ...options
    })

    bridge.send('vuex:mutation', {
      mutation: {
        type: type,
        payload: stringify(payload),
        index
      },
      timestamp: Date.now()
    })
  }

  // devtool -> application
  bridge.on('vuex:travel-to-state', ({ index, apply }) => {
    const snapshot = replayMutations(index)
    const { state } = parse(snapshot, true)
    bridge.send('vuex:inspected-state', {
      index,
      snapshot
    })
    if (apply) {
      ensureRegisteredModules(mutations[index])
      hook.emit('vuex:travel-to-state', state)
    }
  })

  bridge.on('vuex:commit-all', () => {
    reset()
  })

  bridge.on('vuex:revert-all', () => {
    reset()
  })

  bridge.on('vuex:commit', index => {
    baseStateSnapshot = lastState
    resetSnapshotCache()
    mutations = mutations.slice(index + 1)
    mutations.forEach((mutation, index) => {
      mutation.index = index
    })
  })

  bridge.on('vuex:revert', index => {
    resetSnapshotCache()
    ensureRegisteredModules(mutations[index - 1])
    mutations = mutations.slice(0, index)
  })

  bridge.on('vuex:import-state', state => {
    const parsed = parse(state, true)
    hook.initialStore.state = parsed
    reset()
    hook.emit('vuex:travel-to-state', parsed)
    bridge.send('vuex:init')
    bridge.send('vuex:inspected-state', {
      index: -1,
      snapshot: getSnapshot(baseStateSnapshot)
    })
  })

  bridge.on('vuex:inspect-state', index => {
    if (index === -1) {
      bridge.send('vuex:inspected-state', {
        index,
        snapshot: getSnapshot(baseStateSnapshot)
      })
    } else {
      const snapshot = replayMutations(index)
      bridge.send('vuex:inspected-state', {
        index,
        snapshot
      })
    }
  })

  function replayMutations (index) {
    const originalVm = store._vm
    store._vm = snapshotsVm

    let tempRemovedModules = []
    let tempAddedModules = []

    // Get most recent snapshot for target index
    // for faster replay
    let stateSnapshot
    for (let i = 0; i < stateSnapshots.length; i++) {
      const s = stateSnapshots[i]
      if (s.index > index) {
        break
      } else {
        stateSnapshot = s
      }
    }

    let resultState

    // Snapshot was already replayed
    if (stateSnapshot.index === index) {
      resultState = stateSnapshot.state

      const state = parse(stateSnapshot.state, true)
      updateSnapshotsVm(state)
      store.replaceState(state)
    } else {
      const startMutation = mutations[stateSnapshot.index]
      if (startMutation) {
        tempRemovedModules = Object.keys(registeredModules).filter(m => !startMutation.registeredModules.includes(m))
      } else {
        tempRemovedModules = Object.keys(registeredModules)
      }
      tempRemovedModules.filter(m => get(store.state, m.split('/'))).sort((a, b) => b.length - a.length).forEach(m => {
        origUnregisterModule(m.split('/'))
      })

      const state = parse(stateSnapshot.state, true)
      updateSnapshotsVm(state)
      store.replaceState(state)

      SharedData.snapshotLoading = true

      // Replay mutations
      for (let i = stateSnapshot.index + 1; i <= index; i++) {
        const mutation = mutations[i]
        if (mutation.registerModule) {
          const key = mutation.payload.path.join('/')
          const moduleInfo = allTimeModules[key]
          tempAddedModules.push(key)
          origRegisterModule(moduleInfo.path, {
            ...moduleInfo.module,
            state: parse(moduleInfo.state, true)
          }, moduleInfo.options)
          updateSnapshotsVm(store.state)
        } else if (mutation.unregisterModule && get(store.state, mutation.payload.path) != null) {
          const path = mutation.payload.path
          const index = tempAddedModules.indexOf(path.join('/'))
          if (index !== -1) tempAddedModules.splice(index, 1)
          origUnregisterModule(path)
          updateSnapshotsVm(store.state)
        } else if (mutation.handlers) {
          store._committing = true
          if (Array.isArray(mutation.handlers)) {
            mutation.handlers.forEach(handler => handler(mutation.payload))
          } else {
            if (isLegacy) {
              // Vuex 1
              mutation.handlers(store.state, mutation.payload)
            } else {
              mutation.handlers(mutation.payload)
            }
          }
          store._committing = false
        }

        if (i !== index && i % SharedData.cacheVuexSnapshotsEvery === 0) {
          takeStateSnapshot(i)
        }
      }

      // Send final state after replay
      resultState = getStateSnapshot()
    }

    lastState = resultState

    const result = stringify({
      state: store.state,
      getters: store.getters || {}
    })

    // Restore user state
    tempAddedModules.sort((a, b) => b.length - a.length).forEach(m => {
      origUnregisterModule(m.split('/'))
    })
    tempRemovedModules.sort((a, b) => a.length - b.length).forEach(m => {
      const { path, module, options, state } = registeredModules[m]
      origRegisterModule(path, {
        ...module,
        state: parse(state, true)
      }, options)
    })
    store._vm = originalVm

    return result
  }

  bridge.on('vuex:edit-state', ({ index, value, path }) => {
    let parsedValue
    if (value) {
      parsedValue = parse(value, true)
    }
    store._committing = true
    set(store.state, path, parsedValue)
    store._committing = false
    bridge.send('vuex:inspected-state', {
      index,
      snapshot: getSnapshot()
    })
  })

  function takeStateSnapshot (index) {
    stateSnapshots.push({
      index,
      state: getStateSnapshot()
    })
    // Delete old cached snapshots
    if (stateSnapshots.length > SharedData.cacheVuexSnapshotsLimit) {
      stateSnapshots.splice(1, 1)
    }
  }

  function getSnapshot (stateSnapshot = null) {
    let originalVm
    if (stateSnapshot) {
      originalVm = store._vm
      store._vm = snapshotsVm
      store.replaceState(parse(stateSnapshot, true))
    }

    const result = stringify({
      state: store.state,
      getters: store.getters || {}
    })

    if (stateSnapshot) {
      // Restore user state
      store._vm = originalVm
    }

    return result
  }

  function ensureRegisteredModules (mutation) {
    if (mutation) {
      mutation.registeredModules.forEach(m => {
        if (!Object.keys(registeredModules).sort((a, b) => a.length - b.length).includes(m)) {
          const data = allTimeModules[m]
          if (data) {
            const { path, module, options, state } = data
            origRegisterModule(path, {
              ...module,
              state: parse(state, true)
            }, options)
            registeredModules[path.join('/')] = data
          }
        }
      })
      Object.keys(registeredModules).sort((a, b) => b.length - a.length).forEach(m => {
        if (!mutation.registeredModules.includes(m)) {
          origUnregisterModule(m.split('/'))
          delete registeredModules[m]
        }
      })
      updateSnapshotsVm(store.state)
    }
  }
}

export function getCustomStoreDetails (store) {
  return {
    _custom: {
      type: 'store',
      display: 'Store',
      value: {
        state: store.state,
        getters: store.getters
      },
      fields: {
        abstract: true
      }
    }
  }
}
<commit_msg>fix(vuex): duplicate getter error<commit_after>import { stringify, parse } from 'src/util'
import SharedData from 'src/shared-data'
import { set, get } from '../util'
import Vue from 'vue'

const isProd = process.env.NODE_ENV === 'production'

export function initVuexBackend (hook, bridge, isLegacy) {
  const store = hook.store

  let snapshotsVm = null
  const updateSnapshotsVm = (state) => {
    snapshotsVm = new Vue({
      data: {
        $$state: state || {}
      },
      computed: store._vm.$options.computed
    })
  }

  const getStateSnapshot = (_store = store) => stringify(_store.state)

  let baseStateSnapshot, stateSnapshots, mutations, lastState
  let registeredModules = {}
  let allTimeModules = {}

  const earlyModules = hook.flushStoreModules()

  // Init additional state
  earlyModules.forEach(({ path, module, options }) => {
    if (!options || options.preserveState !== true) {
      const state = typeof module.state === 'function' ? module.state() : module.state
      const parentState = path.length === 1 ? hook.initialStore.state : get(hook.initialStore.state, path.slice(0, -1))
      set(parentState, path[path.length - 1], state)
    }
  })

  updateSnapshotsVm()

  function reset () {
    baseStateSnapshot = getStateSnapshot(hook.initialStore)
    mutations = []
    resetSnapshotCache()
  }

  function resetSnapshotCache () {
    stateSnapshots = [
      { index: -1, state: baseStateSnapshot }
    ]
  }

  reset()

  function addModule (path, module, options) {
    if (typeof path === 'string') path = [path]

    let state
    if (options && options.preserveState) {
      state = get(store.state, path)
    }
    if (!state) {
      state = typeof module.state === 'function' ? module.state() : module.state
    }
    if (!state) {
      state = {}
    }

    const key = path.join('/')
    const moduleInfo = registeredModules[key] = allTimeModules[key] = {
      path,
      module,
      options: {
        ...options,
        preserveState: false
      },
      state: stringify(state)
    }

    if (SharedData.recordVuex) {
      addMutation(`Register module: ${key}`, {
        path,
        module,
        options
      }, {
        registerModule: true
      })
    }

    return moduleInfo
  }

  let origRegisterModule, origUnregisterModule

  if (store.registerModule) {
    origRegisterModule = store.registerModule.bind(store)
    store.registerModule = (path, module, options) => {
      addModule(path, module, options)
      origRegisterModule(path, module, options)
      if (!isProd) console.log('register module', path)
    }

    origUnregisterModule = store.unregisterModule.bind(store)
    store.unregisterModule = (path) => {
      if (typeof path === 'string') path = [path]

      delete registeredModules[path.join('/')]

      if (SharedData.recordVuex) {
        addMutation(`Unregister module: ${path.join('/')}`, {
          path
        }, {
          unregisterModule: true
        })
      }

      origUnregisterModule(path)
      if (!isProd) console.log('unregister module', path)
    }
  } else {
    origRegisterModule = origUnregisterModule = () => {}
  }

  bridge.send('vuex:init')

  earlyModules.forEach(({ path, module, options }) => {
    const moduleInfo = addModule(path, module, options)
    moduleInfo.early = true
  })

  // deal with multiple backend injections
  hook.off('vuex:mutation')

  // application -> devtool
  hook.on('vuex:mutation', ({ type, payload }) => {
    if (!SharedData.recordVuex) return

    addMutation(type, payload)
  })

  function addMutation (type, payload, options = {}) {
    const index = mutations.length

    mutations.push({
      type,
      payload,
      index,
      handlers: store._mutations[type],
      registeredModules: Object.keys(registeredModules),
      ...options
    })

    bridge.send('vuex:mutation', {
      mutation: {
        type: type,
        payload: stringify(payload),
        index
      },
      timestamp: Date.now()
    })
  }

  // devtool -> application
  bridge.on('vuex:travel-to-state', ({ index, apply }) => {
    const snapshot = replayMutations(index)
    const { state } = parse(snapshot, true)
    bridge.send('vuex:inspected-state', {
      index,
      snapshot
    })
    if (apply) {
      ensureRegisteredModules(mutations[index])
      hook.emit('vuex:travel-to-state', state)
    }
  })

  bridge.on('vuex:commit-all', () => {
    reset()
  })

  bridge.on('vuex:revert-all', () => {
    reset()
  })

  bridge.on('vuex:commit', index => {
    baseStateSnapshot = lastState
    resetSnapshotCache()
    mutations = mutations.slice(index + 1)
    mutations.forEach((mutation, index) => {
      mutation.index = index
    })
  })

  bridge.on('vuex:revert', index => {
    resetSnapshotCache()
    ensureRegisteredModules(mutations[index - 1])
    mutations = mutations.slice(0, index)
  })

  bridge.on('vuex:import-state', state => {
    const parsed = parse(state, true)
    hook.initialStore.state = parsed
    reset()
    hook.emit('vuex:travel-to-state', parsed)
    bridge.send('vuex:init')
    bridge.send('vuex:inspected-state', {
      index: -1,
      snapshot: getSnapshot(baseStateSnapshot)
    })
  })

  bridge.on('vuex:inspect-state', index => {
    if (index === -1) {
      bridge.send('vuex:inspected-state', {
        index,
        snapshot: getSnapshot(baseStateSnapshot)
      })
    } else {
      const snapshot = replayMutations(index)
      bridge.send('vuex:inspected-state', {
        index,
        snapshot
      })
    }
  })

  function replayMutations (index) {
    const originalVm = store._vm
    store._vm = snapshotsVm

    let tempRemovedModules = []
    let tempAddedModules = []

    // Get most recent snapshot for target index
    // for faster replay
    let stateSnapshot
    for (let i = 0; i < stateSnapshots.length; i++) {
      const s = stateSnapshots[i]
      if (s.index > index) {
        break
      } else {
        stateSnapshot = s
      }
    }

    let resultState

    // Snapshot was already replayed
    if (stateSnapshot.index === index) {
      resultState = stateSnapshot.state

      const state = parse(stateSnapshot.state, true)
      updateSnapshotsVm(state)
      store.replaceState(state)
    } else {
      const startMutation = mutations[stateSnapshot.index]
      if (startMutation) {
        tempRemovedModules = Object.keys(registeredModules).filter(m => !startMutation.registeredModules.includes(m))
      } else {
        tempRemovedModules = Object.keys(registeredModules)
      }
      tempRemovedModules = tempRemovedModules.filter(m => !registeredModules[m].early)
      tempRemovedModules.filter(m => get(store.state, m.split('/'))).sort((a, b) => b.length - a.length).forEach(m => {
        origUnregisterModule(m.split('/'))
        if (!isProd) console.log('before replay unregister', m)
      })

      const state = parse(stateSnapshot.state, true)
      updateSnapshotsVm(state)
      store.replaceState(state)

      SharedData.snapshotLoading = true

      // Replay mutations
      for (let i = stateSnapshot.index + 1; i <= index; i++) {
        const mutation = mutations[i]
        if (mutation.registerModule) {
          const key = mutation.payload.path.join('/')
          const moduleInfo = allTimeModules[key]
          if (!moduleInfo.early) {
            tempAddedModules.push(key)
            origRegisterModule(moduleInfo.path, {
              ...moduleInfo.module,
              state: parse(moduleInfo.state, true)
            }, moduleInfo.options)
            updateSnapshotsVm(store.state)
            if (!isProd) console.log('replay register module', moduleInfo)
          }
        } else if (mutation.unregisterModule && get(store.state, mutation.payload.path) != null) {
          const path = mutation.payload.path
          const index = tempAddedModules.indexOf(path.join('/'))
          if (index !== -1) tempAddedModules.splice(index, 1)
          origUnregisterModule(path)
          updateSnapshotsVm(store.state)
          if (!isProd) console.log('replay unregister module', path)
        } else if (mutation.handlers) {
          store._committing = true
          if (Array.isArray(mutation.handlers)) {
            mutation.handlers.forEach(handler => handler(mutation.payload))
          } else {
            if (isLegacy) {
              // Vuex 1
              mutation.handlers(store.state, mutation.payload)
            } else {
              mutation.handlers(mutation.payload)
            }
          }
          store._committing = false
        }

        if (i !== index && i % SharedData.cacheVuexSnapshotsEvery === 0) {
          takeStateSnapshot(i)
        }
      }

      // Send final state after replay
      resultState = getStateSnapshot()
    }

    lastState = resultState

    const result = stringify({
      state: store.state,
      getters: store.getters || {}
    })

    // Restore user state
    tempAddedModules.sort((a, b) => b.length - a.length).forEach(m => {
      origUnregisterModule(m.split('/'))
      if (!isProd) console.log('after replay unregister', m)
    })
    tempRemovedModules.sort((a, b) => a.length - b.length).forEach(m => {
      const { path, module, options, state } = registeredModules[m]
      origRegisterModule(path, {
        ...module,
        state: parse(state, true)
      }, options)
      if (!isProd) console.log('after replay register', m)
    })
    store._vm = originalVm

    return result
  }

  bridge.on('vuex:edit-state', ({ index, value, path }) => {
    let parsedValue
    if (value) {
      parsedValue = parse(value, true)
    }
    store._committing = true
    set(store.state, path, parsedValue)
    store._committing = false
    bridge.send('vuex:inspected-state', {
      index,
      snapshot: getSnapshot()
    })
  })

  function takeStateSnapshot (index) {
    stateSnapshots.push({
      index,
      state: getStateSnapshot()
    })
    // Delete old cached snapshots
    if (stateSnapshots.length > SharedData.cacheVuexSnapshotsLimit) {
      stateSnapshots.splice(1, 1)
    }
  }

  function getSnapshot (stateSnapshot = null) {
    let originalVm
    if (stateSnapshot) {
      originalVm = store._vm
      store._vm = snapshotsVm
      store.replaceState(parse(stateSnapshot, true))
    }

    const result = stringify({
      state: store.state,
      getters: store.getters || {}
    })

    if (stateSnapshot) {
      // Restore user state
      store._vm = originalVm
    }

    return result
  }

  function ensureRegisteredModules (mutation) {
    if (mutation) {
      mutation.registeredModules.forEach(m => {
        if (!Object.keys(registeredModules).sort((a, b) => a.length - b.length).includes(m)) {
          const data = allTimeModules[m]
          if (data) {
            const { path, module, options, state } = data
            origRegisterModule(path, {
              ...module,
              state: parse(state, true)
            }, options)
            registeredModules[path.join('/')] = data
          }
        }
      })
      Object.keys(registeredModules).sort((a, b) => b.length - a.length).forEach(m => {
        if (!mutation.registeredModules.includes(m)) {
          origUnregisterModule(m.split('/'))
          delete registeredModules[m]
        }
      })
      updateSnapshotsVm(store.state)
    }
  }
}

export function getCustomStoreDetails (store) {
  return {
    _custom: {
      type: 'store',
      display: 'Store',
      value: {
        state: store.state,
        getters: store.getters
      },
      fields: {
        abstract: true
      }
    }
  }
}
<|endoftext|>


<commit_before>/*
 * TODO:
 *  - GULP
 *  - make imagemin work
 *  - visuallyhidden -> vh? (+ remove unneccessary/unused classes)
 *  - humans.txt + rel attr
 *  - uncss, class names minification
 *  - GA (https://github.com/dciccale/grunt-processhtml, https://github.com/changer/grunt-targethtml) + ask for key
 *    - new task ("dploy"/"production"), because of build is sometimes being used for testing final product (don't wanna analyze this :))
 *  - modernizr custom build/CDN
 *  - do I need to include normalize, h5bp, foundation & compass resets?
 *  - support IE desktops https://github.com/jtangelder/grunt-stripmq, http://robin.medvedi.eu/mobile-first-and-ie8-solution-introducing-grunt-legacssy/
 *  - separate print styles and add media element for IE8-
 *  - print.scss, ie.scss (if supported)
 *  - git initialization
 *  - README - file system (to both READMEs)
 *  - open sublime when ready
 *  - check svgmin options
 *
 */

'use strict';
var util = require('util');
var path = require('path');
var yeoman = require('yeoman-generator');

var WebProjectGenerator = module.exports = function WebProjectGenerator(args, options, config) {
	yeoman.generators.Base.apply(this, arguments);

	this.pkg = JSON.parse(this.readFileAsString(path.join(__dirname, '../package.json')));
};

util.inherits(WebProjectGenerator, yeoman.generators.Base);

WebProjectGenerator.prototype.askFor = function askFor() {
	var cb = this.async();

	// welcome message
	console.log(this.yeoman);
	console.log('Foundation5 with Compass and HTML5 Boilerplate are prepared!');

	var prompts = [{
		type: 'confirm',
		name: 'localizeCZ',
		message: 'Would you like to use Czech?',
		default: false
	}, {
		type: 'confirm',
		name: 'createSublimeTextProjectFile',
		message: 'Create Sublime Text project file?',
		default: true
	}, {
		name: 'htmlTitle',
		message: 'Your HTML <title>?',
		default: this.appname
	}];

	this.prompt(prompts, function (answers) {
		// // manually deal with the response, get back and store the results.
		// // we change a bit this way of doing to automatically do this in the self.prompt() method.
		this.language = answers.localizeCZ ? 'cs' : 'en';
		this.createSublimeTextProjectFile = answers.createSublimeTextProjectFile;
		this.htmlTitle = answers.htmlTitle;

		cb();
	}.bind(this));
};

WebProjectGenerator.prototype.gruntfile = function gruntfile() {
	this.template('Gruntfile.js');
};

WebProjectGenerator.prototype.packageJSON = function packageJSON() {
	this.template('_package.json', 'package.json');
};

WebProjectGenerator.prototype.SassConfig = function SassConfig() {
	this.copy('config.rb', 'config.rb');
};

WebProjectGenerator.prototype.git = function git() {
	this.copy('gitignore', '.gitignore');
	this.copy('gitattributes', '.gitattributes');
};

WebProjectGenerator.prototype.bower = function bower() {
	this.copy('bowerrc', '.bowerrc');
	this.template('_bower.json', 'bower.json');
};

WebProjectGenerator.prototype.editor = function editor() {
	this.copy('editorconfig', '.editorconfig');
	if (this.createSublimeTextProjectFile)
		this.template('project.sublime-project', this._.slugify(this.appname) + '.sublime-project');
};

WebProjectGenerator.prototype.assets = function assets() {
	this.mkdir('assets');
	this.mkdir('assets/font');
	this.mkdir('assets/img');
	this.mkdir('assets/js');
	this.mkdir('assets/scss');

	this.mkdir('assets/scss/components');
	this.mkdir('assets/scss/helpers');
	this.mkdir('assets/scss/layout');
	this.mkdir('assets/scss/settings');
	this.mkdir('assets/scss/vendor');
};

WebProjectGenerator.prototype.files = function files() {
	this.template('_README.md', 'README.md');

	this.copy('404_' + this.language + '.html', '404.html');

	this.copy('favicon.ico', 'favicon.ico');
	this.copy('apple-touch-icon-precomposed.png', 'apple-touch-icon-precomposed.png');

	this.copy('htaccess', '.htaccess');
	this.copy('robots.txt', 'robots.txt');
	this.copy('crossdomain.xml', 'crossdomain.xml');

	this.copy('main.scss', 'assets/scss/main.scss');
	this.copy('ie8.scss', 'assets/scss/ie8.scss');
	this.copy('settings.scss', 'assets/scss/settings/_foundation.scss');
	this.copy('h5bp.scss', 'assets/scss/vendor/_h5bp.scss');
	this.copy('foundation.scss', 'assets/scss/vendor/_foundation.scss');
	this.copy('mixins.scss', 'assets/scss/helpers/_mixins.scss');

	this.copy('main.js', 'assets/js/main.js');
	this.copy('plugins.js', 'assets/js/plugins.js');
	this.copy('jquery-2.1.0.js', 'assets/js/jquery.js'); // for dev CDN fallback, for production compiled by grunt from bower_components
	this.copy('jquery-1.8.0.js', 'assets/js/jquery-1.8.0.js');
	this.copy('selectivizr-1.0.3b.js', 'assets/js/selectivizr-1.0.3b.js');
};

WebProjectGenerator.prototype.index = function index() {

	this.copy('index.html', 'index.html');
};

WebProjectGenerator.prototype.install = function () {
	var done = this.async();
	this.installDependencies({
		callback: done
	});
};<commit_msg>Remove default czech support<commit_after>/*
 * TODO:
 *  - Modernizr CDN
 *  - GULP
 *  - make imagemin work
 *  - visuallyhidden -> vh? (+ remove unneccessary/unused classes)
 *  - humans.txt + rel attr
 *  - uncss, class names minification
 *  - GA (https://github.com/dciccale/grunt-processhtml, https://github.com/changer/grunt-targethtml) + ask for key
 *    - new task ("dploy"/"production"), because of build is sometimes being used for testing final product (don't wanna analyze this :))
 *  - modernizr custom build/CDN
 *  - do I need to include normalize, h5bp, foundation & compass resets?
 *  - support IE desktops https://github.com/jtangelder/grunt-stripmq, http://robin.medvedi.eu/mobile-first-and-ie8-solution-introducing-grunt-legacssy/
 *  - separate print styles and add media element for IE8-
 *  - print.scss, ie.scss (if supported)
 *  - git initialization
 *  - README - file system (to both READMEs)
 *  - open sublime when ready
 *  - check svgmin options
 *
 */

'use strict';
var util = require('util');
var path = require('path');
var yeoman = require('yeoman-generator');
var file = yeoman.file;

var WebProjectGenerator = module.exports = function WebProjectGenerator(args, options, config) {
	yeoman.generators.Base.apply(this, arguments);

	this.pkg = JSON.parse(this.readFileAsString(path.join(__dirname, '../package.json')));
};

util.inherits(WebProjectGenerator, yeoman.generators.Base);

WebProjectGenerator.prototype.askFor = function askFor() {
	var cb = this.async();

	// welcome message
	console.log(this.yeoman);
	console.log('Foundation5 with Compass and HTML5 Boilerplate are prepared!');

	var prompts = [{
		name: 'language',
		message: 'Which language would you like to use? (ISO abbreviation)',
		default: 'en'
	}, {
		type: 'confirm',
		name: 'createSublimeTextProjectFile',
		message: 'Create Sublime Text project file?',
		default: true
	}, {
		name: 'htmlTitle',
		message: 'Your HTML <title>?',
		default: this.appname
	}];

	this.prompt(prompts, function (answers) {
		// // manually deal with the response, get back and store the results.
		// // we change a bit this way of doing to automatically do this in the self.prompt() method.
		this.language = answers.language;
		this.createSublimeTextProjectFile = answers.createSublimeTextProjectFile;
		this.htmlTitle = answers.htmlTitle;

		cb();
	}.bind(this));
};

WebProjectGenerator.prototype.gruntfile = function gruntfile() {
	this.template('Gruntfile.js');
};

WebProjectGenerator.prototype.packageJSON = function packageJSON() {
	this.template('_package.json', 'package.json');
};

WebProjectGenerator.prototype.SassConfig = function SassConfig() {
	this.copy('config.rb', 'config.rb');
};

WebProjectGenerator.prototype.git = function git() {
	this.copy('gitignore', '.gitignore');
	this.copy('gitattributes', '.gitattributes');
};

WebProjectGenerator.prototype.bower = function bower() {
	this.copy('bowerrc', '.bowerrc');
	this.template('_bower.json', 'bower.json');
};

WebProjectGenerator.prototype.editor = function editor() {
	this.copy('editorconfig', '.editorconfig');
	if (this.createSublimeTextProjectFile) {
		this.template('project.sublime-project', this._.slugify(this.appname) + '.sublime-project');
	}
};

WebProjectGenerator.prototype.assets = function assets() {
	this.mkdir('assets');
	this.mkdir('assets/font');
	this.mkdir('assets/img');
	this.mkdir('assets/js');
	this.mkdir('assets/scss');

	this.mkdir('assets/scss/components');
	this.mkdir('assets/scss/helpers');
	this.mkdir('assets/scss/layout');
	this.mkdir('assets/scss/settings');
	this.mkdir('assets/scss/vendor');
};

WebProjectGenerator.prototype.files = function files() {
	this.template('_README.md', 'README.md');

	if (file.exists(this.src._base + '/404_' + this.language + '.html')) {
		this.copy('404_' + this.language + '.html', '404.html');
	}
	else {
		console.log('There is not 404.html error page in your choosen language, I\'m including english version.');
		this.copy('404_en.html', '404.html');
	}

	this.copy('favicon.ico', 'favicon.ico');
	this.copy('apple-touch-icon-precomposed.png', 'apple-touch-icon-precomposed.png');

	this.copy('htaccess', '.htaccess');
	this.copy('robots.txt', 'robots.txt');
	this.copy('crossdomain.xml', 'crossdomain.xml');

	this.copy('main.scss', 'assets/scss/main.scss');
	this.copy('ie8.scss', 'assets/scss/ie8.scss');
	this.copy('settings.scss', 'assets/scss/settings/_foundation.scss');
	this.copy('h5bp.scss', 'assets/scss/vendor/_h5bp.scss');
	this.copy('foundation.scss', 'assets/scss/vendor/_foundation.scss');
	this.copy('mixins.scss', 'assets/scss/helpers/_mixins.scss');

	this.copy('main.js', 'assets/js/main.js');
	this.copy('plugins.js', 'assets/js/plugins.js');
	this.copy('jquery-2.1.0.js', 'assets/js/jquery.js'); // for dev CDN fallback, for production compiled by grunt from bower_components
	this.copy('jquery-1.8.0.js', 'assets/js/jquery-1.8.0.js');
	this.copy('selectivizr-1.0.3b.js', 'assets/js/selectivizr-1.0.3b.js');
};

WebProjectGenerator.prototype.index = function index() {

	this.copy('index.html', 'index.html');
};

WebProjectGenerator.prototype.install = function () {
	var done = this.async();
	this.installDependencies({
		callback: done
	});
};<|endoftext|>


<commit_before>/**
 * The module instance cache.
 *
 * @class Cache
 */

// require deps
var fs = require('fs-extra');

engine.modules = engine.pojo('modules');
engine.instances = engine.pojo('module_instances', true);

/**
 * Remove a module instance or empty cache.
 *
 * @public
 * @param {string} The module instance name.
 * @param {string} The role name.
 */
exports.remove = function (name, role) {

    // remove one item
    if (name) {

        // remove cache object
        return engine.instances.rm(name, role);
    }

    // remove all module instances from cache
    engine.rm();
};

/**
 * Get a module instance with role check
 *
 * @public
 * @param {string} The module instance name.
 * @param {string} The role name.
 */
exports.get = function (name, role) {

    if (name) {

        // return cache object
        return engine.instances.get(name, role);
    }
};

/**
 * Create a module instance.
 *
 * @public
 * @param {string} The module instance name.
 * @param {string} The role name.
 * @param {function} The callback function.
 */
exports.create = function (name, role, callback) {
    var module_instance = engine.instances.get(name, role);

    // check if object is already in cache
    if (module_instance) {
        return callback(null, module_instance);
    }

    // handle access denied
    if (module_instance === 0) {
        return callback(new Error('access denied.'));
    }

    // create path to composition file
    var path =  engine.paths.app_composition + name + '.json';

    // read the composition json
    fs.readJson(path, function (err, composition) {

        // handle error and check role access
        if (err || !engine.roleAccess(composition, role)) {
            err = err || new Error('access denied.');
            console.error('lib/comp.js#120', err.toString());
            return callback(err);
        }

        // watch file
        if (module_instance !== null) {
            fs.watch(path, function (event) {

                // remove cached data on file change, to force reload
                if (event === 'change') {

                    // remove the cached item
                    engine.instances.set(name, null, role);
                }
            });
        }

        // TOOD update css and html paths with fingerprints
        if (composition.client && composition.client.css) {
            // TODO update paths
            //engine.file.addFingerprints(composition.module, composition.client.css, function (err, paths) {});
        }

        // TODO update html paths if html is loaded with link import (webcomponents)
        //if (composition.client && composition.client.html) {
        //    engine.file.addFingerprints(composition.module, composition.client.html, function (err, paths) {});
        //}

        // get the module's package infos
        getPackackeInfo(composition.module, function (err, moduleConfig) {

            if (err) {
                console.error('lib/comp.js#141', err.toString());
                return callback(err);
            }

            // extend module instance config with the module dependencies
            composition.client = composition.client || {};

            if (moduleConfig) {

                // all dependencies of the instance dependencies must be in this array!
                // while installing a module, save all dependencies in the module deps
                // dependencies: ['./file.js', 's/o/modA/v']
                // => ModA deps: ['./file.js', 's/o/modB/v'] => ModB deps: []
                // => to client: ['./file.js', 's/o/n/v/file.js']
                if (moduleConfig.clientLoad) {
                    composition.client.scripts = moduleConfig.clientLoad;
                }

                // save main start script
                if (moduleConfig.main) {
                    composition.main = moduleConfig.main;
                }
            }

            // create a new module instance
            engine.module.factory(composition, function (err, module_instance) {

                // handle error
                if (err) {
                    console.error('lib/comp.js#170', err.toString());
                    return callback(err);
                }

                // save module instance in cache
                engine.instances.set(name, module_instance);

                // save the module instance always under the configured name
                if (composition.name !== name) {
                    engine.instances.set(composition.name, module_instance);
                }

                // return data
                callback(null, module_instance);
            });
        });
    });
};

/**
 * Parse and get the engine package infos.
 *
 * @private
 * @param {string} The module name or the custom module config object.
 * @param {function} The callback function.
 */
function getPackackeInfo (module, callback) {

    // handle custom modules
    // TODO cache custom modules
    if (typeof module === 'object') {

        if (!module.name) {
            var error = new Error('No module name.');
            console.error('lib/comp.js#176', error.toString());
            return callback(error);
        }

        // get module package info from cache
        var modulePackage = engine.modules.get(module.name);

        // return cached object
        if (modulePackage) {
            return callback(null, modulePackage);
        }

        // return custom module package
        if (!module.clientLoad) {

            // save package info in cache
            engine.modules.set(module.name, module);

            // return custom module
            return callback(null, module);
        }

        // add fingerprints to custom module dependencies
        engine.file.addFingerprints(module, module.clientLoad, function (err, scripts) {

            if (err) {
                console.error('lib/comp.js#213', err.toString());
                return callback(err);
            }

            // update the module dependencies with new script paths
            module.clientLoad = scripts;

            // save package info in cache
            engine.modules.set(module.name, module);

            // return the custom module package
            callback(null, module);
        });

        return;
    }

    // get module package info from cache
    var modulePackage = engine.modules.get(module);

    // return cached object
    if (modulePackage) {
        return callback(null, modulePackage);
    }

    // create module path
    var path = engine.paths.app_modules + module + '/package.json';

    // read and parse the packackage json file
    fs.readJson(path, function (err, packageInfo) {

        // handle errors
        if (err || !packageInfo) {
            err = err || new Error('No module package found.');
            console.error('lib/comp.js#241', err.toString());
            return callback(err);
        }

        // update file on change
        // TODO remove all module instance, when module changes
        if (modulePackage !== null) {
            fs.watch(path, function (event) {

                // remove cached data on file change, to force reload
                if (event === 'change') {

                    // set the cached module to null
                    engine.modules.set(module, null);
                }
            });
        }

        // return the engine spezific package info
        if (!packageInfo.clientLoad) {

            // save package info in cache
            engine.modules.set(module, packageInfo);

            return callback(null, packageInfo);
        }

        // add fingerprints to client side dependencies
        engine.file.addFingerprints(module, packageInfo.clientLoad, function (err, scripts) {

            if (err) {
                console.error('lib/comp.js#271', err.toString());
                return callback(err);
            }

            // update the module dependencies with new script paths
            packageInfo.clientLoad = scripts;

            // save package info in cache
            engine.modules.set(module, packageInfo);

            // return the module package info
            callback(null, packageInfo);
        });
    });
}
<commit_msg>add fingerprints to client resources<commit_after>/**
 * The module instance cache.
 *
 * @class Cache
 */

// require deps
var fs = require('fs-extra');

engine.modules = engine.pojo('modules');
engine.instances = engine.pojo('module_instances', true);

/**
 * Create a module instance.
 *
 * @public
 * @param {string} The module instance name.
 * @param {string} The role name.
 * @param {function} The callback function.
 */
module.exports = function (name, role, callback) {
    var module_instance = engine.instances.get(name, role);

    // check if object is already in cache
    if (module_instance) {
        return callback(null, module_instance);
    }

    // handle access denied
    if (module_instance === 0) {
        return callback(new Error('access denied.'));
    }

    // create path to composition file
    var path =  engine.paths.app_composition + name + '.json';

    // read the composition json
    fs.readJson(path, function (err, composition) {

        // handle error and check role access
        if (err || !engine.roleAccess(composition, role)) {
            err = err || new Error('access denied.');
            console.error('lib/comp.js#120', err.toString());
            return callback(err);
        }

        // watch file
        if (module_instance !== null) {
            fs.watch(path, function (event) {

                // remove cached data on file change, to force reload
                if (event === 'change') {

                    // remove the cached item
                    engine.instances.set(name, null, role);
                }
            });
        }

        var count = 0;

        // update client resources paths
        if (composition.client) {

            // update css paths with fingerprints
            if (composition.client.css) {

                ++count;

                // update paths
                engine.file.addFingerprints(engine.paths.app_public, composition.client.css, function (err, paths) {

                    if (err) {
                        console.error('lib/comp.js#72', err.toString());
                        return callback(err);
                    }

                    // composition.client.css = paths;

                    if (--count === 0) {

                        // create component
                        factoryModuleInstance(name, composition, module_instance, callback);
                    }
                });
            }

            // TODO update html paths if html is loaded with link import (webcomponents)
            /*if (composition.client.html) {

                ++count;

                engine.file.addFingerprints(engine.paths.app_markup, composition.client.html, function (err, paths) {

                    if (err) {
                        console.error('lib/comp.js#96', err.toString());
                        return callback(err);
                    }

                    // composition.client.html = paths;

                    if (--count === 0) {

                        // create component
                        factoryModuleInstance(name, composition, module_instance, callback);
                    }
                }, {noCompression: true});
            }*/
        }

        if (count === 0) {
            factoryModuleInstance(name, composition, module_instance, callback);
        }
    });
};

//get the module's package infos
function factoryModuleInstance (name, composition, module_instance, callback) {

    getPackackeInfo(composition.module, function (err, moduleConfig) {

        if (err) {
            console.error('lib/comp.js#141', err.toString());
            return callback(err);
        }

        // extend module instance config with the module dependencies
        composition.client = composition.client || {};

        if (moduleConfig) {

            // all dependencies of the instance dependencies must be in this array!
            // while installing a module, save all dependencies in the module deps
            // dependencies: ['./file.js', 's/o/modA/v']
            // => ModA deps: ['./file.js', 's/o/modB/v'] => ModB deps: []
            // => to client: ['./file.js', 's/o/n/v/file.js']
            if (moduleConfig.clientLoad) {
                composition.client.scripts = moduleConfig.clientLoad;
            }

            // save main start script
            if (moduleConfig.main) {
                composition.main = moduleConfig.main;
            }
        }

        // create a new module instance
        engine.module.factory(composition, function (err, module_instance) {

            // handle error
            if (err) {
                console.error('lib/comp.js#170', err.toString());
                return callback(err);
            }

            // save module instance in cache
            engine.instances.set(name, module_instance);

            // save the module instance always under the configured name
            if (composition.name !== name) {
                engine.instances.set(composition.name, module_instance);
            }

            // return data
            callback(null, module_instance);
        });
    });
}

/**
 * Parse and get the engine package infos.
 *
 * @private
 * @param {string} The module name or the custom module config object.
 * @param {function} The callback function.
 */
function getPackackeInfo (module, callback) {

    // handle custom modules
    if (typeof module === 'object') {

        if (!module.name) {
            var error = new Error('No module name.');
            console.error('lib/comp.js#176', error.toString());
            return callback(error);
        }

        // get module package info from cache
        var modulePackage = engine.modules.get(module.name);

        // return cached object
        if (modulePackage) {
            return callback(null, modulePackage);
        }

        return handleModule(module.name, module, callback);
    }

    // get module package info from cache
    var modulePackage = engine.modules.get(module);

    // return cached object
    if (modulePackage) {
        return callback(null, modulePackage);
    }

    // create module path
    var path = engine.paths.app_modules + module + '/package.json';

    // read and parse the packackage json file
    fs.readJson(path, function (err, packageInfo) {

        // handle errors
        if (err || !packageInfo) {
            err = err || new Error('No module package found.');
            console.error('lib/comp.js#241', err.toString());
            return callback(err);
        }

        // update file on change
        // TODO remove all module instance, when module changes
        if (modulePackage !== null) {
            fs.watch(path, function (event) {

                // remove cached data on file change, to force reload
                if (event === 'change') {

                    // set the cached module to null
                    engine.modules.set(module, null);
                }
            });
        }

        handleModule(module, packageInfo, callback);
    });
}

/**
 * Add fingerprints to client load scripts.
 *
 * @private
 * @param {string} The module name or the custom module config object.
 * @param {object} The package info.
 * @param {function} The callback function.
 */
function handleModule (module, packageInfo, callback) {

    // return custom module package
    if (!packageInfo.clientLoad) {

        // save package info in cache
        engine.modules.set(module, packageInfo);

        // return custom module
        return callback(null, module);
    }

    // add fingerprints to custom module dependencies
    engine.file.addFingerprints(engine.paths.app_modules + module + '/', packageInfo.clientLoad, function (err, scripts) {

        if (err) {
            console.error('lib/comp.js#213', err.toString());
            return callback(err);
        }

        // update the module dependencies with new script paths
        packageInfo.clientLoad = scripts;

        // save package info in cache
        engine.modules.set(module, packageInfo);

        // return the custom module package
        callback(null, packageInfo);
    }, {
        prependPath: module
    });
}
<|endoftext|>


<commit_before>'use strict';
var util = require('util');
var path = require('path');
var yeoman = require('yeoman-generator');
var yosay = require('yosay');
var chalk = require('chalk');

var ZurbInkGenerator = yeoman.generators.Base.extend({
  init: function () {
    this.pkg = require('../package.json');

    this.on('end', function () {
      if (!this.options['skip-install']) {
        this.installDependencies();
      }
    });
  },

  askFor: function () {
    var done = this.async();

    // Have Yeoman greet the user.
    this.log(yosay('Welcome to the marvelous Zurb Ink generator!'));

    var prompts = [{
      type: 'list',
      name: 'inkTemplate',
      message: 'Which ink template would you like to start with?',
      choices: [
        'Boilerplate',
        'Basic',
        'Hero',
        'Sidebar',
        'Sidebar Hero',
      ],
    }];

    this.prompt(prompts, function (props) {
      this.inkTemplate = props.inkTemplate.toLowerCase().replace(' ', '-');

      done();
    }.bind(this));
  },

  gulpfile: function() {
    this.copy('gulpfile.js');
  },

  packageJSON: function() {
    this.template('_package.json', 'package.json');
  },

  git: function() {
    this.copy('gitignore', '.gitignore');
  },

  bower: function() {
    this.template('_bower.json', 'bower.json');
  },

  editorConfig: function() {
    this.copy('editorconfig', '.editorconfig');
  },

  app: function () {
    this.mkdir('styles');
    this.copy('ink/' + this.inkTemplate + '/index.html', 'index.html');
    this.copy('ink/' + this.inkTemplate + '/styles.css', 'styles/styles.css');
  },
});

module.exports = ZurbInkGenerator;
<commit_msg>Create empty images directory<commit_after>'use strict';
var util = require('util');
var path = require('path');
var yeoman = require('yeoman-generator');
var yosay = require('yosay');
var chalk = require('chalk');

var ZurbInkGenerator = yeoman.generators.Base.extend({
  init: function () {
    this.pkg = require('../package.json');

    this.on('end', function () {
      if (!this.options['skip-install']) {
        this.installDependencies();
      }
    });
  },

  askFor: function () {
    var done = this.async();

    // Have Yeoman greet the user.
    this.log(yosay('Welcome to the marvelous Zurb Ink generator!'));

    var prompts = [{
      type: 'list',
      name: 'inkTemplate',
      message: 'Which ink template would you like to start with?',
      choices: [
        'Boilerplate',
        'Basic',
        'Hero',
        'Sidebar',
        'Sidebar Hero',
      ],
    }];

    this.prompt(prompts, function (props) {
      this.inkTemplate = props.inkTemplate.toLowerCase().replace(' ', '-');

      done();
    }.bind(this));
  },

  gulpfile: function() {
    this.copy('gulpfile.js');
  },

  packageJSON: function() {
    this.template('_package.json', 'package.json');
  },

  git: function() {
    this.copy('gitignore', '.gitignore');
  },

  bower: function() {
    this.template('_bower.json', 'bower.json');
  },

  editorConfig: function() {
    this.copy('editorconfig', '.editorconfig');
  },

  app: function () {
    this.mkdir('styles');
    this.mkdir('images');
    this.copy('ink/' + this.inkTemplate + '/index.html', 'index.html');
    this.copy('ink/' + this.inkTemplate + '/styles.css', 'styles/styles.css');
  },
});

module.exports = ZurbInkGenerator;
<|endoftext|>


<commit_before>/**
 * Sitespeed.io - How speedy is your site? (http://www.sitespeed.io)
 * Copyright (c) 2014, Peter Hedenskog, Tobias Lidskog
 * and other contributors
 * Released under the Apache 2.0 License
 */
var dateFormat = require('dateformat'),
urlParser = require('url'),
fs = require('fs-extra'),
path = require('path'),
supportedBrowsers = ['chrome','ie','firefox'],
config = require("nomnom").options({
  url: {
    abbr: 'u',
    metavar: '<URL>',
    help: 'The start url'
  },
  file: {
    abbr: 'f',
    metavar: '<FILE>',
    help: 'The path to a plain text file with one URL on each row. Each line must end with a new line in the file.',
    callback: function(file) {
      if (!fs.existsSync(file))
        return "Couldn't find the file:" + file;
    }
  },
  sites: {
    metavar: '<FILE>',
    help: 'The path to a plain text file with one URL on each row. Each line must end with a new line in the file.',
    callback: function(file) {
      if (!fs.existsSync(file))
        return "Couldn't find the file:" + file;
    }
  },
  version: {
    flag: true,
    abbr: 'v',
    help: 'Display the sitespeed.io version',
    callback: function() {
      return require("../package.json").version;
    }
  },
  deep: {
    abbr: 'd',
    metavar: '<INTEGER>',
    default: 1,
    help: 'How deep to crawl.',
    callback: function(deep) {
      if (deep != parseInt(deep))
        return "You must specify an integer";
    }
  },
  containInPath: {
    abbr: 'c',
    metavar: '<KEYWORD>',
    help: 'Only crawl URLs that contains this in the path'
  },
  skip: {
    abbr: 's',
    metavar: '<KEYWORD>',
    help: 'Do not crawl pages that contains this in the path'
  },
  threads: {
    abbr: 't',
    metavar: '<NOOFTHREADS>',
    default: 5,
    help: 'The number of  that will analyze pages.',
    callback: function(threads) {
      if (threads != parseInt(threads))
        return "You must specify an integer";
      else if (parseInt(threads) <= 0)
        return "You must specify a positive integer";
    }
  },
  name: {
    metavar: '<NAME>',
    help: 'Give your test a name, it will be added to all HTML pages'
  },
  memory: {
    metavar: '<INTEGER>',
    default: 1024,
    help: 'We still use Java for a couple of things and you can configure how much memory that the process will have (in mb).'
  },
  resultBaseDir: {
    abbr: 'r',
    metavar: '<DIR>',
    default: 'sitespeed-result',
    help: 'The result base directory',
    callback: function(file) {
      if (!fs.existsSync(file))
        return "Couldn't find the basedir:" + file;
    }
  },
  userAgent: {
    metavar: '<USER-AGENT>',
    default: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.71 Safari/537.36",
    help: 'The full User Agent string, default is Chrome for MacOSX. You can also set the value as iphone or ipad (will automagically change the viewport)'
  },
  viewPort: {
    metavar: '<WidthxHeight>',
    default: '1280x800',
    help: 'The view port, the page viewport size WidthxHeight, like 400x300.'
  },
  yslow: {
    abbr: 'y',
    metavar: '<FILE>',
    default: 'yslow-3.1.8-sitespeed.js',
    help: 'The compiled YSlow file.',
    callback: function(file) {
      if (!fs.existsSync(file))
        return "Couldn't find the file:" + fs.realpathSync(file);
    }
  },
  ruleSet: {
    metavar: '<RULE-SET>',
    default: 'sitespeed.io-desktop',
    help: 'Which ruleset to use.'
  },
  limitFile: {
    metavar: '<PATH>',
    default: '../conf/desktop-rules.json',
    help: 'The path to the limit configuration file'
  },
  basicAuth: {
    metavar: '<USERNAME:PASSWORD>',
    help: 'Basic auth user & password'
  },
  browser: {
    abbr: 'b',
    metavar: '<BROWSER>',
    help: 'Choose which browser to use to collect timing data. You can set multiple browsers in a comma separated list (firefox|chrome|ie)',
    callback: function(browsers) {
      b = browsers.split(","),
      invalidBrowsers = b.filter(function(browser) {
        return supportedBrowsers.indexOf(browser) < 0;
      });

      if (invalidBrowsers.length > 0)
        return "You specified a browser that is not supported:" + invalidBrowsers;
    }
  },
  profile: {
    metavar: '<desktop|mobile>',
    choices: ['desktop', 'mobile'],
    default: 'desktop',
    help: 'Choose between testing for desktop or mobile. Testing for desktop will use desktop rules & user agents, and vice verca'
  },
  no: {
    abbr: 'n',
    metavar: '<NUMBEROFTIMES>',
    default: 3,
    help: 'The number of times you should test each URL when fetching timing metrics. Default is three times',
    callback: function(n) {
      if (n != parseInt(n))
        return "You must specify an integer";
      else if (parseInt(n) <= 0)
        return "You must specify a positive integer";
    }
  },
  screenshot: {
    flag: true,
    help: 'Take screenshots for each page (using the configured view port).'
  },
  junit: {
    flag: true,
    help: 'Create JUnit output'
  },
  skipTest: {
    metavar: '<ruleid1,ruleid2,...>',
    help: 'A comma separeted list of rules to skip when generating JUnit'
  },
  threshold: {
    default: 90,
    metavar: '[0-100]',
    help: "Threshold score for tests, will be used of no mathing thresholdFile with values match. Use : --threshold 95"
  },
  thresholdFile: {
    metavar: '<FILE>',
    help: "A file containing JSON like  {\"overall\": 90, \"thirdpartyversions\": 85}"
  },
  timingsThresholdFile: {
    metavar: '<FILE>',
    help: "A file containing JSON like ..."
  },
  csv: {
    flag: true,
    help: 'Also output CSV where applicable'
  },
  maxPagesToTest: {
    abbr: 'm',
    metavar: '<NUMBEROFPAGES>',
    help: 'The max number of pages to test. Default is no limit'
  },
  proxy: {
    abbr: 'p',
    metavar: '<PROXY>',
    help: 'http://proxy.soulgalore.com:80'
  },
  cdns: {
    metavar: '<cdn1,cdn2>',
    list: true,
    help: 'A comma separated list of additional CDNs'
  },
  boxes: {
    metavar: '<box1,box2>',
    list: true,
    help: 'The boxes showed on site summary page, see http://www.sitespeed.io/documentation/#config-boxes for more info'
  },
  columns: {
    abbr: 'c',
    metavar: '<column1,column2>',
    list: true,
    help: 'The columns showed on detailed page summary table, see http://www.sitespeed.io/documentation/#config-columns for more info'
  },
  configFile: {
    metavar: '<PATH>',
    help: 'The path to a sitespeed.io config.json file, if it exists all other input parameters will be overidden'
  },
  // TODO How to override existing
  aggregators: {
    metavar: '<PATH>',
    help: 'The path to a directory with extra aggregators, see YYY'
  },
  // TODO maybe collectors are overkill
  collectors: {
    metavar: '<PATH>',
    help: 'The path to a directory with extra collectors, see YYY'
  },
  graphiteHost: {
    metavar: '<HOST>',
    help: 'The Graphite host'
  },
  graphitePort: {
    metavar: '<INTEGER>',
    default: 2003,
    help: 'Graphite port'
  },
  graphiteNamespace: {
    metavar: '<NAMESPACE>',
    default: 'sitespeed.io',
    help: 'The namespace of the data sent to Graphite'
  },
  graphiteData: {
    default: 'all',
    help: 'Choose which data to send to Graphite by a comma separated list. Default all data is sent. [summary,rules,pagemetrics,timings]'
  },
  googleKey: {
    help: 'Your Google Key, configure it to also fetch data from Google Page Speed Insights'
  },
  noYslow: {
    flag: true,
    help: 'Set to true to turn of collecting metrics using YSlow.',
  },
  webpagetestLocation: {
    metavar: '<STRING>',
    help: 'WebPageTest location, something like Dulles:Chrome ',
  },
  webpagetestUrl: {
    metavar: '<URL>',
    help: 'The URL to your private webpagetest instance'
  },
  webpagetestKey: {
    metavar: '<KEY>',
    help: 'The API key if running on the public instances'
  }
}).parse();

if ((!config.url) && (!config.file) && (!config.sites)) {
  console.log('You must specify either a URL to test or a file with URL:s');
  process.exit(1);
}

// If we supply a configuration file, then it will ovveride all current conf, meaning
// we can easily use the same conf over and over again
if (config.configFile) {
  config = JSON.parse(fs.readFileSync(config.configFile));
}

config.runYslow = config.noYslow?false:true;

// The run always has a fresh date
config.run = {};
config.run.date = new Date();

// Setup the absolute result dir
if (config.url) {
  config.urlObject = urlParser.parse(config.url);
  config.run.absResultDir = path.join(__dirname, '../',config.resultBaseDir, config.urlObject.hostname, dateFormat(config.run.date, "yyyy-mm-dd-HH-MM-ss") );
} else if (config.file) {
  // TODO handle the file name in a good way if it contains chars that will not fit in a dir
  config.run.absResultDir = path.join(__dirname, '../',config.resultBaseDir, config.file, dateFormat(config.run.date, "yyyy-mm-dd-HH-MM-ss") );
} else if (config.sites) {
  // The log file will end up here
  config.run.absResultDir = path.join(__dirname, '../',config.resultBaseDir, 'sites', dateFormat(config.run.date, "yyyy-mm-dd-HH-MM-ss"));
}
fs.mkdirsSync(config.run.absResultDir);

// Parse the proxy info as a real URL
if (config.proxy) {
  config.urlProxyObject = urlParser.parse(config.proxy);
}

if (config.thresholdFile)
  config.thresholds = require(config.thresholdFile);

if (config.timingsThresholdFile)
  config.timingThresholds = require(timingsThresholdFile);
else
  config.timingThresholds = require('../conf/junit-timings.json');

// decide which rules to use ...
if (config.profile === 'mobile') {
  config.rules= require('../conf/mobile-rules.json');
  config.ruleSet = "sitespeed.io-mobile";
  config.userAgent = "Mozilla/5.0 (iPad; CPU OS 6_0 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/6.0 Mobile/10A5376e Safari/8536.25";
  config.viewPort = "320x444";
}
else {
  config.rules= require(config.limitFile);
  // The rest use the default/configured one, will that work? :)
}

config.summaryBoxes = ['ruleScore'];

config.sitesColumns = ['requests','criticalPathScore','cssWeightPerPage','serverResponseTime'];

if (config.googleKey)
  config.summaryBoxes.push('gpsi.score');

config.summaryBoxes.push('criticalPathScore', 'jsSyncInHead',
  'jsPerPage', 'cssPerPage', 'cssImagesPerPage', 'fontsPerPage',
  'imagesPerPage', 'requests', 'requestsWithoutExpires', 'requestsWithoutGzip',
  'docWeight', 'jsWeightPerPage', 'cssWeightPerPage', 'imageWeightPerPage',
  'pageWeight', 'browserScaledImages', 'spofPerPage', 'numberOfDomains',
  'singleDomainRequests', 'redirectsPerPage', 'cacheTime', 'timeSinceLastMod');

if (config.webpagetestUrl)
  config.summaryBoxes.push('wpt.imageSavings','wpt.speedIndex','wpt.visualComplete');

if (config.browser) {
  config.summaryBoxes.push('serverResponseTime','backEndTime','frontEndTime','domContentLoadedTime','pageLoadTime','firstPaintTime');
}

if (config.boxes) {
  if (config.boxes[0].indexOf('+')===0) {
      config.boxes[0].split(',').forEach(function (box) {
      if (box.indexOf('+')===0)
        config.summaryBoxes.push(box.substring(1));
      else
        config.summaryBoxes.push(box);
      });
  }
  else
    config.summaryBoxes = config.boxes[0].split(',');
}

if (config.runYslow)
  config.pageColumns = ['yslow.assets.js','yslow.assets.css','yslow.assets.img','yslow.requests','rules.expiresmod.items','yslow.pageSize', 'rules.avoidscalingimages.items','rules.criticalpath'];

if (config.browser)
  config.pageColumns.push('timings.serverResponseTime.median','timings.domContentLoadedTime.median');

if (config.googleKey)
  config.pageColumns.push('gpsi.gscore');

if (config.webpagetestUrl)
  config.pageColumns.push('wpt.speedIndex');

if (config.columns) {
  config.pageColumns = config.columns[0].split(',');
}

config.dataDir = 'data';

config.supportedBrowsers = supportedBrowsers;

module.exports = config;
<commit_msg>added wpt/gpsi and browsertime data when configured as default #428<commit_after>/**
 * Sitespeed.io - How speedy is your site? (http://www.sitespeed.io)
 * Copyright (c) 2014, Peter Hedenskog, Tobias Lidskog
 * and other contributors
 * Released under the Apache 2.0 License
 */
var dateFormat = require('dateformat'),
urlParser = require('url'),
fs = require('fs-extra'),
path = require('path'),
supportedBrowsers = ['chrome','ie','firefox'],
config = require("nomnom").options({
  url: {
    abbr: 'u',
    metavar: '<URL>',
    help: 'The start url'
  },
  file: {
    abbr: 'f',
    metavar: '<FILE>',
    help: 'The path to a plain text file with one URL on each row. Each line must end with a new line in the file.',
    callback: function(file) {
      if (!fs.existsSync(file))
        return "Couldn't find the file:" + file;
    }
  },
  sites: {
    metavar: '<FILE>',
    help: 'The path to a plain text file with one URL on each row. Each line must end with a new line in the file.',
    callback: function(file) {
      if (!fs.existsSync(file))
        return "Couldn't find the file:" + file;
    }
  },
  version: {
    flag: true,
    abbr: 'v',
    help: 'Display the sitespeed.io version',
    callback: function() {
      return require("../package.json").version;
    }
  },
  deep: {
    abbr: 'd',
    metavar: '<INTEGER>',
    default: 1,
    help: 'How deep to crawl.',
    callback: function(deep) {
      if (deep != parseInt(deep))
        return "You must specify an integer";
    }
  },
  containInPath: {
    abbr: 'c',
    metavar: '<KEYWORD>',
    help: 'Only crawl URLs that contains this in the path'
  },
  skip: {
    abbr: 's',
    metavar: '<KEYWORD>',
    help: 'Do not crawl pages that contains this in the path'
  },
  threads: {
    abbr: 't',
    metavar: '<NOOFTHREADS>',
    default: 5,
    help: 'The number of  that will analyze pages.',
    callback: function(threads) {
      if (threads != parseInt(threads))
        return "You must specify an integer";
      else if (parseInt(threads) <= 0)
        return "You must specify a positive integer";
    }
  },
  name: {
    metavar: '<NAME>',
    help: 'Give your test a name, it will be added to all HTML pages'
  },
  memory: {
    metavar: '<INTEGER>',
    default: 1024,
    help: 'We still use Java for a couple of things and you can configure how much memory that the process will have (in mb).'
  },
  resultBaseDir: {
    abbr: 'r',
    metavar: '<DIR>',
    default: 'sitespeed-result',
    help: 'The result base directory',
    callback: function(file) {
      if (!fs.existsSync(file))
        return "Couldn't find the basedir:" + file;
    }
  },
  userAgent: {
    metavar: '<USER-AGENT>',
    default: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.71 Safari/537.36",
    help: 'The full User Agent string, default is Chrome for MacOSX. You can also set the value as iphone or ipad (will automagically change the viewport)'
  },
  viewPort: {
    metavar: '<WidthxHeight>',
    default: '1280x800',
    help: 'The view port, the page viewport size WidthxHeight, like 400x300.'
  },
  yslow: {
    abbr: 'y',
    metavar: '<FILE>',
    default: 'yslow-3.1.8-sitespeed.js',
    help: 'The compiled YSlow file.',
    callback: function(file) {
      if (!fs.existsSync(file))
        return "Couldn't find the file:" + fs.realpathSync(file);
    }
  },
  ruleSet: {
    metavar: '<RULE-SET>',
    default: 'sitespeed.io-desktop',
    help: 'Which ruleset to use.'
  },
  limitFile: {
    metavar: '<PATH>',
    default: '../conf/desktop-rules.json',
    help: 'The path to the limit configuration file'
  },
  basicAuth: {
    metavar: '<USERNAME:PASSWORD>',
    help: 'Basic auth user & password'
  },
  browser: {
    abbr: 'b',
    metavar: '<BROWSER>',
    help: 'Choose which browser to use to collect timing data. You can set multiple browsers in a comma separated list (firefox|chrome|ie)',
    callback: function(browsers) {
      b = browsers.split(","),
      invalidBrowsers = b.filter(function(browser) {
        return supportedBrowsers.indexOf(browser) < 0;
      });

      if (invalidBrowsers.length > 0)
        return "You specified a browser that is not supported:" + invalidBrowsers;
    }
  },
  profile: {
    metavar: '<desktop|mobile>',
    choices: ['desktop', 'mobile'],
    default: 'desktop',
    help: 'Choose between testing for desktop or mobile. Testing for desktop will use desktop rules & user agents, and vice verca'
  },
  no: {
    abbr: 'n',
    metavar: '<NUMBEROFTIMES>',
    default: 3,
    help: 'The number of times you should test each URL when fetching timing metrics. Default is three times',
    callback: function(n) {
      if (n != parseInt(n))
        return "You must specify an integer";
      else if (parseInt(n) <= 0)
        return "You must specify a positive integer";
    }
  },
  screenshot: {
    flag: true,
    help: 'Take screenshots for each page (using the configured view port).'
  },
  junit: {
    flag: true,
    help: 'Create JUnit output'
  },
  skipTest: {
    metavar: '<ruleid1,ruleid2,...>',
    help: 'A comma separeted list of rules to skip when generating JUnit'
  },
  threshold: {
    default: 90,
    metavar: '[0-100]',
    help: "Threshold score for tests, will be used of no mathing thresholdFile with values match. Use : --threshold 95"
  },
  thresholdFile: {
    metavar: '<FILE>',
    help: "A file containing JSON like  {\"overall\": 90, \"thirdpartyversions\": 85}"
  },
  timingsThresholdFile: {
    metavar: '<FILE>',
    help: "A file containing JSON like ..."
  },
  csv: {
    flag: true,
    help: 'Also output CSV where applicable'
  },
  maxPagesToTest: {
    abbr: 'm',
    metavar: '<NUMBEROFPAGES>',
    help: 'The max number of pages to test. Default is no limit'
  },
  proxy: {
    abbr: 'p',
    metavar: '<PROXY>',
    help: 'http://proxy.soulgalore.com:80'
  },
  cdns: {
    metavar: '<cdn1,cdn2>',
    list: true,
    help: 'A comma separated list of additional CDNs'
  },
  boxes: {
    metavar: '<box1,box2>',
    list: true,
    help: 'The boxes showed on site summary page, see http://www.sitespeed.io/documentation/#config-boxes for more info'
  },
  columns: {
    abbr: 'c',
    metavar: '<column1,column2>',
    list: true,
    help: 'The columns showed on detailed page summary table, see http://www.sitespeed.io/documentation/#config-columns for more info'
  },
  configFile: {
    metavar: '<PATH>',
    help: 'The path to a sitespeed.io config.json file, if it exists all other input parameters will be overidden'
  },
  // TODO How to override existing
  aggregators: {
    metavar: '<PATH>',
    help: 'The path to a directory with extra aggregators, see YYY'
  },
  // TODO maybe collectors are overkill
  collectors: {
    metavar: '<PATH>',
    help: 'The path to a directory with extra collectors, see YYY'
  },
  graphiteHost: {
    metavar: '<HOST>',
    help: 'The Graphite host'
  },
  graphitePort: {
    metavar: '<INTEGER>',
    default: 2003,
    help: 'Graphite port'
  },
  graphiteNamespace: {
    metavar: '<NAMESPACE>',
    default: 'sitespeed.io',
    help: 'The namespace of the data sent to Graphite'
  },
  graphiteData: {
    default: 'all',
    help: 'Choose which data to send to Graphite by a comma separated list. Default all data is sent. [summary,rules,pagemetrics,timings]'
  },
  googleKey: {
    help: 'Your Google Key, configure it to also fetch data from Google Page Speed Insights'
  },
  noYslow: {
    flag: true,
    help: 'Set to true to turn of collecting metrics using YSlow.',
  },
  webpagetestLocation: {
    metavar: '<STRING>',
    help: 'WebPageTest location, something like Dulles:Chrome ',
  },
  webpagetestUrl: {
    metavar: '<URL>',
    help: 'The URL to your private webpagetest instance'
  },
  webpagetestKey: {
    metavar: '<KEY>',
    help: 'The API key if running on the public instances'
  }
}).parse();

if ((!config.url) && (!config.file) && (!config.sites)) {
  console.log('You must specify either a URL to test or a file with URL:s');
  process.exit(1);
}

// If we supply a configuration file, then it will ovveride all current conf, meaning
// we can easily use the same conf over and over again
if (config.configFile) {
  config = JSON.parse(fs.readFileSync(config.configFile));
}

config.runYslow = config.noYslow?false:true;

// The run always has a fresh date
config.run = {};
config.run.date = new Date();

// Setup the absolute result dir
if (config.url) {
  config.urlObject = urlParser.parse(config.url);
  config.run.absResultDir = path.join(__dirname, '../',config.resultBaseDir, config.urlObject.hostname, dateFormat(config.run.date, "yyyy-mm-dd-HH-MM-ss") );
} else if (config.file) {
  // TODO handle the file name in a good way if it contains chars that will not fit in a dir
  config.run.absResultDir = path.join(__dirname, '../',config.resultBaseDir, config.file, dateFormat(config.run.date, "yyyy-mm-dd-HH-MM-ss") );
} else if (config.sites) {
  // The log file will end up here
  config.run.absResultDir = path.join(__dirname, '../',config.resultBaseDir, 'sites', dateFormat(config.run.date, "yyyy-mm-dd-HH-MM-ss"));
}
fs.mkdirsSync(config.run.absResultDir);

// Parse the proxy info as a real URL
if (config.proxy) {
  config.urlProxyObject = urlParser.parse(config.proxy);
}

if (config.thresholdFile)
  config.thresholds = require(config.thresholdFile);

if (config.timingsThresholdFile)
  config.timingThresholds = require(timingsThresholdFile);
else
  config.timingThresholds = require('../conf/junit-timings.json');

// decide which rules to use ...
if (config.profile === 'mobile') {
  config.rules= require('../conf/mobile-rules.json');
  config.ruleSet = "sitespeed.io-mobile";
  config.userAgent = "Mozilla/5.0 (iPad; CPU OS 6_0 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/6.0 Mobile/10A5376e Safari/8536.25";
  config.viewPort = "320x444";
}
else {
  config.rules= require(config.limitFile);
  // The rest use the default/configured one, will that work? :)
}

config.summaryBoxes = ['ruleScore'];

config.sitesColumns = ['requests','criticalPathScore','pageWeight','cacheTime','ruleScore'];

if (config.googleKey) {
  config.summaryBoxes.push('gpsi.score');
  config.sitesColumns.push('gpsi.score');
}
config.summaryBoxes.push('criticalPathScore', 'jsSyncInHead',
  'jsPerPage', 'cssPerPage', 'cssImagesPerPage', 'fontsPerPage',
  'imagesPerPage', 'requests', 'requestsWithoutExpires', 'requestsWithoutGzip',
  'docWeight', 'jsWeightPerPage', 'cssWeightPerPage', 'imageWeightPerPage',
  'pageWeight', 'browserScaledImages', 'spofPerPage', 'numberOfDomains',
  'singleDomainRequests', 'redirectsPerPage', 'cacheTime', 'timeSinceLastMod');

if (config.webpagetestUrl) {
  config.summaryBoxes.push('wpt.imageSavings','wpt.speedIndex','wpt.visualComplete');
  config.sitesColumns.push('wpt.speedIndex');
}

if (config.browser) {
  config.summaryBoxes.push('serverResponseTime','backEndTime','frontEndTime','domContentLoadedTime','pageLoadTime','firstPaintTime');
  config.sitesColumns.push('serverResponseTime','domContentLoadedTime');
}

if (config.boxes) {
  if (config.boxes[0].indexOf('+')===0) {
      config.boxes[0].split(',').forEach(function (box) {
      if (box.indexOf('+')===0)
        config.summaryBoxes.push(box.substring(1));
      else
        config.summaryBoxes.push(box);
      });
  }
  else
    config.summaryBoxes = config.boxes[0].split(',');
}

if (config.runYslow)
  config.pageColumns = ['yslow.assets.js','yslow.assets.css','yslow.assets.img','yslow.requests','rules.expiresmod.items','yslow.pageSize', 'rules.avoidscalingimages.items','rules.criticalpath'];

if (config.browser)
  config.pageColumns.push('timings.serverResponseTime.median','timings.domContentLoadedTime.median');

if (config.googleKey)
  config.pageColumns.push('gpsi.gscore');

if (config.webpagetestUrl)
  config.pageColumns.push('wpt.speedIndex');

if (config.columns) {
  config.pageColumns = config.columns[0].split(',');
}

config.dataDir = 'data';

config.supportedBrowsers = supportedBrowsers;

module.exports = config;
<|endoftext|>