<commit_before>const {wasm2json, json2wasm} = require('wasm-json-toolkit')
const wasmMetering = require('wasm-metering')
const ReferanceMap = require('reference-map')
const Message = require('./message.js')
const customTypes = require('./customTypes.js')
const injectGlobals = require('./injectGlobals.js')
const typeCheckWrapper = require('./typeCheckWrapper.js')
const {FunctionRef, ModuleRef, DEFAULTS} = require('./systemObjects.js')

const nativeTypes = new Set(['i32', 'i64', 'f32', 'f64'])
const LANGUAGE_TYPES = {
  0x0: 'actor',
  0x1: 'buf',
  0x02: 'elem',
  0x03: 'link',
  0x04: 'id',
  0x7f: 'i32',
  0x7e: 'i64',
  0x7d: 'f32',
  0x7c: 'f64',
  0x70: 'anyFunc',
  0x60: 'func',
  0x40: 'block_type'
}
const FUNC_INDEX_OFFSET = 1

function generateWrapper (funcRef, container) {
  let wrapper = typeCheckWrapper(funcRef.params)
  const wasm = json2wasm(wrapper)
  const mod = WebAssembly.Module(wasm)
  const self = funcRef
  wrapper = WebAssembly.Instance(mod, {
    'env': {
      'checkTypes': function () {
        const args = [...arguments]
        const checkedArgs = []
        while (args.length) {
          const type = LANGUAGE_TYPES[args.shift()]
          let arg = args.shift()
          if (!nativeTypes.has(type)) {
            arg = container.refs.get(arg, type)
          }
          checkedArgs.push(arg)
        }
        const message = new Message({
          funcRef: self,
          funcArguments: checkedArgs
        })
        container.actor.send(message)
      }
    }
  })
  wrapper.exports.check.object = funcRef
  return wrapper
}

module.exports = class WasmContainer {
  constructor (actor) {
    this.actor = actor
    this.refs = new ReferanceMap()
  }

  static validateWasm (wasm, id) {
    if (!WebAssembly.validate(wasm)) {
      throw new Error('invalid wasm binary')
    }
    let moduleJSON = wasm2json(wasm)
    const json = customTypes.mergeTypeSections(moduleJSON)
    moduleJSON = wasmMetering.meterJSON(moduleJSON, {
      meterType: 'i32'
    })

    // initialize the globals
    let numOfGlobals = json.globals.length
    if (numOfGlobals) {
      moduleJSON = injectGlobals(moduleJSON, json.globals)
    }
    // recompile the wasm
    wasm = json2wasm(moduleJSON)
    const modRef = ModuleRef.fromMetaJSON(json, id)
    return {
      wasm,
      json,
      modRef
    }
  }

  static async onCreation (unverifiedWasm, id, tree) {
    const cachedb = tree.dag._dag
    let {json, wasm, modRef} = this.validateWasm(unverifiedWasm, id)
    await Promise.all([
      new Promise((resolve, reject) => {
        cachedb.put(id.id.toString() + 'meta', JSON.stringify(json), resolve)
      }),
      new Promise((resolve, reject) => {
        cachedb.put(id.id.toString() + 'code', wasm.toString('hex'), resolve)
      })
    ])
    return modRef
  }

  getInterface (funcRef) {
    const self = this
    return {
      func: {
        externalize: index => {
          const func = this.instance.exports.table.get(index)
          const object = func.object
          if (object) {
            // externalize a pervously internalized function
            return self.refs.add(object)
          } else {
            const params = self.json.types[self.json.indexes[func.name - FUNC_INDEX_OFFSET]].params
            const ref = new FunctionRef(true, func.tableIndex, params, self.actor.id)
            return self.refs.add(ref, 'func')
          }
        },
        internalize: (ref, index) => {
          const funcRef = self.refs.get(ref, 'func')
          const wrapper = generateWrapper(funcRef, self)
          this.instance.exports.table.set(index, wrapper.exports.check)
        },
        catch: (ref, catchRef) => {
          const {funcRef} = self.refs.get(ref, FunctionRef)
          const {funcRef: catchFunc} = self.refs.get(ref, FunctionRef)
          funcRef.catch = catchFunc
        },
        get_gas_budget: (funcRef) => {
          const func = self.refs.get(funcRef, 'func')
          return func.gas
        },
        set_gas_budget: (funcRef, amount) => {
          const func = self.refs.get(funcRef, 'func')
          func.gas = amount
        }
      },
      link: {
        wrap: ref => {
          const obj = this.refs.get(ref)
          const link = {'/': obj}
          return this.refs.add(link, 'link')
        },
        unwrap: async (ref, cb) => {
          const obj = this.refs.get(ref, 'link')
          const promise = this.actor.tree.dataStore.get(obj)
          await this._opsQueue.push(promise)
          // todo
        }
      },
      module: {
        new: dataRef => {

        },
        exports: (modRef, offset, length) => {
          const mod = this.refs.get(modRef, 'mod')
          let name = this.getMemory(offset, length)
          name = Buffer.from(name).toString()
          const funcRef = mod.getFuncRef(name)
          return this.refs.add(funcRef, 'func')
        },
        self: () => {
          return this.refs.add(this.modSelf, 'mod')
        }
      },
      memory: {
        externalize: (index, length) => {
          const buf = Buffer.from(this.getMemory(index, length))
          return this.refs.add(buf, 'buf')
        },
        internalize: (dataRef, srcOffset, sinkOffset, length) => {
          let buf = this.refs.get(dataRef, 'buf')
          buf = buf.subarray(srcOffset, length)
          const mem = this.getMemory(sinkOffset, buf.length)
          mem.set(buf)
        },
        length (dataRef) {
          let buf = this.refs.get(dataRef, 'buf')
          return buf.length
        }
      },
      table: {
        externalize: (index, length) => {
          const mem = Buffer.from(this.getMemory(index, length * 4))
          const objects = []
          while (length--) {
            const ref = mem.readUInt32LE(length * 4)
            const obj = this.refs.get(ref)
            objects.unshift(obj)
          }
          return this.refs.add(objects, 'elem')
        },
        internalize: (elemRef, srcOffset, sinkOffset, length) => {
          let table = this.refs.get(elemRef, 'elem')
          const buf = table.slice(srcOffset, srcOffset + length).map(obj => this.refs.add(obj))
          const mem = this.getMemory(sinkOffset, length)
          mem.set(buf)
        },
        length (elemRef) {
          let elem = this.refs.get(elemRef, 'elem')
          return elem.length
        }
      },
      metering: {
        usegas: amount => {
          this.actor.incrementTicks(amount)
          funcRef.gas -= amount
          if (funcRef.gas < 0) {
            throw new Error('out of gas! :(')
          }
        }
      }
    }
  }

  async onMessage (message) {
    const funcRef = message.funcRef
    const intef = this.getInterface(funcRef)
    this.instance = WebAssembly.Instance(this.mod, intef)
    // map table indexes
    const table = this.instance.exports.table
    if (table) {
      let length = table.length
      while (length--) {
        const func = table.get(length)
        if (func) {
          func.tableIndex = length
        }
      }
    }
    // import references
    const args = message.funcArguments.map((arg, index) => {
      const type = funcRef.params[index]
      if (nativeTypes.has(type)) {
        return arg
      } else {
        return this.refs.add(arg, type)
      }
    })

    // setup globals
    let numOfGlobals = this.json.globals.length
    if (numOfGlobals) {
      const refs = []
      while (numOfGlobals--) {
        const obj = this.actor.storage[numOfGlobals] || DEFAULTS[this.json.globals[numOfGlobals].type]
        refs.push(this.refs.add(obj, this.json.globals[numOfGlobals].type))
      }
      this.instance.exports.setter_globals(...refs)
    }

    // call entrypoint function
    if (funcRef.private) {
      this.instance.exports.table.get(funcRef.identifier)(...args)
    } else {
      this.instance.exports[funcRef.identifier](...args)
    }
    await this.onDone()

    // store globals
    numOfGlobals = this.json.globals.length
    if (numOfGlobals) {
      this.actor.storage = []
      this.instance.exports.getter_globals()
      const mem = this.getMemory(0, numOfGlobals)
      while (numOfGlobals--) {
        const ref = mem[numOfGlobals]
        this.actor.storage.push(this.refs.get(ref, this.json.globals[numOfGlobals].type))
      }
    }

    this.refs.clear()
  }

  /**
   * returns a promise that resolves when the wasm instance is done running
   * @returns {Promise}
   */
  async onDone () {
    let prevOps
    while (prevOps !== this._opsQueue) {
      prevOps = this._opsQueue
      await prevOps
    }
  }

  /**
   * Pushed an async operation to the a promise queue that
   * @returns {Promise} the returned promise resolves in the order the intail
   * operation was pushed to the queue
   */
  pushOpsQueue (promise) {
    this._opsQueue = Promise.all([this._opsQueue, promise])
    return this._opsQueue
  }

  async onStartup () {
    let [json, wasm] = await Promise.all([
      new Promise((resolve, reject) => {
        this.actor.cachedb.get(this.actor.id.id.toString() + 'meta', (err, json) => {
          if (err) {
            reject(err)
          } else {
            resolve(json)
          }
        })
      }),
      new Promise((resolve, reject) => {
        this.actor.cachedb.get(this.actor.id.id.toString() + 'code', (err, wasm) => {
          if (err) {
            reject(err)
          } else {
            resolve(wasm)
          }
        })
      })
    ])
    wasm = Buffer.from(wasm, 'hex')
    json = JSON.parse(json)
    this.mod = WebAssembly.Module(wasm)
    this.json = json
    this.modSelf = ModuleRef.fromMetaJSON(json, this.actor.id)
  }

  getMemory (offset, length) {
    return new Uint8Array(this.instance.exports.memory.buffer, offset, length)
  }

  static get typeId () {
    return 9
  }
}
<commit_msg>get{8,32}Memory<commit_after>const {wasm2json, json2wasm} = require('wasm-json-toolkit')
const wasmMetering = require('wasm-metering')
const ReferanceMap = require('reference-map')
const Message = require('./message.js')
const customTypes = require('./customTypes.js')
const injectGlobals = require('./injectGlobals.js')
const typeCheckWrapper = require('./typeCheckWrapper.js')
const {FunctionRef, ModuleRef, DEFAULTS} = require('./systemObjects.js')

const nativeTypes = new Set(['i32', 'i64', 'f32', 'f64'])
const LANGUAGE_TYPES = {
  0x0: 'actor',
  0x1: 'buf',
  0x02: 'elem',
  0x03: 'link',
  0x04: 'id',
  0x7f: 'i32',
  0x7e: 'i64',
  0x7d: 'f32',
  0x7c: 'f64',
  0x70: 'anyFunc',
  0x60: 'func',
  0x40: 'block_type'
}
const FUNC_INDEX_OFFSET = 1

function generateWrapper (funcRef, container) {
  let wrapper = typeCheckWrapper(funcRef.params)
  const wasm = json2wasm(wrapper)
  const mod = WebAssembly.Module(wasm)
  const self = funcRef
  wrapper = WebAssembly.Instance(mod, {
    'env': {
      'checkTypes': function () {
        const args = [...arguments]
        const checkedArgs = []
        while (args.length) {
          const type = LANGUAGE_TYPES[args.shift()]
          let arg = args.shift()
          if (!nativeTypes.has(type)) {
            arg = container.refs.get(arg, type)
          }
          checkedArgs.push(arg)
        }
        const message = new Message({
          funcRef: self,
          funcArguments: checkedArgs
        })
        container.actor.send(message)
      }
    }
  })
  wrapper.exports.check.object = funcRef
  return wrapper
}

module.exports = class WasmContainer {
  constructor (actor) {
    this.actor = actor
    this.refs = new ReferanceMap()
  }

  static validateWasm (wasm, id) {
    if (!WebAssembly.validate(wasm)) {
      throw new Error('invalid wasm binary')
    }
    let moduleJSON = wasm2json(wasm)
    const json = customTypes.mergeTypeSections(moduleJSON)
    moduleJSON = wasmMetering.meterJSON(moduleJSON, {
      meterType: 'i32'
    })

    // initialize the globals
    let numOfGlobals = json.globals.length
    if (numOfGlobals) {
      moduleJSON = injectGlobals(moduleJSON, json.globals)
    }
    // recompile the wasm
    wasm = json2wasm(moduleJSON)
    const modRef = ModuleRef.fromMetaJSON(json, id)
    return {
      wasm,
      json,
      modRef
    }
  }

  static async onCreation (unverifiedWasm, id, tree) {
    const cachedb = tree.dag._dag
    let {json, wasm, modRef} = this.validateWasm(unverifiedWasm, id)
    await Promise.all([
      new Promise((resolve, reject) => {
        cachedb.put(id.id.toString() + 'meta', JSON.stringify(json), resolve)
      }),
      new Promise((resolve, reject) => {
        cachedb.put(id.id.toString() + 'code', wasm.toString('hex'), resolve)
      })
    ])
    return modRef
  }

  getInterface (funcRef) {
    const self = this
    return {
      func: {
        externalize: index => {
          const func = this.instance.exports.table.get(index)
          const object = func.object
          if (object) {
            // externalize a pervously internalized function
            return self.refs.add(object)
          } else {
            const params = self.json.types[self.json.indexes[func.name - FUNC_INDEX_OFFSET]].params
            const ref = new FunctionRef(true, func.tableIndex, params, self.actor.id)
            return self.refs.add(ref, 'func')
          }
        },
        internalize: (ref, index) => {
          const funcRef = self.refs.get(ref, 'func')
          const wrapper = generateWrapper(funcRef, self)
          this.instance.exports.table.set(index, wrapper.exports.check)
        },
        catch: (ref, catchRef) => {
          const {funcRef} = self.refs.get(ref, FunctionRef)
          const {funcRef: catchFunc} = self.refs.get(ref, FunctionRef)
          funcRef.catch = catchFunc
        },
        get_gas_budget: (funcRef) => {
          const func = self.refs.get(funcRef, 'func')
          return func.gas
        },
        set_gas_budget: (funcRef, amount) => {
          const func = self.refs.get(funcRef, 'func')
          func.gas = amount
        }
      },
      link: {
        wrap: ref => {
          const obj = this.refs.get(ref)
          const link = {'/': obj}
          return this.refs.add(link, 'link')
        },
        unwrap: async (ref, cb) => {
          const obj = this.refs.get(ref, 'link')
          const promise = this.actor.tree.dataStore.get(obj)
          await this._opsQueue.push(promise)
          // todo
        }
      },
      module: {
        new: dataRef => {

        },
        exports: (modRef, offset, length) => {
          const mod = this.refs.get(modRef, 'mod')
          let name = this.getMemory(offset, length)
          name = Buffer.from(name).toString()
          const funcRef = mod.getFuncRef(name)
          return this.refs.add(funcRef, 'func')
        },
        self: () => {
          return this.refs.add(this.modSelf, 'mod')
        }
      },
      memory: {
        externalize: (index, length) => {
          const buf = Buffer.from(this.getMemory(index, length))
          return this.refs.add(buf, 'buf')
        },
        internalize: (dataRef, srcOffset, sinkOffset, length) => {
          let buf = this.refs.get(dataRef, 'buf')
          buf = buf.subarray(srcOffset, length)
          const mem = this.getMemory(sinkOffset, buf.length)
          mem.set(buf)
        },
        length (dataRef) {
          let buf = this.refs.get(dataRef, 'buf')
          return buf.length
        }
      },
      table: {
        externalize: (index, length) => {
          const mem = Buffer.from(this.getMemory(index, length * 4))
          const objects = []
          while (length--) {
            const ref = mem.readUInt32LE(length * 4)
            const obj = this.refs.get(ref)
            objects.unshift(obj)
          }
          return this.refs.add(objects, 'elem')
        },
        internalize: (elemRef, srcOffset, sinkOffset, length) => {
          let table = this.refs.get(elemRef, 'elem')
          const buf = table.slice(srcOffset, srcOffset + length).map(obj => this.refs.add(obj))
          const mem = this.get32Memory(sinkOffset, length)
          mem.set(buf)
        },
        length (elemRef) {
          let elem = this.refs.get(elemRef, 'elem')
          return elem.length
        }
      },
      metering: {
        usegas: amount => {
          this.actor.incrementTicks(amount)
          funcRef.gas -= amount
          if (funcRef.gas < 0) {
            throw new Error('out of gas! :(')
          }
        }
      }
    }
  }

  async onMessage (message) {
    const funcRef = message.funcRef
    const intef = this.getInterface(funcRef)
    this.instance = WebAssembly.Instance(this.mod, intef)
    // map table indexes
    const table = this.instance.exports.table
    if (table) {
      let length = table.length
      while (length--) {
        const func = table.get(length)
        if (func) {
          func.tableIndex = length
        }
      }
    }
    // import references
    const args = message.funcArguments.map((arg, index) => {
      const type = funcRef.params[index]
      if (nativeTypes.has(type)) {
        return arg
      } else {
        return this.refs.add(arg, type)
      }
    })

    // setup globals
    let numOfGlobals = this.json.globals.length
    if (numOfGlobals) {
      const refs = []
      while (numOfGlobals--) {
        const obj = this.actor.storage[numOfGlobals] || DEFAULTS[this.json.globals[numOfGlobals].type]
        refs.push(this.refs.add(obj, this.json.globals[numOfGlobals].type))
      }
      this.instance.exports.setter_globals(...refs)
    }

    // call entrypoint function
    if (funcRef.private) {
      this.instance.exports.table.get(funcRef.identifier)(...args)
    } else {
      this.instance.exports[funcRef.identifier](...args)
    }
    await this.onDone()

    // store globals
    numOfGlobals = this.json.globals.length
    if (numOfGlobals) {
      this.actor.storage = []
      this.instance.exports.getter_globals()
      const mem = this.get32Memory(0, numOfGlobals)
      while (numOfGlobals--) {
        const ref = mem[numOfGlobals]
        this.actor.storage.push(this.refs.get(ref, this.json.globals[numOfGlobals].type))
      }
    }

    this.refs.clear()
  }

  /**
   * returns a promise that resolves when the wasm instance is done running
   * @returns {Promise}
   */
  async onDone () {
    let prevOps
    while (prevOps !== this._opsQueue) {
      prevOps = this._opsQueue
      await prevOps
    }
  }

  /**
   * Pushed an async operation to the a promise queue that
   * @returns {Promise} the returned promise resolves in the order the intail
   * operation was pushed to the queue
   */
  pushOpsQueue (promise) {
    this._opsQueue = Promise.all([this._opsQueue, promise])
    return this._opsQueue
  }

  async onStartup () {
    let [json, wasm] = await Promise.all([
      new Promise((resolve, reject) => {
        this.actor.cachedb.get(this.actor.id.id.toString() + 'meta', (err, json) => {
          if (err) {
            reject(err)
          } else {
            resolve(json)
          }
        })
      }),
      new Promise((resolve, reject) => {
        this.actor.cachedb.get(this.actor.id.id.toString() + 'code', (err, wasm) => {
          if (err) {
            reject(err)
          } else {
            resolve(wasm)
          }
        })
      })
    ])
    wasm = Buffer.from(wasm, 'hex')
    json = JSON.parse(json)
    this.mod = WebAssembly.Module(wasm)
    this.json = json
    this.modSelf = ModuleRef.fromMetaJSON(json, this.actor.id)
  }

  get8Memory (offset, length) {
    return new Uint8Array(this.instance.exports.memory.buffer, offset, length)
  }

  get32Memory (offset, length) {
    return new Uint32Array(this.instance.exports.memory.buffer, offset, length)
  }

  static get typeId () {
    return 9
  }
}
<|endoftext|>


<commit_before>import React from 'react';

import Wrapper from 'common/Wrapper';
import SPELLS from 'common/SPELLS';
import SpellLink from 'common/SpellLink';
import ITEMS from 'common/ITEMS';
import ItemLink from 'common/ItemLink';

import CoreChecklist, { Rule, Requirement } from 'Parser/Core/Modules/Features/Checklist';
import { PreparationRule } from 'Parser/Core/Modules/Features/Checklist/Rules';
import { GenericCastEfficiencyRequirement } from 'Parser/Core/Modules/Features/Checklist/Requirements';
import CastEfficiency from 'Parser/Core/Modules/CastEfficiency';
import Combatants from 'Parser/Core/Modules/Combatants';
import LegendaryUpgradeChecker from 'Parser/Core/Modules/Items/LegendaryUpgradeChecker';
import LegendaryCountChecker from 'Parser/Core/Modules/Items/LegendaryCountChecker';
import PrePotion from 'Parser/Core/Modules/Items/PrePotion';
import EnchantChecker from 'Parser/Core/Modules/Items/EnchantChecker';
import AbilityTracker from 'Parser/Core/Modules/AbilityTracker';

import AlwaysBeCasting from './AlwaysBeCasting';
import HolyPowerDetails from '../HolyPower/HolyPowerDetails';
import BoWProcTracker from '../PaladinCore/BoWProcTracker';
import Judgment from '../PaladinCore/Judgment';
import Liadrins from '../Items/LiadrinsFuryUnleashed';
import Whisper from '../Items/WhisperOfTheNathrezim';
import BotA from '../PaladinCore/BlessingOfTheAshbringer';

class Checklist extends CoreChecklist {
	static dependencies = {
		castEfficiency: CastEfficiency,
    combatants: Combatants,
    alwaysBeCasting: AlwaysBeCasting,
    legendaryUpgradeChecker: LegendaryUpgradeChecker,
    legendaryCountChecker: LegendaryCountChecker,
    prePotion: PrePotion,
    enchantChecker: EnchantChecker,
    abilityTracker: AbilityTracker,

    holyPowerDetails: HolyPowerDetails,
    boWProcTracker: BoWProcTracker,
    judgment: Judgment,
    liadrins: Liadrins,
    whisper: Whisper,
    bota: BotA,
	};

	rules = [
    new Rule({
    	name: 'Always be casting',
   		description: <Wrapper>You should try to avoid doing nothing during the fight. If you have to move, use your <SpellLink id={SPELLS.DIVINE_STEED.id} icon/> to minimize downtime. Also use ranged abilities like <SpellLink id={SPELLS.JUDGMENT_CAST.id} icon/>, <SpellLink id={SPELLS.BLADE_OF_JUSTICE.id} icon/>, or <SpellLink id={SPELLS.DIVINE_STORM.id} icon/> if out of melee range for extended periods.</Wrapper>,
    		requirements: () => {
      		return [
        			new Requirement({
          			name: 'Downtime',
          			check: () => this.alwaysBeCasting.suggestionThresholds,
        			}),
      		];
    		},
  	}),
  	new PreparationRule(),
  	new Rule({
  		name: 'Use core abilities as often as possible',
  		description:<Wrapper>Spells with short cooldowns like <SpellLink id={SPELLS.JUDGMENT_CAST.id} icon/>, <SpellLink id={SPELLS.BLADE_OF_JUSTICE.id} icon/>, and <SpellLink id={SPELLS.CRUSADER_STRIKE.id} icon/> should be used as often as possible.</Wrapper>,
  		requirements: () => {
  			const combatant = this.combatants.selected;
  			return [
  				new GenericCastEfficiencyRequirement({
  					spell: SPELLS.ZEAL_TALENT,
  					when: combatant.hasTalent(SPELLS.ZEAL_TALENT.id),
  				}),
  				new GenericCastEfficiencyRequirement({
  					spell: SPELLS.CRUSADER_STRIKE,
  					when: !combatant.hasTalent(SPELLS.ZEAL_TALENT.id),
  				}),
  				new GenericCastEfficiencyRequirement({
  					spell: SPELLS.JUDGMENT_CAST,
  				}),
  				new GenericCastEfficiencyRequirement({
  					spell: SPELLS.BLADE_OF_JUSTICE,
  					when: !combatant.hasTalent(SPELLS.DIVINE_HAMMER_TALENT.id),
  				}),
  				new GenericCastEfficiencyRequirement({
  					spell: SPELLS.DIVINE_HAMMER_TALENT,
  					when: combatant.hasTalent(SPELLS.DIVINE_HAMMER_TALENT.id),
  				}),
  			];
  		},
  	}),
  	new Rule({
  		name: 'Use your cooldowns',
  		description: <Wrapper>Retribution Paladin is a very cooldown dependant spec. Make sure you are keeping <SpellLink id={SPELLS.CRUSADE_TALENT.id} icon/> and <SpellLink id={SPELLS.WAKE_OF_ASHES.id} icon /> on cooldown.</Wrapper>,
  		requirements: () => {
  			const combatant = this.combatants.selected;
  			return [
  				new GenericCastEfficiencyRequirement({
  					spell: SPELLS.CRUSADE_TALENT,
  					when: combatant.hasTalent(SPELLS.CRUSADE_TALENT.id),
  				}),
  				new GenericCastEfficiencyRequirement({
  					spell: SPELLS.AVENGING_WRATH,
  					when: !combatant.hasTalent(SPELLS.CRUSADE_TALENT.id),
  				}),
  				new GenericCastEfficiencyRequirement({
  					spell: SPELLS.HOLY_WRATH_TALENT,
  					when: combatant.hasTalent(SPELLS.HOLY_WRATH_TALENT.id),
  				}),
  				new GenericCastEfficiencyRequirement({
  					spell: SPELLS.WAKE_OF_ASHES,
  				}),
  			];
  		},
  	}),
  	new Rule({
  		name: 'Pick the right tools for the fight',
  		description: 'The throughput gain of some legendaries might vary greatly. Consider switching to a more reliable alternative if something is underperforming regularly.',
  		requirements: () => {
  			return [
  				new Requirement({
  					name: <ItemLink id={ITEMS.LIADRINS_FURY_UNLEASHED.id} icon/>,
  					check: () => this.liadrins.suggestionThresholds,
  					when: this.liadrins.active,
  				}),
  				new Requirement({
  					name: <ItemLink id={ITEMS.WHISPER_OF_THE_NATHREZIM.id} icon/>,
  					check: () => this.whisper.suggestionThresholds,
  					when: this.whisper.active,
  				}),
  			];
  		},
  	}),
  	new Rule({
  		name: 'Use your resources efficently',
  		description: <Wrapper>Holy Power is your main resource and it's very important not to let it cap. You should also only be spending Holy Power inside of the <SpellLink id={SPELLS.JUDGMENT_CAST.id} icon/> debuff window.</Wrapper>,
  		requirements: () => {
  			return [
  				new Requirement({
  					name: 'Wasted Holy Power',
  					check: () => this.holyPowerDetails.suggestionThresholds,
  				}),
  				new Requirement({
  					name: 'Holy power spent without Judgment',
  					check: () => this.judgment.suggestionThresholds,
  				}),
  				new Requirement({
  					name: 'Wasted Blade of Wrath procs',
  					check: () => this.boWProcTracker.suggestionThresholds,
  				}),
  			];
  		},
  	}),
  	new Rule({
      name: 'Use your utility and defensive spells',
      description: <Wrapper>Use other spells in your toolkit to your advantage. For example, you can use <SpellLink id={SPELLS.SHIELD_OF_VENGEANCE.id} icon/> to mitigate some damage and <SpellLink id={SPELLS.LAY_ON_HANDS.id} icon/> to save your own or someone elses life.</Wrapper>,
      requirements: () => {
        return [
	        new GenericCastEfficiencyRequirement({
	          spell: SPELLS.SHIELD_OF_VENGEANCE,
	        }),
	        new GenericCastEfficiencyRequirement({
            spell: SPELLS.LAY_ON_HANDS,
					}),
          new Requirement({
            name: <Wrapper> <SpellLink id={SPELLS.BLESSING_OF_THE_ASHBRINGER_BUFF.id} icon/></Wrapper>,
            check: () => this.bota.suggestionThresholds,
          }),
        ];
      },
    }),
	];
}

export default Checklist;<commit_msg>updated checklist to makre more sense<commit_after>import React from 'react';

import Wrapper from 'common/Wrapper';
import SPELLS from 'common/SPELLS';
import SpellLink from 'common/SpellLink';
import ITEMS from 'common/ITEMS';
import ItemLink from 'common/ItemLink';

import CoreChecklist, { Rule, Requirement } from 'Parser/Core/Modules/Features/Checklist';
import { PreparationRule } from 'Parser/Core/Modules/Features/Checklist/Rules';
import { GenericCastEfficiencyRequirement } from 'Parser/Core/Modules/Features/Checklist/Requirements';
import CastEfficiency from 'Parser/Core/Modules/CastEfficiency';
import Combatants from 'Parser/Core/Modules/Combatants';
import LegendaryUpgradeChecker from 'Parser/Core/Modules/Items/LegendaryUpgradeChecker';
import LegendaryCountChecker from 'Parser/Core/Modules/Items/LegendaryCountChecker';
import PrePotion from 'Parser/Core/Modules/Items/PrePotion';
import EnchantChecker from 'Parser/Core/Modules/Items/EnchantChecker';
import AbilityTracker from 'Parser/Core/Modules/AbilityTracker';

import AlwaysBeCasting from './AlwaysBeCasting';
import HolyPowerDetails from '../HolyPower/HolyPowerDetails';
import BoWProcTracker from '../PaladinCore/BoWProcTracker';
import Judgment from '../PaladinCore/Judgment';
import Liadrins from '../Items/LiadrinsFuryUnleashed';
import Whisper from '../Items/WhisperOfTheNathrezim';
import BotA from '../PaladinCore/BlessingOfTheAshbringer';

class Checklist extends CoreChecklist {
	static dependencies = {
		castEfficiency: CastEfficiency,
    combatants: Combatants,
    alwaysBeCasting: AlwaysBeCasting,
    legendaryUpgradeChecker: LegendaryUpgradeChecker,
    legendaryCountChecker: LegendaryCountChecker,
    prePotion: PrePotion,
    enchantChecker: EnchantChecker,
    abilityTracker: AbilityTracker,

    holyPowerDetails: HolyPowerDetails,
    boWProcTracker: BoWProcTracker,
    judgment: Judgment,
    liadrins: Liadrins,
    whisper: Whisper,
    bota: BotA,
	};

	rules = [
    new Rule({
    	name: 'Always be casting',
   		description: <Wrapper>You should try to avoid doing nothing during the fight. If you have to move, use your <SpellLink id={SPELLS.DIVINE_STEED.id} icon/> to minimize downtime. Also use ranged abilities like <SpellLink id={SPELLS.JUDGMENT_CAST.id} icon/>, <SpellLink id={SPELLS.BLADE_OF_JUSTICE.id} icon/>, or <SpellLink id={SPELLS.DIVINE_STORM.id} icon/> if out of melee range for extended periods.</Wrapper>,
    		requirements: () => {
      		return [
        			new Requirement({
          			name: 'Downtime',
          			check: () => this.alwaysBeCasting.suggestionThresholds,
        			}),
      		];
    		},
  	}),
  	new PreparationRule(),
  	new Rule({
  		name: 'Use core abilities as often as possible',
  		description:<Wrapper>Spells with short cooldowns like <SpellLink id={SPELLS.JUDGMENT_CAST.id} icon/>, <SpellLink id={SPELLS.BLADE_OF_JUSTICE.id} icon/>, and <SpellLink id={SPELLS.CRUSADER_STRIKE.id} icon/> should be used as often as possible.</Wrapper>,
  		requirements: () => {
  			const combatant = this.combatants.selected;
  			return [
  				new GenericCastEfficiencyRequirement({
  					spell: SPELLS.ZEAL_TALENT,
  					when: combatant.hasTalent(SPELLS.ZEAL_TALENT.id),
  				}),
  				new GenericCastEfficiencyRequirement({
  					spell: SPELLS.CRUSADER_STRIKE,
  					when: !combatant.hasTalent(SPELLS.ZEAL_TALENT.id),
  				}),
  				new GenericCastEfficiencyRequirement({
  					spell: SPELLS.JUDGMENT_CAST,
  				}),
  				new GenericCastEfficiencyRequirement({
  					spell: SPELLS.BLADE_OF_JUSTICE,
  					when: !combatant.hasTalent(SPELLS.DIVINE_HAMMER_TALENT.id),
  				}),
  				new GenericCastEfficiencyRequirement({
  					spell: SPELLS.DIVINE_HAMMER_TALENT,
  					when: combatant.hasTalent(SPELLS.DIVINE_HAMMER_TALENT.id),
  				}),
  			];
  		},
  	}),
  	new Rule({
  		name: 'Use your cooldowns',
  		description: <Wrapper>Retribution Paladin is a very cooldown dependant spec. Make sure you are keeping <SpellLink id={SPELLS.CRUSADE_TALENT.id} icon/> and <SpellLink id={SPELLS.WAKE_OF_ASHES.id} icon /> on cooldown.</Wrapper>,
  		requirements: () => {
  			const combatant = this.combatants.selected;
  			return [
  				new GenericCastEfficiencyRequirement({
  					spell: SPELLS.CRUSADE_TALENT,
  					when: combatant.hasTalent(SPELLS.CRUSADE_TALENT.id),
  				}),
  				new GenericCastEfficiencyRequirement({
  					spell: SPELLS.AVENGING_WRATH,
  					when: !combatant.hasTalent(SPELLS.CRUSADE_TALENT.id),
  				}),
  				new GenericCastEfficiencyRequirement({
  					spell: SPELLS.HOLY_WRATH_TALENT,
  					when: combatant.hasTalent(SPELLS.HOLY_WRATH_TALENT.id),
  				}),
  				new GenericCastEfficiencyRequirement({
  					spell: SPELLS.WAKE_OF_ASHES,
  				}),
  			];
  		},
  	}),
  	new Rule({
  		name: 'Pick the right tools for the fight',
  		description: 'The throughput gain of some legendaries might vary greatly. Consider switching to a more reliable alternative if something is underperforming regularly.',
  		requirements: () => {
  			return [
  				new Requirement({
  					name: <Wrapper>Holy power wasted from <ItemLink id={ITEMS.LIADRINS_FURY_UNLEASHED.id} icon/></Wrapper>,
  					check: () => this.liadrins.suggestionThresholds,
  					when: this.liadrins.active,
  				}),
  				new Requirement({
  					name: <Wrapper>Spenders with the <ItemLink id={ITEMS.WHISPER_OF_THE_NATHREZIM.id} icon/> buff</Wrapper>,
  					check: () => this.whisper.suggestionThresholds,
  					when: this.whisper.active,
  				}),
  			];
  		},
  	}),
  	new Rule({
  		name: 'Use your resources efficently',
  		description: <Wrapper>Holy Power is your main resource and it's very important not to let it cap. You should also only be spending Holy Power inside of the <SpellLink id={SPELLS.JUDGMENT_CAST.id} icon/> debuff window.</Wrapper>,
  		requirements: () => {
  			return [
  				new Requirement({
  					name: 'Wasted Holy Power',
  					check: () => this.holyPowerDetails.suggestionThresholds,
  				}),
  				new Requirement({
  					name: 'Holy power spent without Judgment',
  					check: () => this.judgment.suggestionThresholds,
  				}),
  				new Requirement({
  					name: 'Wasted Blade of Wrath procs',
  					check: () => this.boWProcTracker.suggestionThresholds,
  				}),
  			];
  		},
  	}),
  	new Rule({
      name: 'Use your utility and defensive spells',
      description: <Wrapper>Use other spells in your toolkit to your advantage. For example, you can use <SpellLink id={SPELLS.SHIELD_OF_VENGEANCE.id} icon/> to mitigate some damage and <SpellLink id={SPELLS.LAY_ON_HANDS.id} icon/> to save your own or someone elses life.</Wrapper>,
      requirements: () => {
        return [
	        new GenericCastEfficiencyRequirement({
	          spell: SPELLS.SHIELD_OF_VENGEANCE,
	        }),
	        new GenericCastEfficiencyRequirement({
            spell: SPELLS.LAY_ON_HANDS,
					}),
          new Requirement({
            name: <Wrapper> <SpellLink id={SPELLS.BLESSING_OF_THE_ASHBRINGER_BUFF.id} icon/></Wrapper>,
            check: () => this.bota.suggestionThresholds,
          }),
        ];
      },
    }),
	];
}

export default Checklist;<|endoftext|>


<commit_before>var logger = require('raptor-logging').logger(module);
var transport = require('raptor-modules/transport');
var parallel = require('raptor-async/parallel');
var nodePath = require('path');
var extend = require('raptor-util/extend');
var markoCompiler = require('marko/compiler');
var extend = require('raptor-util/extend');
var util = require('../util');

var types = {
    'marko': function(config, callback) {
        var value = this.value;

        if (Array.isArray(value)) {
            value = value.join('\n');
        }

        var dirname = nodePath.dirname(this.dictionaryInfo.absolutePath);
        var templatePath = nodePath.join(dirname, this.key + '.marko');
        var _this = this;

        var moduleName = this.dictionaryModuleName + '/' + this.key + '.marko';

        markoCompiler.compile(value, templatePath, function(err, code) {
            if (err) {
                return callback(err);
            }

            _this.beforeCode.push(transport.defineCode.sync(moduleName, code, {
                modulesRuntimeGlobal: config.modulesRuntimeGlobal
            }));

            var out = '';
            out += 'function(data) {\n';
            out += '        return require("marko").load(module.id + ' + JSON.stringify('/' + _this.key + '.marko') + ').renderSync(data);\n';
            out += '    }';
            callback(null, out);
        });
    }
};

function writeDictionary(config, dictionary, info, localeContext, callback) {

    var code = [];

    function createCompileTask(compiler, compilerContext, theLast, index, length) {
        return function(callback) {
            compiler.call(compilerContext, config, function(err, compiledCode) {
                if (err) {
                    return callback(err);
                }
                code.push('    ' + JSON.stringify(compilerContext.key) + ': ' + compiledCode);
                callback();
            });
        };
    }

    var work = [];

    var keys = Object.keys(dictionary);
    for (var i = 0; i < keys.length; i++) {
        var key = keys[i];
        var last = (i === keys.length - 1);

        if (dictionary.hasOwnProperty(key)) {
            var value = dictionary[key];
            var compiler = null;

            var pos = key.indexOf(':');
            if (pos !== -1) {
                var type = key.substring(0, pos).trim().toLowerCase();
                compiler = types[type];
                if (compiler) {
                    key = key.substring(pos + 1).trim();
                }
            }

            if (compiler) {

                var compilerContext = {
                    value: value,
                    key: key,
                    dictionaryInfo: info,
                    beforeCode: localeContext.beforeCode,
                    afterCode: localeContext.afterCode,
                    attributes: localeContext.attributes,
                    locale: localeContext.locale,
                    dictionaryModuleName: localeContext.localeModuleName + '/' + info.name
                };

                work.push(createCompileTask(compiler, compilerContext, last, i, keys.length));
            } else {
                code.push('    ' + JSON.stringify(key) + ': ' + JSON.stringify(value));
            }
        }
    }

    parallel(work, function(err) {
        callback(err, code.join(',\n'));
    });
}

function createReadDictionaryTask(config, info, dependency, localeContext, out) {
    return function(callback) {
        var work = new Array(dependency.locales.length + 1);

        work[0] = function(callback) {
            dependency.i18nContext.readRawDictionary(info.absolutePath, callback);
        };

        dependency.locales.forEach(function(locale, index) {
            work[index + 1] = function(callback) {
                var path = nodePath.join(info.localizedDir, locale + '.i18n.json');
                dependency.i18nContext.readRawDictionary(path, function(err, dict) {
                    if (err || !dict) {
                        return callback(err);
                    }

                    callback(null, dict[info.relativePath]);
                });
            };
        });

        parallel(work, function(err, result) {
            if (err) {
                return callback(err);
            }

            var merged = (result.length) ? extend({}, result[0]) : {};
            for (var i = 1; i < result.length; i++) {
                var raw = result[i];
                if (raw) {
                    extend(merged, raw);
                }
            }

            writeDictionary(config, merged, info, localeContext, function(err, dictionaryCode) {
                if (err) {
                    return callback(err);
                }

                var code = '\nmodule.exports = {\n';

                code += dictionaryCode;

                code += '\n}';

                var moduleName = localeContext.localeModuleName + '/' + info.name;

                callback(null, transport.defineCode.sync(moduleName, code, {
                    modulesRuntimeGlobal: config.modulesRuntimeGlobal
                }) + '\n');
            });

        });
    };
}

exports.create = function(config) {
    return {
        properties: {
            locale: 'string',
            i18nContext: 'object'
        },

        init: function() {

            var locale = util.normalizeLocaleCode(this.locale);

            this.defaultBundleName = 'i18n-' + locale;

            var locales = this.locales = new Array(2);

            if (this.locale.charAt(2) === '-') {
                locales[0] = locale.substring(0, 2);
                locales[1] = locale;
            } else if (locale.length > 0) {
                locales[0] = locale;
                locales.length = 1;
            } else {
                locales.length = 0;
            }
        },

        read: function(lassoContext, callback) {
            var self = this;
            var out = lassoContext.deferredStream(function() {
                var i18nContext = self.i18nContext;
                var localeContext = {
                    beforeCode: [],
                    afterCode: [],
                    attributes: {},
                    locale: self.locale,
                    localeModuleName: '/i18n/' + (util.normalizeLocaleCode(self.locale) || '_')
                };

                var work = [];
                var names = i18nContext.getDictionaryNames();

                for (var i = 0; i < names.length; i++) {
                    var info = i18nContext.getDictionaryInfoByName(names[i]);
                    work.push(createReadDictionaryTask(config, info, self, localeContext, out));
                }

                logger.info('Compiling dictionaries for locale "' + self.locale + '"...');

                parallel(work, function(err, results) {
                    if (err) {
                        logger.error('Error reading dictionaries for locale "' + self.locale + '"', err);
                    } else {
                        out.push('(function(){\n');

                        var i;

                        for (i = 0; i < localeContext.beforeCode.length; i++) {
                            out.push(localeContext.beforeCode[i]);
                            out.push('\n');
                        }

                        for (i = 0; i < results.length; i++) {
                            out.push(results[i]);
                        }

                        for (i = 0; i < localeContext.afterCode.length; i++) {
                            out.push(localeContext.afterCode[i]);
                            out.push('\n');
                        }

                        out.push('})();\n');
                    }

                    logger.info('Done compiling dictionaries for locale "' + self.locale + '"');

                    out.push(null);
                });
            }, {
                encoding: 'utf8'
            });

            return out;
        },

        calculateKey: function() {
            return this.defaultBundleName;
        },

        toString: function() {
            return this.defaultBundleName;
        }
    };
};
<commit_msg>Use getDefaultBundleName to make sure unique bundle name is calculated<commit_after>var logger = require('raptor-logging').logger(module);
var transport = require('raptor-modules/transport');
var parallel = require('raptor-async/parallel');
var nodePath = require('path');
var extend = require('raptor-util/extend');
var markoCompiler = require('marko/compiler');
var extend = require('raptor-util/extend');
var util = require('../util');

var types = {
    'marko': function(config, callback) {
        var value = this.value;

        if (Array.isArray(value)) {
            value = value.join('\n');
        }

        var dirname = nodePath.dirname(this.dictionaryInfo.absolutePath);
        var templatePath = nodePath.join(dirname, this.key + '.marko');
        var _this = this;

        var moduleName = this.dictionaryModuleName + '/' + this.key + '.marko';

        markoCompiler.compile(value, templatePath, function(err, code) {
            if (err) {
                return callback(err);
            }

            _this.beforeCode.push(transport.defineCode.sync(moduleName, code, {
                modulesRuntimeGlobal: config.modulesRuntimeGlobal
            }));

            var out = '';
            out += 'function(data) {\n';
            out += '        return require("marko").load(module.id + ' + JSON.stringify('/' + _this.key + '.marko') + ').renderSync(data);\n';
            out += '    }';
            callback(null, out);
        });
    }
};

function writeDictionary(config, dictionary, info, localeContext, callback) {

    var code = [];

    function createCompileTask(compiler, compilerContext, theLast, index, length) {
        return function(callback) {
            compiler.call(compilerContext, config, function(err, compiledCode) {
                if (err) {
                    return callback(err);
                }
                code.push('    ' + JSON.stringify(compilerContext.key) + ': ' + compiledCode);
                callback();
            });
        };
    }

    var work = [];

    var keys = Object.keys(dictionary);
    for (var i = 0; i < keys.length; i++) {
        var key = keys[i];
        var last = (i === keys.length - 1);

        if (dictionary.hasOwnProperty(key)) {
            var value = dictionary[key];
            var compiler = null;

            var pos = key.indexOf(':');
            if (pos !== -1) {
                var type = key.substring(0, pos).trim().toLowerCase();
                compiler = types[type];
                if (compiler) {
                    key = key.substring(pos + 1).trim();
                }
            }

            if (compiler) {

                var compilerContext = {
                    value: value,
                    key: key,
                    dictionaryInfo: info,
                    beforeCode: localeContext.beforeCode,
                    afterCode: localeContext.afterCode,
                    attributes: localeContext.attributes,
                    locale: localeContext.locale,
                    dictionaryModuleName: localeContext.localeModuleName + '/' + info.name
                };

                work.push(createCompileTask(compiler, compilerContext, last, i, keys.length));
            } else {
                code.push('    ' + JSON.stringify(key) + ': ' + JSON.stringify(value));
            }
        }
    }

    parallel(work, function(err) {
        callback(err, code.join(',\n'));
    });
}

function createReadDictionaryTask(config, info, dependency, localeContext, out) {
    return function(callback) {
        var work = new Array(dependency.locales.length + 1);

        work[0] = function(callback) {
            dependency.i18nContext.readRawDictionary(info.absolutePath, callback);
        };

        dependency.locales.forEach(function(locale, index) {
            work[index + 1] = function(callback) {
                var path = nodePath.join(info.localizedDir, locale + '.i18n.json');
                dependency.i18nContext.readRawDictionary(path, function(err, dict) {
                    if (err || !dict) {
                        return callback(err);
                    }

                    callback(null, dict[info.relativePath]);
                });
            };
        });

        parallel(work, function(err, result) {
            if (err) {
                return callback(err);
            }

            var merged = (result.length) ? extend({}, result[0]) : {};
            for (var i = 1; i < result.length; i++) {
                var raw = result[i];
                if (raw) {
                    extend(merged, raw);
                }
            }

            writeDictionary(config, merged, info, localeContext, function(err, dictionaryCode) {
                if (err) {
                    return callback(err);
                }

                var code = '\nmodule.exports = {\n';

                code += dictionaryCode;

                code += '\n}';

                var moduleName = localeContext.localeModuleName + '/' + info.name;

                callback(null, transport.defineCode.sync(moduleName, code, {
                    modulesRuntimeGlobal: config.modulesRuntimeGlobal
                }) + '\n');
            });

        });
    };
}

exports.create = function(config) {
    return {
        properties: {
            locale: 'string',
            i18nContext: 'object'
        },

        init: function() {

            var locale = util.normalizeLocaleCode(this.locale);

            this.name = 'i18n-' + locale;

            var locales = this.locales = new Array(2);

            if (this.locale.charAt(2) === '-') {
                locales[0] = locale.substring(0, 2);
                locales[1] = locale;
            } else if (locale.length > 0) {
                locales[0] = locale;
                locales.length = 1;
            } else {
                locales.length = 0;
            }
        },

        read: function(lassoContext, callback) {
            var self = this;
            var out = lassoContext.deferredStream(function() {
                var i18nContext = self.i18nContext;
                var localeContext = {
                    beforeCode: [],
                    afterCode: [],
                    attributes: {},
                    locale: self.locale,
                    localeModuleName: '/i18n/' + (util.normalizeLocaleCode(self.locale) || '_')
                };

                var work = [];
                var names = i18nContext.getDictionaryNames();

                for (var i = 0; i < names.length; i++) {
                    var info = i18nContext.getDictionaryInfoByName(names[i]);
                    work.push(createReadDictionaryTask(config, info, self, localeContext, out));
                }

                logger.info('Compiling dictionaries for locale "' + self.locale + '"...');

                parallel(work, function(err, results) {
                    if (err) {
                        logger.error('Error reading dictionaries for locale "' + self.locale + '"', err);
                    } else {
                        out.push('(function(){\n');

                        var i;

                        for (i = 0; i < localeContext.beforeCode.length; i++) {
                            out.push(localeContext.beforeCode[i]);
                            out.push('\n');
                        }

                        for (i = 0; i < results.length; i++) {
                            out.push(results[i]);
                        }

                        for (i = 0; i < localeContext.afterCode.length; i++) {
                            out.push(localeContext.afterCode[i]);
                            out.push('\n');
                        }

                        out.push('})();\n');
                    }

                    logger.info('Done compiling dictionaries for locale "' + self.locale + '"');

                    out.push(null);
                });
            }, {
                encoding: 'utf8'
            });

            return out;
        },

        calculateKey: function() {
            return this.name;
        },

        toString: function() {
            return this.name;
        },

        getDefaultBundleName: function(pageBundleName, lassoContext) {
            return this.name + '-' + pageBundleName;
        }
    };
};
<|endoftext|>


<commit_before>define([], function() {
    var DevicePreviewFacade = function(top_node, devices, excludes){

        var that = this;
        var base_url = "/js/vendors/go-device-preview/img/";

        that.default_excludes = [
            ".device",
            "#stryke-db",
            "#qunit",
            "#qunit-fixture",
            "script"
        ];

        that.top_node = $(top_node).length==0?$("body"):$(top_node).first();
        that.device = "";
        that.mode = "landscape";
        that.excludes = [];
        that.excludes = merge(that.default_excludes, excludes);

        function more_param(loc, param_to_update) {
            loc = loc.replace(param_to_update + "&", "");

            if (loc.indexOf("?") == -1) {
                loc += "?" + param_to_update + "";
            } else {
                loc += "&" + param_to_update + "";
            }
            return loc;
        }
        function less_param(loc, param_to_update) {
            var params = param_to_update.split("=");

            if( params[1] == "" ){
                var pattern = ".*[&?]"+params[0]+"=([^&]*)"
                pattern = new RegExp(pattern,"i");
                var matches = loc.match(pattern)
                if ( matches ) {
                    param_to_update = params[0]+"="+matches[1];
                } else {
                    param_to_update = "";
                }
            }

            if( param_to_update != "" ){
                loc = loc.replace(param_to_update + "&", "");
                loc = loc.replace(param_to_update + "", "");

                if (loc.slice(loc.length - 1) == "&")
                    loc = loc.slice(0, loc.length - 1);
                else if (loc.slice(loc.length - 1) == "?")
                    loc = loc.slice(0, loc.length - 1);
            }
            return loc;
        }

        that.wrap_nodes = function(top_node){
            if( $(top_node).children(".device-preview-wrap").length == 0 ){
                var nodes = $("body").children();
                for(var n in that.excludes ){
                    nodes = nodes.not( that.excludes[n] );
                }
                var location = window.location.href;
                location = less_param(location,"device=")
                location = less_param(location,"device_mode=")
                location = less_param(location,"no_dashboard=")
                location = more_param(location,"no_dashboard=true")

                $("style, link").remove();
                $("<link rel='stylesheet' type='text/css' href='/js/vendors/go-device-preview/device-preview.css' />

")
                    .appendTo(top_node);
                nodes.remove();
            }
        }
        that.unwrap_nodes = function(top_node){
        }
        that.DoFlip = function(){
            $('.device-screen').css("overflow-y", "");
            $('.device-screen').css( "width", "" );
            $('.device-screen').data("w", null);
            $(".device").removeClass(that.mode);
            that.mode = that.mode=="portrait"?"landscape":"portrait";
            that.EnableDevice(that.device)
        }
        that.EnableDeviceMode = function(mode){
            $('.device-screen').css("overflow-y", "");
            $('.device-screen').css( "width", "" );
            $('.device-screen').data("w", null);
            if( mode != that.mode && (mode == "portrait" || mode == "landscape") ){
                $(".device").removeClass(that.mode);
                that.mode = mode;
                that.EnableDevice(that.device)
            }else if (mode==""){
                that.DisableDevice()
            }
        }
        that.EnableDevice = function(device){

            $("html").removeClass("device-enabled");
            $(".device").removeClass(that.device)
            $(".device").removeClass(that.mode);
            that.device = device;
            if( device == "" ) return ;
            var top_node = that.top_node;
            that.wrap_nodes( top_node );

            $("html").removeClass("device-disabled");
            $("html").addClass("device-enabled");
            $(".device").addClass(that.device)
            $(".device").addClass(that.mode);

            $(".device-decoration").show();
        }
        that.DisableDevice = function(){
            $('.device-screen').css("overflow-y", "");
            $('.device-screen').css( "width", "" );
            $('.device-screen').data("w", null);
            $(".device").removeClass(that.device)
            $(".device").removeClass(that.mode);
            $("html").removeClass("device-enabled");
            $("html").addClass("device-disabled");
            if( that.device == "" ) return ;
            that.device = "";
        }

        function merge(arr,arr1){
            var retour = []
            for(var n in arr){
                if( in_array(retour,arr[n]) == false ){
                    retour.push(arr[n])
                }
            }
            for(var n in arr1){
                if( in_array(retour,arr1[n]) == false ){
                    retour.push(arr1[n])
                }
            }
            return retour;
        }
        function in_array(arr,v){
            for(var n in arr){
                if( arr[n] == v ){
                    return true;
                }
            }
            return false;
        }
    }

    return DevicePreviewFacade;
});
<commit_msg>added some comments (....)<commit_after>define([], function() {
    var DevicePreviewFacade = function(top_node, devices, excludes){

        var that = this;
        var base_url = "/js/vendors/go-device-preview/img/";

        that.default_excludes = [
            ".device",
            "#stryke-db",
            "#qunit",
            "#qunit-fixture",
            "script"
        ];

        that.top_node = $(top_node).length==0?$("body"):$(top_node).first();
        that.device = "";
        that.mode = "landscape";
        that.excludes = [];
        that.excludes = merge(that.default_excludes, excludes);

        function more_param(loc, param_to_update) {
            loc = loc.replace(param_to_update + "&", "");

            if (loc.indexOf("?") == -1) {
                loc += "?" + param_to_update + "";
            } else {
                loc += "&" + param_to_update + "";
            }
            return loc;
        }
        function less_param(loc, param_to_update) {
            var params = param_to_update.split("=");

            if( params[1] == "" ){
                var pattern = ".*[&?]"+params[0]+"=([^&]*)"
                pattern = new RegExp(pattern,"i");
                var matches = loc.match(pattern)
                if ( matches ) {
                    param_to_update = params[0]+"="+matches[1];
                } else {
                    param_to_update = "";
                }
            }

            if( param_to_update != "" ){
                loc = loc.replace(param_to_update + "&", "");
                loc = loc.replace(param_to_update + "", "");

                if (loc.slice(loc.length - 1) == "&")
                    loc = loc.slice(0, loc.length - 1);
                else if (loc.slice(loc.length - 1) == "?")
                    loc = loc.slice(0, loc.length - 1);
            }
            return loc;
        }

        that.wrap_nodes = function(top_node){
            if( $(top_node).children(".device-preview-wrap").length == 0 ){
                var nodes = $("body").children();
                for(var n in that.excludes ){
                    nodes = nodes.not( that.excludes[n] );
                }
                nodes.remove();

                var location = window.location.href;
                location = less_param(location,"device=")
                location = less_param(location,"device_mode=")
                location = less_param(location,"no_dashboard=")
                location = more_param(location,"no_dashboard=true")
                $("style, link").remove();
                $("<link rel='stylesheet' type='text/css' href='/js/vendors/go-device-preview/device-preview.css' />

")
                    .appendTo(top_node);
            }
        }
        that.unwrap_nodes = function(top_node){
        }
        that.DoFlip = function(){
            $('.device-screen').css("overflow-y", "");
            $('.device-screen').css( "width", "" );
            $('.device-screen').data("w", null);
            $(".device").removeClass(that.mode);
            that.mode = that.mode=="portrait"?"landscape":"portrait";
            that.EnableDevice(that.device)
        }
        that.EnableDeviceMode = function(mode){
            $('.device-screen').css("overflow-y", "");
            $('.device-screen').css( "width", "" );
            $('.device-screen').data("w", null);
            if( mode != that.mode && (mode == "portrait" || mode == "landscape") ){
                $(".device").removeClass(that.mode);
                that.mode = mode;
                that.EnableDevice(that.device)
            }else if (mode==""){
                that.DisableDevice()
            }
        }
        that.EnableDevice = function(device){

            $("html").removeClass("device-enabled");
            $(".device").removeClass(that.device)
            $(".device").removeClass(that.mode);
            that.device = device;
            if( device == "" ) return ;
            var top_node = that.top_node;
            that.wrap_nodes( top_node );

            $("html").removeClass("device-disabled");
            $("html").addClass("device-enabled");
            $(".device").addClass(that.device)
            $(".device").addClass(that.mode);

            $(".device-decoration").show();
        }
        that.DisableDevice = function(){
            $('.device-screen').css("overflow-y", "");
            $('.device-screen').css( "width", "" );
            $('.device-screen').data("w", null);
            $(".device").removeClass(that.device)
            $(".device").removeClass(that.mode);
            $("html").removeClass("device-enabled");
            $("html").addClass("device-disabled");
            if( that.device == "" ) return ;
            that.device = "";
        }

        function merge(arr,arr1){
            var retour = []
            for(var n in arr){
                if( in_array(retour,arr[n]) == false ){
                    retour.push(arr[n])
                }
            }
            for(var n in arr1){
                if( in_array(retour,arr1[n]) == false ){
                    retour.push(arr1[n])
                }
            }
            return retour;
        }
        function in_array(arr,v){
            for(var n in arr){
                if( arr[n] == v ){
                    return true;
                }
            }
            return false;
        }
    }

    return DevicePreviewFacade;
});
<|endoftext|>


<commit_before>define('Mobile/SalesLogix/Views/Owner/List', [
    'dojo/_base/declare',
    'dojo/string',
    'dojo/_base/array',
    'Sage/Platform/Mobile/List'
], function(
    declare,
    string,
    array,
    List
) {

    return declare('Mobile.SalesLogix.Views.Owner.List', [List], {
        //Templates
        itemTemplate: new Simplate([
            '
{%: $.OwnerDescription %}
'
        ]),

        //Localization
        titleText: 'Owners',

        //View Properties
        id: 'owner_list',
        security: 'Entities/Owner/View',
        queryOrderBy: 'OwnerDescription',
        querySelect: [
            'OwnerDescription',
            'User/Enabled',
            'User/Type'
        ],
        resourceKind: 'owners',

        formatSearchQuery: function(searchQuery) {
            return string.substitute('upper(OwnerDescription) like "%${0}%"', [this.escapeSearchQuery(searchQuery.toUpperCase())]);
        },
        processFeed: function(feed) {
            // Filter the feed in memory (SData cannot check if owner.User is null)
            if (feed.$resources) {
                feed.$resources = array.filter(feed.$resources, function(item) {
                    return this._userEnabled(item) && this._isCorrectType(item);
                }, this);
            }

            this.inherited(arguments);
        },
        _userEnabled: function(item) {
            // If User is null, it is probably a team
            if (item.User === null || item.User.Enabled) {
                return true;
            }

            return false;
        },
        _isCorrectType: function(item) {
            // If user is null, it is probably a team
            if (item.User === null) {
                return true;
            }

            // Filter out WebViewer, Retired, Template and AddOn users like the user list does
            return item.User.Type !== 3 && item.User.Type !== 5 && item.User.Type !== 6 && item.User.Type !== 7;
        }
    });
});

<commit_msg>Removing departments to match LAN and the 7.5.4 web client. Waiting for feedback on why the 8.0 web client has departments in the lookup.<commit_after>define('Mobile/SalesLogix/Views/Owner/List', [
    'dojo/_base/declare',
    'dojo/string',
    'dojo/_base/array',
    'Sage/Platform/Mobile/List'
], function(
    declare,
    string,
    array,
    List
) {

    return declare('Mobile.SalesLogix.Views.Owner.List', [List], {
        //Templates
        itemTemplate: new Simplate([
            '
{%: $.OwnerDescription %}
'
        ]),

        //Localization
        titleText: 'Owners',

        //View Properties
        id: 'owner_list',
        security: 'Entities/Owner/View',
        queryOrderBy: 'OwnerDescription',
        querySelect: [
            'OwnerDescription',
            'User/Enabled',
            'User/Type',
            'Type'
        ],
        queryWhere: 'Type ne "Department"',
        resourceKind: 'owners',

        formatSearchQuery: function(searchQuery) {
            return string.substitute('upper(OwnerDescription) like "%${0}%"', [this.escapeSearchQuery(searchQuery.toUpperCase())]);
        },
        processFeed: function(feed) {
            // Filter the feed in memory (SData cannot check if owner.User is null)
            if (feed.$resources) {
                feed.$resources = array.filter(feed.$resources, function(item) {
                    return this._userEnabled(item) && this._isCorrectType(item);
                }, this);
            }

            this.inherited(arguments);
        },
        _userEnabled: function(item) {
            // If User is null, it is probably a team
            if (item.User === null || item.User.Enabled) {
                return true;
            }

            return false;
        },
        _isCorrectType: function(item) {
            // If user is null, it is probably a team
            if (item.User === null) {
                return true;
            }

            // Filter out WebViewer, Retired, Template and AddOn users like the user list does
            return item.User.Type !== 3 && item.User.Type !== 5 && item.User.Type !== 6 && item.User.Type !== 7;
        }
    });
});

<|endoftext|>


<commit_before>const cp = require('child_process')

const actionEmitter = require('../action-emitter')
const processDoorLib = require('./process-door')

const lc = require('../leds/led-control')

let ledAddresses

module.exports = { run }

function unauthorizedSequence () {
  if (!processDoorLib.isDoorSecured()) return
  return lc.timedPulse(ledAddresses.doorLeds, lc.COLORS.red, 1000)
}

function doorSecured () {
  return lc.lightDown()
}

function doorNotSecured () {
  return solidUp(ledAddresses.doorLeds, lc.COLORS.amazonite)
}

function processFob (event) {
  if (!processDoorLib.isDoorSecured()) return

  switch (event.action) {
    case 'unauthorized':
      return unauthorizedSequence()
    case 'registered':
      return lc.timed({range: ledAddresses.doorLeds, color: lc.COLORS.orange, type: 'pulse'}, 1000)
  }
}

function processDoor (event) {
  switch (event.action) {
    case 'doorSecured':
      return doorSecured()
    case 'doorNotSecured':
      return doorNotSecured()
  }
}

function processDoorManager (event) {
  switch (event.action) {
    case 'doorSequenceOn':
      return pulseUp(ledAddresses.doorLeds, lc.COLORS.orange)
    case 'doorSequenceOff':
      return lc.lightDown()
  }
}

function pulseUp (range, color) {
  return lc.lightUp({range, color, type: 'pulse'})
}

function solidUp (range, color) {
  return lc.lightUp({range, color, type: 'solid'})
}

function processBrain (event) {
  switch (event.action) {
    case 'billValidatorPending':
      return pulseUp(ledAddresses.validatorLeds, lc.COLORS.amazonite)
    case 'billValidatorAccepting':
      return pulseUp(ledAddresses.validatorLeds, lc.COLORS.orange)
    case 'billValidatorRejecting':
      return pulseUp(ledAddresses.validatorLeds, lc.COLORS.red)

    case 'billDispenserDispensing':
      return solidUp(ledAddresses.dispenserLeds, lc.COLORS.amazonite)
    case 'billDispenserDispensed':
      return pulseUp(ledAddresses.dispenserLeds, lc.COLORS.orange)

    case 'scanBayLightOn':
      return solidUp(ledAddresses.scanBayLeds, lc.COLORS.dimmed)

    case 'billValidatorOff':
    case 'billDispenserCollected':
    case 'scanBayLightOff':
    case 'ledsOff':
    case 'forceOff':
      return lc.lightDown()
  }
}

function killAll () {
  console.log('LED: kill any hangovers')
  cp.execFile('/usr/bin/killall', ['leds'], () => {})
}

function processDispenseGenerator (event) {
  switch (event.action) {
    case 'billDispenserDispensed':
      return pulseUp(ledAddresses.dispenserLeds, lc.COLORS.orange)
    default:
      break
  }
}

function run (leds) {
  console.log('DEBUG100')
  ledAddresses = _.isNil(leds) ? require('./led-addresses') : leds
  killAll()
  actionEmitter.on('fob', processFob)
  actionEmitter.on('door', processDoor)
  actionEmitter.on('doorManager', processDoorManager)
  actionEmitter.on('brain', processBrain)
  actionEmitter.on('dispenseGenerator', processDispenseGenerator)
  return Promise.resolve()
}
<commit_msg>fix: remove dynamic import<commit_after>const cp = require('child_process')

const actionEmitter = require('../action-emitter')
const processDoorLib = require('./process-door')

const lc = require('../leds/led-control')
const LEDS = require('./led-addresses')

let ledAddresses

module.exports = { run }

function unauthorizedSequence () {
  if (!processDoorLib.isDoorSecured()) return
  return lc.timedPulse(ledAddresses.doorLeds, lc.COLORS.red, 1000)
}

function doorSecured () {
  return lc.lightDown()
}

function doorNotSecured () {
  return solidUp(ledAddresses.doorLeds, lc.COLORS.amazonite)
}

function processFob (event) {
  if (!processDoorLib.isDoorSecured()) return

  switch (event.action) {
    case 'unauthorized':
      return unauthorizedSequence()
    case 'registered':
      return lc.timed({range: ledAddresses.doorLeds, color: lc.COLORS.orange, type: 'pulse'}, 1000)
  }
}

function processDoor (event) {
  switch (event.action) {
    case 'doorSecured':
      return doorSecured()
    case 'doorNotSecured':
      return doorNotSecured()
  }
}

function processDoorManager (event) {
  switch (event.action) {
    case 'doorSequenceOn':
      return pulseUp(ledAddresses.doorLeds, lc.COLORS.orange)
    case 'doorSequenceOff':
      return lc.lightDown()
  }
}

function pulseUp (range, color) {
  return lc.lightUp({range, color, type: 'pulse'})
}

function solidUp (range, color) {
  return lc.lightUp({range, color, type: 'solid'})
}

function processBrain (event) {
  switch (event.action) {
    case 'billValidatorPending':
      return pulseUp(ledAddresses.validatorLeds, lc.COLORS.amazonite)
    case 'billValidatorAccepting':
      return pulseUp(ledAddresses.validatorLeds, lc.COLORS.orange)
    case 'billValidatorRejecting':
      return pulseUp(ledAddresses.validatorLeds, lc.COLORS.red)

    case 'billDispenserDispensing':
      return solidUp(ledAddresses.dispenserLeds, lc.COLORS.amazonite)
    case 'billDispenserDispensed':
      return pulseUp(ledAddresses.dispenserLeds, lc.COLORS.orange)

    case 'scanBayLightOn':
      return solidUp(ledAddresses.scanBayLeds, lc.COLORS.dimmed)

    case 'billValidatorOff':
    case 'billDispenserCollected':
    case 'scanBayLightOff':
    case 'ledsOff':
    case 'forceOff':
      return lc.lightDown()
  }
}

function killAll () {
  console.log('LED: kill any hangovers')
  cp.execFile('/usr/bin/killall', ['leds'], () => {})
}

function processDispenseGenerator (event) {
  switch (event.action) {
    case 'billDispenserDispensed':
      return pulseUp(ledAddresses.dispenserLeds, lc.COLORS.orange)
    default:
      break
  }
}

function run (ledAddrs) {
  console.log('DEBUG100')
  ledAddresses = _.isNil(ledAddrs) ? LEDS : ledAddrs
  killAll()
  actionEmitter.on('fob', processFob)
  actionEmitter.on('door', processDoor)
  actionEmitter.on('doorManager', processDoorManager)
  actionEmitter.on('brain', processBrain)
  actionEmitter.on('dispenseGenerator', processDispenseGenerator)
  return Promise.resolve()
}
<|endoftext|>


<commit_before>'use strict';

var rest   = require('../src/rest-client');
var assert = chai.assert;

require('./shim-function.bind');

describe('Client', function () {
    var client = rest('http://localhost:8000');

    it('performs a basic get operation', function (ok) {
        client.get('', function(req) {
            assert.equal(req.response, 'test');
            ok();
        });
    });
});
<commit_msg>Testing 'get' functionality<commit_after>'use strict';

var rest   = require('../src/rest-client');
var assert = chai.assert;

require('./shim-function.bind');

describe('Client', function () {

    describe('get', function() {
        it('Performs a basic get operation', function (ok) {
            var client = rest('http://localhost:8000');
            client.get(function(req) {
                ok();
        });
        it('Calls error middleware', function(ok) {
            var message = 'some error';

                assert.equal(err.status, 404);
                ok();
            });
        });
    });

        });
    });
});
<|endoftext|>


<commit_before>
module.exports = function(grunt) {

  grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'),
    docco: {
      debug: {
        src: [
          'lib/Phantomizer.js',
          'lib/router.js',
          'lib/webserver.js'
        ],
        options: {
          layout:'linear',
          output: 'documentation/'
        }
      }
    },
    'gh-pages': {
      options: {
        base: '.',
        add: true
      },
      src: ['documentation/**']
    },
    release: {
      options: {
        bump: true,
        add: false,
        commit: false,
        npm: false,
        npmtag: true,
        tagName: '<%= version %>',
        github: {
          repo: 'maboiteaspam/phantomizer-libutil',
          usernameVar: 'GITHUB_USERNAME',
          passwordVar: 'GITHUB_PASSWORD'
        }
      }
    }
  });
  grunt.loadNpmTasks('grunt-docco');
  grunt.loadNpmTasks('grunt-gh-pages');
  grunt.loadNpmTasks('grunt-release');
  grunt.registerTask('cleanup-grunt-temp', [],function(){
    var wrench = require('wrench');
    wrench.rmdirSyncRecursive(__dirname + '/.grunt', !true);
    wrench.rmdirSyncRecursive(__dirname + '/documentation', !true);
  });

  // to generate and publish the docco style documentation
  // execute this
  // grunt
  grunt.registerTask('default', ['docco','gh-pages', 'cleanup-grunt-temp']);

  // to release the project in a new version
  // use one of those commands
  // grunt --no-write -v release # test only
  // grunt release:patch
  // grunt release:minor
  // grunt release:major
  // grunt release:prerelease

};
<commit_msg>Release support<commit_after>
module.exports = function(grunt) {

  grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'),
    docco: {
      debug: {
        src: [
          'lib/Phantomizer.js',
          'lib/router.js',
          'lib/webserver.js'
        ],
        options: {
          layout:'linear',
          output: 'documentation/'
        }
      }
    },
    'gh-pages': {
      options: {
        base: '.',
        add: true
      },
      src: ['documentation/**']
    },
    release: {
      options: {
        npm: false, //default: true
        // true will apply the version number as the tag
        npmtag: true, //default: no tag
        tagName: '<%= version %>', //default: '<%= version %>'
        github: {
          repo: 'maboiteaspam/phantomizer-libutil',
          usernameVar: 'GITHUB_USERNAME',
          passwordVar: 'GITHUB_PASSWORD'
        }
      }
    }
  });
  grunt.loadNpmTasks('grunt-docco');
  grunt.loadNpmTasks('grunt-gh-pages');
  grunt.loadNpmTasks('grunt-release');
  grunt.registerTask('cleanup-grunt-temp', [],function(){
    var wrench = require('wrench');
    wrench.rmdirSyncRecursive(__dirname + '/.grunt', !true);
    wrench.rmdirSyncRecursive(__dirname + '/documentation', !true);
  });

  // to generate and publish the docco style documentation
  // execute this
  // grunt
  grunt.registerTask('default', ['docco','gh-pages', 'cleanup-grunt-temp']);

  // to release the project in a new version
  // use one of those commands
  // grunt --no-write -v release # test only
  // grunt release:patch
  // grunt release:minor
  // grunt release:major
  // grunt release:prerelease

};
<|endoftext|>


<commit_before>module('Uploading multiple files', {
  setup: function () {
    this.fieldset = $(
      '<fieldset id="image_fields" class="images multiple_file_uploads">' +
      '
' +
      '
' +
      'File' +
      '<input id="edition_images_attributes_0_image_data_attributes_file" name="edition[images_attributes][0][image_data_attributes][file]" type="file" class="js-upload-image-input" />' +
      '<input id="edition_images_attributes_0_image_data_attributes_file_cache" name="edition[images_attributes][0][image_data_attributes][file_cache]" type="hidden" />' +
      '
' +
      '
' +
      'Alt text' +
      '<input id="edition_images_attributes_0_alt_text" name="edition[images_attributes][0][alt_text]" type="text" />' +
      '
' +
      '
' +
      'Caption' +
      '<textarea id="edition_images_attributes_0_caption" name="edition[images_attributes][0][caption]"></textarea>' +
      '
' +
      '
' +
      '</fieldset>')

    $('#qunit-fixture').append(this.fieldset)
    this.first_file_input = $('#edition_images_attributes_0_image_data_attributes_file')
    this.fieldset.enableMultipleFileUploads()
  }
})

var fireClickEventOnLastFileInputOf = function (fieldset) {
  fieldset.find('input[type=file]:last').click()
}

test('should add a new file input when a file is selected', function () {
  this.first_file_input.click()
  equal(this.fieldset.children('.file_upload').length, 2)
})

test('should not add a new file input when a selected file is clicked', function () {
  this.first_file_input.click()
  this.first_file_input.click()
  equal(this.fieldset.children('.file_upload').length, 2)
})

test('should continue adding new inputs as new files are selected', function () {
  for (var i = 0; i < 10; i++) {
    fireClickEventOnLastFileInputOf(this.fieldset)
  }
  equal(this.fieldset.children('.file_upload').length, 11)

  fireClickEventOnLastFileInputOf(this.fieldset)
  equal(this.fieldset.children('.file_upload').length, 12)
})

test('should increment the ID and name of the alt text input for each set of new inputs added', function () {
  fireClickEventOnLastFileInputOf(this.fieldset)
  ok($('#edition_images_attributes_1_alt_text')[0], 'input with id not found')
  ok($("input[name='edition[images_attributes][1][alt_text]']")[0], 'input with name not found')

  fireClickEventOnLastFileInputOf(this.fieldset)
  ok($('#edition_images_attributes_2_alt_text')[0], 'input with id not found')
  ok($("input[name='edition[images_attributes][2][alt_text]']")[0], 'input with name not found')
})

test('should increment the ID and name of the caption textarea for each set of new inputs added', function () {
  fireClickEventOnLastFileInputOf(this.fieldset)
  ok($('#edition_images_attributes_1_caption')[0], 'textarea with id not found')
  ok($("textarea[name='edition[images_attributes][1][caption]']")[0], 'textarea with name not found')

  fireClickEventOnLastFileInputOf(this.fieldset)
  ok($('#edition_images_attributes_2_caption')[0], 'textarea with id not found')
  ok($("textarea[name='edition[images_attributes][2][caption]']")[0], 'textarea with name not found')
})

test('should increment the referenced ID of the file label for each set of new inputs added', function () {
  fireClickEventOnLastFileInputOf(this.fieldset)
  var latestInput = this.fieldset.find("label:contains('File'):last")
  equal(latestInput.attr('for'), 'edition_images_attributes_1_image_data_attributes_file')

  fireClickEventOnLastFileInputOf(this.fieldset)
  latestInput = this.fieldset.find("label:contains('File'):last")
  equal(latestInput.attr('for'), 'edition_images_attributes_2_image_data_attributes_file')
})

test('should increment the ID and name of the file input for each set of new inputs added', function () {
  fireClickEventOnLastFileInputOf(this.fieldset)
  var latestInput = this.fieldset.find('input[type=file]:last')[0]
  equal(latestInput.id, 'edition_images_attributes_1_image_data_attributes_file')
  equal(latestInput.name, 'edition[images_attributes][1][image_data_attributes][file]')

  fireClickEventOnLastFileInputOf(this.fieldset)
  latestInput = this.fieldset.find('input[type=file]:last')[0]
  equal(latestInput.id, 'edition_images_attributes_2_image_data_attributes_file')
  equal(latestInput.name, 'edition[images_attributes][2][image_data_attributes][file]')
})

test('should increment the ID and name of the hidden cache input for each set of new inputs added', function () {
  fireClickEventOnLastFileInputOf(this.fieldset)
  var latestInput = this.fieldset.find('input[type=hidden]:last')[0]
  equal(latestInput.id, 'edition_images_attributes_1_image_data_attributes_file_cache')
  equal(latestInput.name, 'edition[images_attributes][1][image_data_attributes][file_cache]')

  fireClickEventOnLastFileInputOf(this.fieldset)
  latestInput = this.fieldset.find('input[type=hidden]:last')[0]
  equal(latestInput.id, 'edition_images_attributes_2_image_data_attributes_file_cache')
  equal(latestInput.name, 'edition[images_attributes][2][image_data_attributes][file_cache]')
})

test('should make the value of the text input blank for each set of new inputs added', function () {
  this.fieldset.find('input[type=text]:last').val('not-blank')
  fireClickEventOnLastFileInputOf(this.fieldset)
  var latestInput = this.fieldset.find('input[type=text]:last')
  equal(latestInput.val(), '')
})

test('should make the value of the caption textarea blank for each set of new inputs added', function () {
  this.fieldset.find('#edition_images_attributes_0_caption').append('not-blank')
  fireClickEventOnLastFileInputOf(this.fieldset)
  equal($('#edition_images_attributes_1_caption').val(), '')
})

test('should set the value of the hidden cache input to blank for each new input added', function () {
  $('input[type=hidden]:last').val('not-blank')
  fireClickEventOnLastFileInputOf(this.fieldset)
  var latestInput = this.fieldset.find('input[type=hidden]:last')
  equal(latestInput.val(), '')
})

test('should set the text of the already_uploaded element to blank for each new input added', function () {
  var alreadyUploaded = $('some-file.pdf already uploaded')
  $('input[type=file]:last').after(alreadyUploaded)
  fireClickEventOnLastFileInputOf(this.fieldset)
  equal(this.fieldset.find('.already_uploaded').length, 2)
  equal(this.fieldset.find('.already_uploaded:last').text(), '')
})

module('Uploading multiple files after file field validation error', {
  setup: function () {
    this.fieldset = $(
      '<fieldset id="image_fields" class="images multiple_file_uploads">' +
      '
' +
      '
' +
      'File' +
      '
' +
      '
' +
      '<input id="edition_images_attributes_0_image_data_attributes_file" name="edition[images_attributes][0][image_data_attributes][file]" type="file" class="js-upload-image-input" />' +
      '
' +
      '<input id="edition_images_attributes_0_image_data_attributes_file_cache" name="edition[images_attributes][0][image_data_attributes][file_cache]" type="hidden" />' +
      '
' +
      '</fieldset>')
    $('#qunit-fixture').append(this.fieldset)
    this.fieldset.enableMultipleFileUploads()
  }
})

test('should add a new file input when a file is selected', function () {
  fireClickEventOnLastFileInputOf(this.fieldset)
  equal(this.fieldset.children('.file_upload').length, 2)
})

test('should copy the file label without error wrapper for each set of new inputs added', function () {
  fireClickEventOnLastFileInputOf(this.fieldset)
  equal(this.fieldset.find("label:contains('File'):last").parent().hasClass('field_with_errors'), false)
  equal(this.fieldset.find(".field_with_errors label:contains('File')").length, 1)
})

test('should copy the file input without error wrapper for each set of new inputs added', function () {
  fireClickEventOnLastFileInputOf(this.fieldset)
  equal(this.fieldset.find('input[type=file]').length, 2)
  equal(this.fieldset.find('.field_with_errors input[type=file]').length, 1)
})
<commit_msg>Port multiple_file_upload JS tests from QUnit to Jasmine<commit_after>describe('jquery.enableMultipleFileUploads', function () {
  var fieldset

  beforeEach(function () {
    fieldset = $(
      '<fieldset id="image_fields" class="images multiple_file_uploads">' +
        '
' +
          '
' +
            'File' +
            '<input id="edition_images_attributes_0_image_data_attributes_file" name="edition[images_attributes][0][image_data_attributes][file]" type="file" class="js-upload-image-input" />' +
            '<input id="edition_images_attributes_0_image_data_attributes_file_cache" name="edition[images_attributes][0][image_data_attributes][file_cache]" type="hidden" />' +
          '
' +
          '
' +
            'Alt text' +
            '<input id="edition_images_attributes_0_alt_text" name="edition[images_attributes][0][alt_text]" type="text" />' +
          '
' +
          '
' +
            'Caption' +
            '<textarea id="edition_images_attributes_0_caption" name="edition[images_attributes][0][caption]"></textarea>' +
          '
' +
        '
' +
      '</fieldset>'
    )

    $(document.body).append(fieldset)
  })

  afterEach(function () {
    fieldset.remove()
  })

  it('should add a new file input when a file is selected', function () {
    fieldset.enableMultipleFileUploads()
    fieldset.find('input[type=file]').click()
    expect(fieldset.children('.file_upload').length).toEqual(2)
  })

  it("shouldn't add extra file inputs if the same one is selected twice", function () {
    fieldset.enableMultipleFileUploads()
    var item = fieldset.find('input[type=file]')
    item.click().click()
    expect(fieldset.children('.file_upload').length).toEqual(2)
  })

  it('should continue adding new inputs as new files are selected', function () {
    fieldset.enableMultipleFileUploads()
    for (var i = 0; i < 10; i++) {
      fieldset.find('input[type=file]:last').click()
    }

    expect(fieldset.children('.file_upload').length).toEqual(11)
  })

  it('should increment the names of new inputs added', function () {
    fieldset.enableMultipleFileUploads()

    var elements = [
      'input[type=file][name="edition[images_attributes][1][image_data_attributes][file]"]',
      'input[type=hidden][name="edition[images_attributes][1][image_data_attributes][file_cache]"]',
      'input[type=text][name="edition[images_attributes][1][alt_text]"]',
      'textarea[name="edition[images_attributes][1][caption]"]'
    ]
    expect($(elements.join(',')).length).toEqual(0)

    fieldset.find('input[type=file]').click()

    expect($(elements.join(',')).length).toEqual(elements.length)
  })

  it('should reset values set on fields', function () {
    fieldset.enableMultipleFileUploads()

    fieldset.find('[name="edition[images_attributes][0][image_data_attributes][file_cache]"]').val('not-blank')
    fieldset.find('[name="edition[images_attributes][0][alt_text]"]').val('not-blank')
    fieldset.find('[name="edition[images_attributes][0][caption]"]').val('not-blank')

    fieldset.find('input[type=file]').click()

    expect(fieldset.find('[name="edition[images_attributes][1][image_data_attributes][file_cache]"]').val()).toEqual('')
    expect(fieldset.find('[name="edition[images_attributes][1][alt_text]"]').val()).toEqual('')
    expect(fieldset.find('[name="edition[images_attributes][1][caption]"]').val()).toEqual('')
  })

  it('should reset an already uploaded warning', function () {
    fieldset.enableMultipleFileUploads()

    var alreadyUploaded = $('some-file.pdf already uploaded')
    $('input[type=file]').after(alreadyUploaded)

    fieldset.find('input[type=file]').click()

    expect(fieldset.find('.already_uploaded').length).toEqual(2)
    expect(fieldset.find('.already_uploaded:last').text()).toEqual('')
  })

  describe('uploading files after a file field validation error', function () {
    beforeEach(function () {
      fieldset.remove()
      fieldset = $(
        '<fieldset id="image_fields" class="images multiple_file_uploads">' +
          '
' +
            '
' +
              'File' +
            '
' +
            '
' +
              '<input id="edition_images_attributes_0_image_data_attributes_file" name="edition[images_attributes][0][image_data_attributes][file]" type="file" class="js-upload-image-input" />' +
            '
' +
            '<input id="edition_images_attributes_0_image_data_attributes_file_cache" name="edition[images_attributes][0][image_data_attributes][file_cache]" type="hidden" />' +
        '
' +
      '</fieldset>'
      )

      $(document.body).append(fieldset)
    })

    it('should still allow creating additional file uploads', function () {
      fieldset.enableMultipleFileUploads()
      fieldset.find('input[type=file]').click()
      expect(fieldset.children('.file_upload').length).toEqual(2)
    })

    it("doesn't copy error wrappers", function () {
      fieldset.enableMultipleFileUploads()
      fieldset.find('input[type=file]').click()

      var labelParent = $('label[for=edition_images_attributes_1_image_data_attributes_file]').parent()
      var inputParent = $('input[name="edition[images_attributes][1][image_data_attributes][file]"]').parent()

      expect(labelParent.hasClass('field_with_errors')).toBeFalse()
      expect(inputParent.hasClass('field_with_errors')).toBeFalse()
    })
  })
})
<|endoftext|>