<commit_before>const Gibberish   = require( 'gibberish-dsp' )
const Ugen        = require( './ugen.js' )
const Instruments = require( './instruments.js' )
const Oscillators = require( './oscillators.js' )
const Effects     = require( './effects.js' )
const Filters     = require( './filters.js' )
const Binops      = require( './binops.js' )
const Analysis    = require( './analysis.js' )
const Envelopes   = require( './envelopes.js' )
const Busses      = require( './busses.js' )
const Ensemble    = require( './ensemble.js' )
const Utility     = require( './utility.js' )
const Freesound   = require( './freesound.js' )
const Gen         = require( './gen.js' )
const WavePattern = require( './wavePattern.js' )
const WaveObjects = require( './waveObjects.js' )
const Core        = require( 'gibber.core.lib' )
const AWPF        = require( './external/audioworklet-polyfill.js' )
//const Arp         = require( './arp.js' )

const Audio = {
  Clock: require( './clock.js' ),
  Theory: require( './theory.js' ),
  Presets: require( './presets.js' ),
  Make: require( './make.js' ),
  Core,
  initialized:false,
  autoConnect:true,
  shouldDelay:false,
  instruments:{},
  oscillators:{},
  effects:{},
  exportTarget:null,

  export( obj ) {
    if( Audio.initialized ){
      Object.assign(
        obj,
        this.instruments,
        this.oscillators,
        this.effects,
        this.filters,
        this.busses,
        this.envelopes,
        this.waveObjects,
        this.binops,
        this.analysis
      )

      Utility.export( obj )
      this.Gen.export( obj )

      obj.gen = this.Gen.make
      obj.lfo = this.Gen.composites.lfo
      obj.Ensemble = this.Ensemble
      obj.Drums = this.Drums
      obj.EDrums = this.EDrums
      obj.Theory = this.Theory
      obj.Freesound = this.Freesound
      obj.Clock = this.Clock
      obj.WavePattern = this.WavePattern
      obj.Gen = this.Gen

      obj.Out = this.Out
      obj.Make = this.Make
      obj.Gibberish = this.Gibberish
      obj.future = this.Gibberish.utilities.future
    }else{
      Audio.exportTarget = obj
    }
  },

  __defaults : {
    workletPath: '../dist/gibberish_worklet.js',
    ctx:         null,
    bufferSize:  2048
  },

  init( options, Gibber  ) {
    let { workletPath, ctx, bufferSize } = Object.assign( {}, this.__defaults, options )
    this.Gibber = Gibber
    Gibber.Audio = this
    this.Gibberish = Gibberish

    Gibberish.workletPath = workletPath

    this.createPubSub()

    const AC = typeof AudioContext === 'undefined' ? webkitAudioContext : AudioContext
    window.AudioContext = AC
    AWPF( window, bufferSize )

    const p = new Promise( (resolve, reject) => {
      if( ctx === null ) {
        ctx = new AC({ latencyHint:.05 })
        //ctx = new AudioContext()
      }

      Gibberish.init( 44100*60*10, ctx ).then( processorNode => {
        // XXX remove once gibber.core.lib has been properly integrated
        Audio.Core.Audio = Audio.Core.audio = Audio

        Audio.initialized = true
        Audio.node = processorNode
        Audio.Gen = Gen( Audio )
        Audio.Gen.init()
        //Audio.Arp = Arp( Gibber )
        Audio.Gen.export( Audio.Gen.ugens )
        Audio.Theory.init( Audio )
        Audio.Ugen = Ugen
        Audio.Utilities = Utility
        Audio.WavePattern = WavePattern( Audio )
        Audio.ctx = ctx
        Audio.Out = Gibberish.output

        // must wait for Gen to be initialized
        Audio.Clock.init( Audio.Gen, Audio )

        Audio.createUgens()

        if( Audio.exportTarget !== null ) Audio.export( Audio.exportTarget )

        Gibberish.worklet.port.__postMessage = Gibberish.worklet.port.postMessage

        Gibberish.worklet.port.postMessage = function( dict ) {
          if( Audio.shouldDelay === true ) dict.delay = true

          Gibberish.worklet.port.__postMessage( dict )
        }

        Audio.export( window )

        //const drums = Audio.Drums('x*o-')
        //drums.disconnect()
        //drums.stop()

        // store last location in memory... we can clear everything else in Gibber.clear9)
        const memIdx = Object.keys( Gibberish.memory.list ).reverse()[0]
        this.__memoryEnd = parseInt( memIdx ) + Gibberish.memory.list[ memIdx ]

        // XXX this forces the gibberish scheduler to start
        // running, but it's about as hacky as it can get...
        const __start = Audio.instruments.Synth().connect()
        __start.disconnect()

        Audio.Gibberish.genish.gen.histories.clear()

        resolve( Audio )
      })
    })

    return p
  },

  // XXX stop clock from being cleared.
  clear() {
    Gibberish.clear()
    Audio.Clock.init( Audio.Gen, Audio )

    // the idea is that we only clear memory that was filled after
    // the initial Gibber initialization... this stops objects
    // like Clock and Theory from having their memory cleared and
    // from having to re-initialize them.

    // fill memory with zeros from the end initialization block onwards
    Gibberish.memory.heap.fill( 0, this.__memoryEnd )

    // get locations of all memory blocks
    const memKeys = Object.keys( Gibberish.memory.list )

    // get idx of final initialization block
    const endIdx =  memKeys.indexOf( ''+this.__memoryEnd )

    // loop through all blocks after final initialzation block
    // and delete them in the memory list... they've already
    // been zeroed out.
    for( let i = endIdx; i < memKeys.length; i++ ) {
      delete Gibberish.memory.list[ memKeys[ i ] ]
    }

    Audio.publish('clear')
  },

  onload() {},

  createUgens() {
    //Core.export( this, this )

    this.Freesound = Freesound( this )
    this.binops = Binops.create( this )
    this.analysis = Analysis.create( this )
    this.oscillators = Oscillators.create( this )
    this.instruments = Instruments.create( this )
    this.envelopes   = Envelopes.create( this )
    this.filters     = Filters.create( this )
    this.effects = Effects.create( this )
    this.busses = Busses.create( this )
    this.Ensemble = Ensemble( this )
    this.waveObjects = WaveObjects( this )

    const Pattern = Core.__Pattern
    Pattern.transfer( this, Pattern.toString() )

    this.Make = this.Make( this )

    const drums = require( './drums.js' )( this )
    Object.assign( this, drums )
  },

  printcb() {
    Gibber.Gibberish.worklet.port.postMessage({ address:'callback' })
  },
  printobj( obj ) {
    Gibber.Gibberish.worklet.port.postMessage({ address:'print', object:obj.id })
  },
  send( msg ){
    Gibber.Gibberish.worklet.port.postMessage( msg )
  },

  createPubSub() {
    const events = {}
    this.subscribe = function( key, fcn ) {
      if( typeof events[ key ] === 'undefined' ) {
        events[ key ] = []
      }
      events[ key ].push( fcn )
    }

    this.unsubscribe = function( key, fcn ) {
      if( typeof events[ key ] !== 'undefined' ) {
        const arr = events[ key ]

        arr.splice( arr.indexOf( fcn ), 1 )
      }
    }

    this.publish = function( key, data ) {
      if( typeof events[ key ] !== 'undefined' ) {
        const arr = events[ key ]

        arr.forEach( v => v( data ) )
      }
    }
  },

  createProperty: Core.createProperty,

  createMapping( from, to, name, wrappedTo ) {
    if( from.__useMapping === false ) {
      to[ name ].value = from
    }else if( from.type === 'audio' ) {
      const f = to[ '__' + name ].follow = Follow({ input: from })

      let m = f.multiplier
      Object.defineProperty( to[ name ], 'multiplier', {
        get() { return m },
        set(v) { m = v; f.multiplier = m }
      })

      let o = f.offset
      Object.defineProperty( to[ name ], 'offset', {
        get() { return o },
        set(v) { o = v; f.offset = o }
      })

      wrappedTo[ name ] = f
    }else if( from.type === 'gen' ) {
      // gen objects can be referred to without the graphics/audio abstraction,
      // in which case they will have no .render() function, and don't need to be rendered
      const gen = from.render !== undefined ? from.render() : from

      wrappedTo[ name ] = gen
    }
  },

  createGetter( obj, name ) { return () => obj[ '__' + name ] },

  createSetter( obj, name, post, transform=null, isPoly=false ) {
    const setter = v => {
      let value, shouldSend = true

      if( typeof v === 'number' || typeof v === 'string' ) {
        value = transform !== null ? transform( v ) : v

        if( isPoly === true ) {
          const __wrappedObject = obj.__wrapped__
          const voice = __wrappedObject.voices[ __wrappedObject.voiceCount % __wrappedObject.voices.length ]
          voice[ name ] = value

          shouldSend = false

          Gibberish.worklet.port.postMessage({
            address:'property',
            object:voice.id,
            name,
            value
          })

        }else{
          obj[ '__'+name].value = v
        }
      }else if( typeof v === 'object' && v !== null && v.type === 'gen' ) {
        // gen objects can be referred to without the graphics/audio abstraction,
        // in which case they will have no .render() function, and don't need to be rendered
        const gen = v.render !== undefined ? v.render() : from

        obj['__'+ name ].value = gen
        value = { id: gen.id }
      }else{
        obj[ '__'+name].value = v
        value = v !== null ? { id:v.id } : v
      }
        //Audio.createMapping( v, obj, name, obj.__wrapped__ )

      if( Gibberish.mode === 'worklet' && shouldSend === true ) {
        Gibberish.worklet.port.postMessage({
          address:'property',
          object:obj.id,
          name,
          value
        })
      }
      if( post !== null ) {
        post.call( obj )
      }
    }

    return setter
  },

  createFade( from=null, to=null, time=1, obj, name ) {
    if( from === null ) from = obj[ name ].value
    if( to === null ) to = obj[ name ].value

    time = Gibber.Clock.time( time )

    // XXX only covers condition where ramps from fades are assigned...
    // does this need to be more generic?
    if( isNaN( from ) && from.__wrapped__.ugenName.indexOf('ramp') > -1 ) {
      from = from.to.value
    }
    if( isNaN( to ) && to.__wrapped__.ugenName.indexOf('ramp') > -1 ) {
      to = to.to.value
    }

    let ramp = Gibber.envelopes.Ramp({ from, to, length:time, shouldLoop:false })
    // this is a key to not use an envelope follower for mapping
    ramp.__useMapping = false

    obj[ name ] = ramp

    if( ramp.__wrapped__ === undefined ) ramp.__wrapped__ = {}
    ramp.__wrapped__.values = []

    ramp.__wrapped__.output = v => {
      if( ramp.__wrapped__ !== undefined ) {
        ramp.__wrapped__.values.unshift( v )
        while( ramp.__wrapped__.values.length > 60 ) ramp.__wrapped__.values.pop()
      }
    }

    ramp.__wrapped__.finalize = () => {
      const store = ramp.__wrapped__

      // XXX I can't quite figure out why I have to wait to reset the property
      // value here... if I don't, then the fade ugen stays assigned in the worklet processor.
      // and 0 doesn't work!
      setTimeout( ()=> obj[ name ] = store.to === 0 ? .000001 : store.to, 0 )
      store.widget.clear()
    }

    ramp.__wrapped__.from = from
    ramp.__wrapped__.to = to

    return obj
  },

  // what properties should be automatically (automagickally?)
  // filtered through Audio.Clock.time()?
  timeProps : {
    Synth:[ 'attack', 'decay', 'sustain', 'release' ],
    PolySynth:[ 'attack', 'decay', 'sustain', 'release' ],
    Complex:[ 'attack', 'decay', 'sustain', 'release' ],
    PolyComplex:[ 'attack', 'decay', 'sustain', 'release' ],
    FM:[ 'attack', 'decay', 'sustain', 'release' ],
    PolyFM:[ 'attack', 'decay', 'sustain', 'release' ],
    Monosynth:[ 'attack', 'decay', 'sustain', 'release' ],
    PolyMono:[ 'attack', 'decay', 'sustain', 'release' ],
    Delay:[ 'time' ],
  }
}

module.exports = Audio
<commit_msg>correcting print messages<commit_after>const Gibberish   = require( 'gibberish-dsp' )
const Ugen        = require( './ugen.js' )
const Instruments = require( './instruments.js' )
const Oscillators = require( './oscillators.js' )
const Effects     = require( './effects.js' )
const Filters     = require( './filters.js' )
const Binops      = require( './binops.js' )
const Analysis    = require( './analysis.js' )
const Envelopes   = require( './envelopes.js' )
const Busses      = require( './busses.js' )
const Ensemble    = require( './ensemble.js' )
const Utility     = require( './utility.js' )
const Freesound   = require( './freesound.js' )
const Gen         = require( './gen.js' )
const WavePattern = require( './wavePattern.js' )
const WaveObjects = require( './waveObjects.js' )
const Core        = require( 'gibber.core.lib' )
const AWPF        = require( './external/audioworklet-polyfill.js' )
//const Arp         = require( './arp.js' )

const Audio = {
  Clock: require( './clock.js' ),
  Theory: require( './theory.js' ),
  Presets: require( './presets.js' ),
  Make: require( './make.js' ),
  Core,
  initialized:false,
  autoConnect:true,
  shouldDelay:false,
  instruments:{},
  oscillators:{},
  effects:{},
  exportTarget:null,

  export( obj ) {
    if( Audio.initialized ){
      Object.assign(
        obj,
        this.instruments,
        this.oscillators,
        this.effects,
        this.filters,
        this.busses,
        this.envelopes,
        this.waveObjects,
        this.binops,
        this.analysis
      )

      Utility.export( obj )
      this.Gen.export( obj )

      obj.gen = this.Gen.make
      obj.lfo = this.Gen.composites.lfo
      obj.Ensemble = this.Ensemble
      obj.Drums = this.Drums
      obj.EDrums = this.EDrums
      obj.Theory = this.Theory
      obj.Freesound = this.Freesound
      obj.Clock = this.Clock
      obj.WavePattern = this.WavePattern
      obj.Gen = this.Gen

      obj.Out = this.Out
      obj.Make = this.Make
      obj.Gibberish = this.Gibberish
      obj.future = this.Gibberish.utilities.future
    }else{
      Audio.exportTarget = obj
    }
  },

  __defaults : {
    workletPath: '../dist/gibberish_worklet.js',
    ctx:         null,
    bufferSize:  2048
  },

  init( options, Gibber  ) {
    let { workletPath, ctx, bufferSize } = Object.assign( {}, this.__defaults, options )
    this.Gibber = Gibber
    Gibber.Audio = this
    this.Gibberish = Gibberish

    Gibberish.workletPath = workletPath

    this.createPubSub()

    const AC = typeof AudioContext === 'undefined' ? webkitAudioContext : AudioContext
    window.AudioContext = AC
    AWPF( window, bufferSize )

    const p = new Promise( (resolve, reject) => {
      if( ctx === null ) {
        ctx = new AC({ latencyHint:.05 })
        //ctx = new AudioContext()
      }

      Gibberish.init( 44100*60*10, ctx ).then( processorNode => {
        // XXX remove once gibber.core.lib has been properly integrated
        Audio.Core.Audio = Audio.Core.audio = Audio

        Audio.initialized = true
        Audio.node = processorNode
        Audio.Gen = Gen( Audio )
        Audio.Gen.init()
        //Audio.Arp = Arp( Gibber )
        Audio.Gen.export( Audio.Gen.ugens )
        Audio.Theory.init( Audio )
        Audio.Ugen = Ugen
        Audio.Utilities = Utility
        Audio.WavePattern = WavePattern( Audio )
        Audio.ctx = ctx
        Audio.Out = Gibberish.output

        // must wait for Gen to be initialized
        Audio.Clock.init( Audio.Gen, Audio )

        Audio.createUgens()

        if( Audio.exportTarget !== null ) Audio.export( Audio.exportTarget )

        Gibberish.worklet.port.__postMessage = Gibberish.worklet.port.postMessage

        Gibberish.worklet.port.postMessage = function( dict ) {
          if( Audio.shouldDelay === true ) dict.delay = true

          Gibberish.worklet.port.__postMessage( dict )
        }

        Audio.export( window )

        //const drums = Audio.Drums('x*o-')
        //drums.disconnect()
        //drums.stop()

        // store last location in memory... we can clear everything else in Gibber.clear9)
        const memIdx = Object.keys( Gibberish.memory.list ).reverse()[0]
        this.__memoryEnd = parseInt( memIdx ) + Gibberish.memory.list[ memIdx ]

        // XXX this forces the gibberish scheduler to start
        // running, but it's about as hacky as it can get...
        const __start = Audio.instruments.Synth().connect()
        __start.disconnect()

        Audio.Gibberish.genish.gen.histories.clear()

        resolve( Audio )
      })
    })

    return p
  },

  // XXX stop clock from being cleared.
  clear() {
    Gibberish.clear()
    Audio.Clock.init( Audio.Gen, Audio )

    // the idea is that we only clear memory that was filled after
    // the initial Gibber initialization... this stops objects
    // like Clock and Theory from having their memory cleared and
    // from having to re-initialize them.

    // fill memory with zeros from the end initialization block onwards
    Gibberish.memory.heap.fill( 0, this.__memoryEnd )

    // get locations of all memory blocks
    const memKeys = Object.keys( Gibberish.memory.list )

    // get idx of final initialization block
    const endIdx =  memKeys.indexOf( ''+this.__memoryEnd )

    // loop through all blocks after final initialzation block
    // and delete them in the memory list... they've already
    // been zeroed out.
    for( let i = endIdx; i < memKeys.length; i++ ) {
      delete Gibberish.memory.list[ memKeys[ i ] ]
    }

    Audio.publish('clear')
  },

  onload() {},

  createUgens() {
    //Core.export( this, this )

    this.Freesound = Freesound( this )
    this.binops = Binops.create( this )
    this.analysis = Analysis.create( this )
    this.oscillators = Oscillators.create( this )
    this.instruments = Instruments.create( this )
    this.envelopes   = Envelopes.create( this )
    this.filters     = Filters.create( this )
    this.effects = Effects.create( this )
    this.busses = Busses.create( this )
    this.Ensemble = Ensemble( this )
    this.waveObjects = WaveObjects( this )

    const Pattern = Core.__Pattern
    Pattern.transfer( this, Pattern.toString() )

    this.Make = this.Make( this )

    const drums = require( './drums.js' )( this )
    Object.assign( this, drums )
  },

  printcb() {
    Gibber.Audio.Gibberish.worklet.port.postMessage({ address:'callback' })
  },
  printobj( obj ) {
    Gibber.Audio.Gibberish.worklet.port.postMessage({ address:'print', object:obj.id })
  },
  send( msg ){
    Gibber.Audio.Gibberish.worklet.port.postMessage( msg )
  },

  createPubSub() {
    const events = {}
    this.subscribe = function( key, fcn ) {
      if( typeof events[ key ] === 'undefined' ) {
        events[ key ] = []
      }
      events[ key ].push( fcn )
    }

    this.unsubscribe = function( key, fcn ) {
      if( typeof events[ key ] !== 'undefined' ) {
        const arr = events[ key ]

        arr.splice( arr.indexOf( fcn ), 1 )
      }
    }

    this.publish = function( key, data ) {
      if( typeof events[ key ] !== 'undefined' ) {
        const arr = events[ key ]

        arr.forEach( v => v( data ) )
      }
    }
  },

  createProperty: Core.createProperty,

  createMapping( from, to, name, wrappedTo ) {
    if( from.__useMapping === false ) {
      to[ name ].value = from
    }else if( from.type === 'audio' ) {
      const f = to[ '__' + name ].follow = Follow({ input: from })

      let m = f.multiplier
      Object.defineProperty( to[ name ], 'multiplier', {
        get() { return m },
        set(v) { m = v; f.multiplier = m }
      })

      let o = f.offset
      Object.defineProperty( to[ name ], 'offset', {
        get() { return o },
        set(v) { o = v; f.offset = o }
      })

      wrappedTo[ name ] = f
    }else if( from.type === 'gen' ) {
      // gen objects can be referred to without the graphics/audio abstraction,
      // in which case they will have no .render() function, and don't need to be rendered
      const gen = from.render !== undefined ? from.render() : from

      wrappedTo[ name ] = gen
    }
  },

  createGetter( obj, name ) { return () => obj[ '__' + name ] },

  createSetter( obj, name, post, transform=null, isPoly=false ) {
    const setter = v => {
      let value, shouldSend = true

      if( typeof v === 'number' || typeof v === 'string' ) {
        value = transform !== null ? transform( v ) : v

        if( isPoly === true ) {
          const __wrappedObject = obj.__wrapped__
          const voice = __wrappedObject.voices[ __wrappedObject.voiceCount % __wrappedObject.voices.length ]
          voice[ name ] = value

          shouldSend = false

          Gibberish.worklet.port.postMessage({
            address:'property',
            object:voice.id,
            name,
            value
          })

        }else{
          obj[ '__'+name].value = v
        }
      }else if( typeof v === 'object' && v !== null && v.type === 'gen' ) {
        // gen objects can be referred to without the graphics/audio abstraction,
        // in which case they will have no .render() function, and don't need to be rendered
        const gen = v.render !== undefined ? v.render() : from

        obj['__'+ name ].value = gen
        value = { id: gen.id }
      }else{
        obj[ '__'+name].value = v
        value = v !== null ? { id:v.id } : v
      }
        //Audio.createMapping( v, obj, name, obj.__wrapped__ )

      if( Gibberish.mode === 'worklet' && shouldSend === true ) {
        Gibberish.worklet.port.postMessage({
          address:'property',
          object:obj.id,
          name,
          value
        })
      }
      if( post !== null ) {
        post.call( obj )
      }
    }

    return setter
  },

  createFade( from=null, to=null, time=1, obj, name ) {
    if( from === null ) from = obj[ name ].value
    if( to === null ) to = obj[ name ].value

    time = Gibber.Clock.time( time )

    // XXX only covers condition where ramps from fades are assigned...
    // does this need to be more generic?
    if( isNaN( from ) && from.__wrapped__.ugenName.indexOf('ramp') > -1 ) {
      from = from.to.value
    }
    if( isNaN( to ) && to.__wrapped__.ugenName.indexOf('ramp') > -1 ) {
      to = to.to.value
    }

    let ramp = Gibber.envelopes.Ramp({ from, to, length:time, shouldLoop:false })
    // this is a key to not use an envelope follower for mapping
    ramp.__useMapping = false

    obj[ name ] = ramp

    if( ramp.__wrapped__ === undefined ) ramp.__wrapped__ = {}
    ramp.__wrapped__.values = []

    ramp.__wrapped__.output = v => {
      if( ramp.__wrapped__ !== undefined ) {
        ramp.__wrapped__.values.unshift( v )
        while( ramp.__wrapped__.values.length > 60 ) ramp.__wrapped__.values.pop()
      }
    }

    ramp.__wrapped__.finalize = () => {
      const store = ramp.__wrapped__

      // XXX I can't quite figure out why I have to wait to reset the property
      // value here... if I don't, then the fade ugen stays assigned in the worklet processor.
      // and 0 doesn't work!
      setTimeout( ()=> obj[ name ] = store.to === 0 ? .000001 : store.to, 0 )
      store.widget.clear()
    }

    ramp.__wrapped__.from = from
    ramp.__wrapped__.to = to

    return obj
  },

  // what properties should be automatically (automagickally?)
  // filtered through Audio.Clock.time()?
  timeProps : {
    Synth:[ 'attack', 'decay', 'sustain', 'release' ],
    PolySynth:[ 'attack', 'decay', 'sustain', 'release' ],
    Complex:[ 'attack', 'decay', 'sustain', 'release' ],
    PolyComplex:[ 'attack', 'decay', 'sustain', 'release' ],
    FM:[ 'attack', 'decay', 'sustain', 'release' ],
    PolyFM:[ 'attack', 'decay', 'sustain', 'release' ],
    Monosynth:[ 'attack', 'decay', 'sustain', 'release' ],
    PolyMono:[ 'attack', 'decay', 'sustain', 'release' ],
    Delay:[ 'time' ],
  }
}

module.exports = Audio
<|endoftext|>


<commit_before>export class Capabilities {

  constructor(versionStr) {
    this.QUERY_POST_ENDPOINTS = true;
    this.QUERY_BY_TAGS = true;
    this.QUERY_STATS_POST_ENDPOINTS = true;
    this.FETCH_ALL_TAGS = true;
    this.TAGS_QUERY_LANGUAGE = true;
    const regExp = new RegExp('([0-9]+)\.([0-9]+)\.(.+)');
    if (versionStr.match(regExp)) {
      const versionInfo = regExp.exec(versionStr);
      const major = versionInfo[1];
      const minor = versionInfo[2];
      if (major === 0 && minor < 17) {
        this.QUERY_POST_ENDPOINTS = false;
      }
      if (major === 0 && minor < 20) {
        this.QUERY_STATS_POST_ENDPOINTS = false;
        this.QUERY_BY_TAGS = false;
      }
      if (major === 0 && minor < 22) {
        this.FETCH_ALL_TAGS = false;
      }
      if (major === 0 && minor < 24) {
        this.TAGS_QUERY_LANGUAGE = false;
      }
    }
  }
}
<commit_msg>Convert version to numbers<commit_after>export class Capabilities {

  constructor(versionStr) {
    this.QUERY_POST_ENDPOINTS = true;
    this.QUERY_BY_TAGS = true;
    this.QUERY_STATS_POST_ENDPOINTS = true;
    this.FETCH_ALL_TAGS = true;
    this.TAGS_QUERY_LANGUAGE = true;
    const regExp = new RegExp('([0-9]+)\.([0-9]+)\.(.+)');
    if (versionStr.match(regExp)) {
      const versionInfo = regExp.exec(versionStr);
      const major = +versionInfo[1];
      const minor = +versionInfo[2];
      if (major === 0 && minor < 17) {
        this.QUERY_POST_ENDPOINTS = false;
      }
      if (major === 0 && minor < 20) {
        this.QUERY_STATS_POST_ENDPOINTS = false;
        this.QUERY_BY_TAGS = false;
      }
      if (major === 0 && minor < 22) {
        this.FETCH_ALL_TAGS = false;
      }
      if (major === 0 && minor < 24) {
        this.TAGS_QUERY_LANGUAGE = false;
      }
    }
  }
}
<|endoftext|>


<commit_before>/**
 * Copyright 2020 The Subscribe with Google Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS-IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import {AutoPromptManager} from './auto-prompt-manager';
import {AutoPromptType} from '../api/basic-subscriptions';
import {ButtonApi, ButtonAttributeValues} from './button-api';
import {ConfiguredRuntime} from './runtime';
import {Constants} from '../utils/constants';
import {PageConfigResolver} from '../model/page-config-resolver';
import {PageConfigWriter} from '../model/page-config-writer';
import {Toast} from '../ui/toast';
import {XhrFetcher} from './fetcher';
import {acceptPortResultData} from '../utils/activity-utils';
import {feArgs, feOrigin, feUrl} from './services';
import {resolveDoc} from '../model/doc';

const BASIC_RUNTIME_PROP = 'SWG_BASIC';
const BUTTON_ATTRIUBUTE = 'swg-standard-button';
const CHECK_ENTITLEMENTS_REQUEST_ID = 'CHECK_ENTITLEMENTS';

/**
 * Reference to the runtime, for testing.
 * @private {!BasicRuntime}
 */
let basicRuntimeInstance_;

/**
 * Returns runtime for testing if available. Throws if the runtime is not
 * initialized yet.
 * @visibleForTesting
 * @return {!BasicRuntime}
 */
export function getBasicRuntime() {
  assert(basicRuntimeInstance_, 'not initialized yet');
  return basicRuntimeInstance_;
}

/**
 * Installs runtime for SwG Basic.
 * @param {!Window} win
 */
export function installBasicRuntime(win) {
  // Only install the SwG Basic runtime once.
  if (win[BASIC_RUNTIME_PROP] && !Array.isArray(win[BASIC_RUNTIME_PROP])) {
    return;
  }

  // Create the runtime.
  const basicRuntime = new BasicRuntime(win);

  // Create the public version of the SwG Basic runtime.
  /** @type {!../api/basic-subscriptions.BasicSubscriptions} */
  const publicBasicRuntime = createPublicBasicRuntime(basicRuntime);

  /**
   * Executes a callback when the runtime is ready.
   * @param {function(!../api/basic-subscriptions.BasicSubscriptions)} callback
   */
  function callWhenRuntimeIsReady(callback) {
    if (!callback) {
      return;
    }

    basicRuntime.whenReady().then(() => {
      callback(publicBasicRuntime);
    });
  }

  // Queue up any callbacks the publication might have provided.
  const waitingCallbacks = [].concat(win[BASIC_RUNTIME_PROP]);
  waitingCallbacks.forEach(callWhenRuntimeIsReady);

  // If any more callbacks are `push`ed to the global SwG Basic variables,
  // they'll be queued up to receive the SwG Basic runtime when it's ready.
  win[BASIC_RUNTIME_PROP] = {
    push: callWhenRuntimeIsReady,
  };

  // Set variable for testing.
  basicRuntimeInstance_ = basicRuntime;

  // Automatically set up buttons already on the page.
  basicRuntime.setupButtons();
}

/**
 * @implements {../api/basic-subscriptions.BasicSubscriptions}
 */
export class BasicRuntime {
  /**
   * @param {!Window} win
   */
  constructor(win) {
    /** @private @const {!Window} */
    this.win_ = win;

    /** @private @const {!../model/doc.Doc} */
    this.doc_ = resolveDoc(win);

    /** @private @const {!Promise} */
    this.ready_ = Promise.resolve();

    /** @private @const {!../api/subscriptions.Config} */
    this.config_ = {};

    /** @private {../api/basic-subscriptions.ClientOptions|undefined} */
    this.clientOptions_ = undefined;

    /** @private {boolean} */
    this.committed_ = false;

    /** @private {?function((!ConfiguredBasicRuntime|!Promise))} */
    this.configuredResolver_ = null;

    /** @private @const {!Promise<!ConfiguredBasicRuntime>} */
    this.configuredPromise_ = new Promise((resolve) => {
      this.configuredResolver_ = resolve;
    });

    /** @private {?PageConfigWriter} */
    this.pageConfigWriter_ = null;

    /** @private {?PageConfigResolver} */
    this.pageConfigResolver_ = null;
  }

  /**
   * @return {!Promise}
   */
  whenReady() {
    return this.ready_;
  }

  /**
   * @param {boolean} commit
   * @return {!Promise<!ConfiguredBasicRuntime>}
   * @private
   */
  configured_(commit) {
    if (!this.committed_ && commit && !this.pageConfigWriter_) {
      this.committed_ = true;

      this.pageConfigResolver_ = new PageConfigResolver(this.doc_);
      this.pageConfigResolver_.resolveConfig().then(
        (pageConfig) => {
          this.pageConfigResolver_ = null;
          this.configuredResolver_(
            new ConfiguredBasicRuntime(
              this.doc_,
              pageConfig,
              /* integr */ {configPromise: this.configuredPromise_},
              this.config_,
              this.clientOptions_
            )
          );
          this.configuredResolver_ = null;
        },
        (reason) => {
          this.configuredResolver_(Promise.reject(reason));
          this.configuredResolver_ = null;
        }
      );
    } else if (commit && this.pageConfigResolver_) {
      this.pageConfigResolver_.check();
    }
    return this.configuredPromise_;
  }

  /** @override */
  /* eslint-disable no-unused-vars */
  init(params) {
    this.pageConfigWriter_ = new PageConfigWriter(this.doc_);
    this.pageConfigWriter_
      .writeConfigWhenReady({
        type: params.type,
        isAccessibleForFree: params.isAccessibleForFree,
        isPartOfType: params.isPartOfType,
        isPartOfProductId: params.isPartOfProductId,
      })
      .then(() => {
        this.pageConfigWriter_ = null;
        this.configured_(true);
      });

    this.clientOptions_ = params.clientOptions;
    this.setupAndShowAutoPrompt({
      autoPromptType: params.autoPromptType,
      alwaysShow: false,
    });
    this.setOnLoginRequest();
    this.processEntitlements();
  }
  /* eslint-enable no-unused-vars */

  /** @override */
  setOnEntitlementsResponse(callback) {
    return this.configured_(false).then((runtime) =>
      runtime.setOnEntitlementsResponse(callback)
    );
  }

  /** @override */
  setOnPaymentResponse(callback) {
    return this.configured_(false).then((runtime) =>
      runtime.setOnPaymentResponse(callback)
    );
  }

  /** @override */
  setOnLoginRequest() {
    return this.configured_(false).then((runtime) =>
      runtime.setOnLoginRequest()
    );
  }

  /** @override */
  setupAndShowAutoPrompt(options) {
    return this.configured_(false).then((runtime) =>
      runtime.setupAndShowAutoPrompt(options)
    );
  }

  /** @override */
  dismissSwgUI() {
    return this.configured_(false).then((runtime) => runtime.dismissSwgUI());
  }

  /**
   * Sets up all the buttons on the page with attribute
   * 'swg-standard-button:subscription' or 'swg-standard-button:contribution'.
   */
  setupButtons() {
    return this.configured_(false).then((runtime) => runtime.setupButtons());
  }

  /** Process result from checkentitlements view */
  processEntitlements() {
    return this.configured_(false).then((runtime) =>
      runtime.processEntitlements()
    );
  }
}

/**
 * @implements  {../api/basic-subscriptions.BasicSubscriptions}
 * @implements {./deps.DepsDef}
 */
// eslint-disable-next-line no-unused-vars
export class ConfiguredBasicRuntime {
  /**
   * @param {!Window|!Document|!../model/doc.Doc} winOrDoc
   * @param {!../model/page-config.PageConfig} pageConfig
   * @param {{
   *     fetcher: (!./fetcher.Fetcher|undefined),
   *     configPromise: (!Promise|undefined),
   *   }=} integr
   * @param {!../api/subscriptions.Config=} config
   * @param {!../api/basic-subscriptions.ClientOptions=} clientOptions
   */
  constructor(winOrDoc, pageConfig, integr, config, clientOptions) {
    /** @private @const {!../model/doc.Doc} */
    this.doc_ = resolveDoc(winOrDoc);

    /** @private @const {!Window} */
    this.win_ = this.doc_.getWin();

    integr = integr || {};
    integr.configPromise = integr.configPromise || Promise.resolve();
    integr.fetcher = integr.fetcher || new XhrFetcher(this.win_);
    integr.enableGoogleAnalytics = true;

    /** @private @const {!./fetcher.Fetcher} */
    this.fetcher_ = integr.fetcher;

    /** @private @const {!ConfiguredRuntime} */
    this.configuredClassicRuntime_ = new ConfiguredRuntime(
      winOrDoc,
      pageConfig,
      integr,
      config,
      clientOptions
    );

    // Do not show toast in swgz.
    this.entitlementsManager().blockNextNotification();

    // Fetches entitlements.
    this.configuredClassicRuntime_.start();

    // Fetch the client config.
    this.configuredClassicRuntime_.clientConfigManager().fetchClientConfig();

    /** @private @const {!AutoPromptManager} */
    this.autoPromptManager_ = new AutoPromptManager(this);

    /** @private @const {!ButtonApi} */
    this.buttonApi_ = new ButtonApi(
      this.doc_,
      Promise.resolve(this.configuredClassicRuntime_)
    );
    this.buttonApi_.init(); // Injects swg-button stylesheet.
  }

  /** Getter for the ConfiguredRuntime, exposed for testing. */
  configuredClassicRuntime() {
    return this.configuredClassicRuntime_;
  }

  /** @override */
  doc() {
    return this.doc_;
  }

  /** @override */
  win() {
    return this.win_;
  }

  /** @override */
  config() {
    return this.configuredClassicRuntime_.config();
  }

  /** @override */
  pageConfig() {
    return this.configuredClassicRuntime_.pageConfig();
  }

  /** @override */
  activities() {
    return this.configuredClassicRuntime_.activities();
  }

  /** @override */
  payClient() {
    return this.configuredClassicRuntime_.payClient();
  }

  /** @override */
  dialogManager() {
    return this.configuredClassicRuntime_.dialogManager();
  }

  /** @override */
  entitlementsManager() {
    return this.configuredClassicRuntime_.entitlementsManager();
  }

  /** @override */
  callbacks() {
    return this.configuredClassicRuntime_.callbacks();
  }

  /** @override */
  storage() {
    return this.configuredClassicRuntime_.storage();
  }

  /** @override */
  analytics() {
    return this.configuredClassicRuntime_.analytics();
  }

  /** @override */
  jserror() {
    return this.configuredClassicRuntime_.jserror();
  }

  /** @override */
  eventManager() {
    return this.configuredClassicRuntime_.eventManager();
  }

  /** @override */
  clientConfigManager() {
    return this.configuredClassicRuntime_.clientConfigManager();
  }

  /** @override */
  init() {
    // Implemented by the 'BasicRuntime' class.
  }

  /** @override */
  setOnEntitlementsResponse(callback) {
    this.configuredClassicRuntime_.setOnEntitlementsResponse(callback);
  }

  /** @override */
  setOnPaymentResponse(callback) {
    this.configuredClassicRuntime_.setOnPaymentResponse(callback);
  }

  /** @override */
  setOnLoginRequest() {
    this.configuredClassicRuntime_.setOnLoginRequest(() => {
      const publicationId = this.pageConfig().getPublicationId();
      const args = feArgs({
        'publicationId': publicationId,
      });

      this.activities().open(
        CHECK_ENTITLEMENTS_REQUEST_ID,
        feUrl('/checkentitlements', '', {
          'publicationId': publicationId,
        }),
        '_blank',
        args,
        {'width': 600, 'height': 600}
      );
    });
  }

  /** Process result from checkentitlements view */
  processEntitlements() {
    this.activities().onResult(
      CHECK_ENTITLEMENTS_REQUEST_ID,
      this.entitlementsResponseHandler.bind(this)
    );
  }

  /**
   * Handler function to process EntitlementsResponse.
   * @param {!../components/activities.ActivityPortDef} port
   */
  entitlementsResponseHandler(port) {
    const promise = acceptPortResultData(
      port,
      feOrigin(),
      /* requireOriginVerified */ true,
      /* requireSecureChannel */ true
    );
    return promise.then((response) => {
      const jwt = response['jwt'];
      if (jwt) {
        // If entitlements are returned, close the subscription/contribution offers iframe
        this.configuredClassicRuntime_.closeDialog();

        // Also save the entitlements and user token
        this.entitlementsManager().pushNextEntitlements(jwt);
        const userToken = response['usertoken'];
        if (userToken) {
          this.storage().set(Constants.USER_TOKEN, userToken, true);
        }

        // Show 'Signed in as abc@gmail.com' toast on the pub page.
        new Toast(this, feUrl('/toastiframe')).open();
      } else {
        // If no entitlements are returned, subscription/contribution offers iframe will show
        // a toast with label "no subscription/contribution found"
        const lastOffersFlow =
          this.configuredClassicRuntime_.getLastOffersFlow();
        if (lastOffersFlow) {
          lastOffersFlow.showNoEntitlementFoundToast();
          return;
        }

        const lastContributionsFlow =
          this.configuredClassicRuntime_.getLastContributionsFlow();
        if (lastContributionsFlow) {
          lastContributionsFlow.showNoEntitlementFoundToast();
        }
      }
    });
  }

  /** @override */
  setupAndShowAutoPrompt(options) {
    if (options.autoPromptType === AutoPromptType.SUBSCRIPTION) {
      options.displayForLockedContentFn = () => {
        this.configuredClassicRuntime_.showOffers({
          isClosable: !this.pageConfig().isLocked(),
        });
      };
    } else if (options.autoPromptType === AutoPromptType.CONTRIBUTION) {
      options.displayForLockedContentFn = () => {
        this.configuredClassicRuntime_.showContributionOptions({
          isClosable: !this.pageConfig().isLocked(),
        });
      };
    }
    return this.autoPromptManager_.showAutoPrompt(options);
  }

  /** @override */
  /** Dismiss displayed SwG UI */
  dismissSwgUI() {
    this.dialogManager().completeAll();
  }

  /**
   * Sets up all the buttons on the page with attribute
   * 'swg-standard-button:subscription' or 'swg-standard-button:contribution'.
   */
  setupButtons() {
    this.clientConfigManager()
      .shouldEnableButton()
      .then((enable) => {
        this.buttonApi_.attachButtonsWithAttribute(
          BUTTON_ATTRIUBUTE,
          [
            ButtonAttributeValues.SUBSCRIPTION,
            ButtonAttributeValues.CONTRIBUTION,
          ],
          {
            theme: this.clientConfigManager().getTheme(),
            lang: this.clientConfigManager().getLanguage(),
            enable,
          },
          {
            [ButtonAttributeValues.SUBSCRIPTION]: () => {
              this.configuredClassicRuntime_.showOffers({
                isClosable: !this.pageConfig().isLocked(),
              });
            },
            [ButtonAttributeValues.CONTRIBUTION]: () => {
              this.configuredClassicRuntime_.showContributionOptions({
                isClosable: !this.pageConfig().isLocked(),
              });
            },
          }
        );
      });
  }
}

/**
 * Creates and returns the public facing BasicSubscription object.
 * @param {!BasicRuntime} basicRuntime
 * @return {!../api/basic-subscriptions.BasicSubscriptions}
 */
function createPublicBasicRuntime(basicRuntime) {
  return /** @type {!../api/basic-subscriptions.BasicSubscriptions} */ ({
    init: basicRuntime.init.bind(basicRuntime),
    setOnEntitlementsResponse:
      basicRuntime.setOnEntitlementsResponse.bind(basicRuntime),
    setOnPaymentResponse: basicRuntime.setOnPaymentResponse.bind(basicRuntime),
    setOnLoginRequest: basicRuntime.setOnLoginRequest.bind(basicRuntime),
    setupAndShowAutoPrompt:
      basicRuntime.setupAndShowAutoPrompt.bind(basicRuntime),
    dismissSwgUI: basicRuntime.dismissSwgUI.bind(basicRuntime),
  });
}
<commit_msg>Update src/runtime/basic-runtime.js<commit_after>/**
 * Copyright 2020 The Subscribe with Google Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS-IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import {AutoPromptManager} from './auto-prompt-manager';
import {AutoPromptType} from '../api/basic-subscriptions';
import {ButtonApi, ButtonAttributeValues} from './button-api';
import {ConfiguredRuntime} from './runtime';
import {Constants} from '../utils/constants';
import {PageConfigResolver} from '../model/page-config-resolver';
import {PageConfigWriter} from '../model/page-config-writer';
import {Toast} from '../ui/toast';
import {XhrFetcher} from './fetcher';
import {acceptPortResultData} from '../utils/activity-utils';
import {feArgs, feOrigin, feUrl} from './services';
import {resolveDoc} from '../model/doc';

const BASIC_RUNTIME_PROP = 'SWG_BASIC';
const BUTTON_ATTRIUBUTE = 'swg-standard-button';
const CHECK_ENTITLEMENTS_REQUEST_ID = 'CHECK_ENTITLEMENTS';

/**
 * Reference to the runtime, for testing.
 * @private {!BasicRuntime}
 */
let basicRuntimeInstance_;

/**
 * Returns runtime for testing if available. Throws if the runtime is not
 * initialized yet.
 * @visibleForTesting
 * @return {!BasicRuntime}
 */
export function getBasicRuntime() {
  assert(basicRuntimeInstance_, 'not initialized yet');
  return basicRuntimeInstance_;
}

/**
 * Installs runtime for SwG Basic.
 * @param {!Window} win
 */
export function installBasicRuntime(win) {
  // Only install the SwG Basic runtime once.
  if (win[BASIC_RUNTIME_PROP] && !Array.isArray(win[BASIC_RUNTIME_PROP])) {
    return;
  }

  // Create the runtime.
  const basicRuntime = new BasicRuntime(win);

  // Create the public version of the SwG Basic runtime.
  /** @type {!../api/basic-subscriptions.BasicSubscriptions} */
  const publicBasicRuntime = createPublicBasicRuntime(basicRuntime);

  /**
   * Executes a callback when the runtime is ready.
   * @param {function(!../api/basic-subscriptions.BasicSubscriptions)} callback
   */
  function callWhenRuntimeIsReady(callback) {
    if (!callback) {
      return;
    }

    basicRuntime.whenReady().then(() => {
      callback(publicBasicRuntime);
    });
  }

  // Queue up any callbacks the publication might have provided.
  const waitingCallbacks = [].concat(win[BASIC_RUNTIME_PROP]);
  waitingCallbacks.forEach(callWhenRuntimeIsReady);

  // If any more callbacks are `push`ed to the global SwG Basic variables,
  // they'll be queued up to receive the SwG Basic runtime when it's ready.
  win[BASIC_RUNTIME_PROP] = {
    push: callWhenRuntimeIsReady,
  };

  // Set variable for testing.
  basicRuntimeInstance_ = basicRuntime;

  // Automatically set up buttons already on the page.
  basicRuntime.setupButtons();
}

/**
 * @implements {../api/basic-subscriptions.BasicSubscriptions}
 */
export class BasicRuntime {
  /**
   * @param {!Window} win
   */
  constructor(win) {
    /** @private @const {!Window} */
    this.win_ = win;

    /** @private @const {!../model/doc.Doc} */
    this.doc_ = resolveDoc(win);

    /** @private @const {!Promise} */
    this.ready_ = Promise.resolve();

    /** @private @const {!../api/subscriptions.Config} */
    this.config_ = {};

    /** @private {../api/basic-subscriptions.ClientOptions|undefined} */
    this.clientOptions_ = undefined;

    /** @private {boolean} */
    this.committed_ = false;

    /** @private {?function((!ConfiguredBasicRuntime|!Promise))} */
    this.configuredResolver_ = null;

    /** @private @const {!Promise<!ConfiguredBasicRuntime>} */
    this.configuredPromise_ = new Promise((resolve) => {
      this.configuredResolver_ = resolve;
    });

    /** @private {?PageConfigWriter} */
    this.pageConfigWriter_ = null;

    /** @private {?PageConfigResolver} */
    this.pageConfigResolver_ = null;
  }

  /**
   * @return {!Promise}
   */
  whenReady() {
    return this.ready_;
  }

  /**
   * @param {boolean} commit
   * @return {!Promise<!ConfiguredBasicRuntime>}
   * @private
   */
  configured_(commit) {
    if (!this.committed_ && commit && !this.pageConfigWriter_) {
      this.committed_ = true;

      this.pageConfigResolver_ = new PageConfigResolver(this.doc_);
      this.pageConfigResolver_.resolveConfig().then(
        (pageConfig) => {
          this.pageConfigResolver_ = null;
          this.configuredResolver_(
            new ConfiguredBasicRuntime(
              this.doc_,
              pageConfig,
              /* integr */ {configPromise: this.configuredPromise_},
              this.config_,
              this.clientOptions_
            )
          );
          this.configuredResolver_ = null;
        },
        (reason) => {
          this.configuredResolver_(Promise.reject(reason));
          this.configuredResolver_ = null;
        }
      );
    } else if (commit && this.pageConfigResolver_) {
      this.pageConfigResolver_.check();
    }
    return this.configuredPromise_;
  }

  /** @override */
  /* eslint-disable no-unused-vars */
  init(params) {
    this.pageConfigWriter_ = new PageConfigWriter(this.doc_);
    this.pageConfigWriter_
      .writeConfigWhenReady({
        type: params.type,
        isAccessibleForFree: params.isAccessibleForFree,
        isPartOfType: params.isPartOfType,
        isPartOfProductId: params.isPartOfProductId,
      })
      .then(() => {
        this.pageConfigWriter_ = null;
        this.configured_(true);
      });

    this.clientOptions_ = params.clientOptions;
    this.setupAndShowAutoPrompt({
      autoPromptType: params.autoPromptType,
      alwaysShow: false,
    });
    this.setOnLoginRequest();
    this.processEntitlements();
  }
  /* eslint-enable no-unused-vars */

  /** @override */
  setOnEntitlementsResponse(callback) {
    return this.configured_(false).then((runtime) =>
      runtime.setOnEntitlementsResponse(callback)
    );
  }

  /** @override */
  setOnPaymentResponse(callback) {
    return this.configured_(false).then((runtime) =>
      runtime.setOnPaymentResponse(callback)
    );
  }

  /** @override */
  setOnLoginRequest() {
    return this.configured_(false).then((runtime) =>
      runtime.setOnLoginRequest()
    );
  }

  /** @override */
  setupAndShowAutoPrompt(options) {
    return this.configured_(false).then((runtime) =>
      runtime.setupAndShowAutoPrompt(options)
    );
  }

  /** @override */
  dismissSwgUI() {
    return this.configured_(false).then((runtime) => runtime.dismissSwgUI());
  }

  /**
   * Sets up all the buttons on the page with attribute
   * 'swg-standard-button:subscription' or 'swg-standard-button:contribution'.
   */
  setupButtons() {
    return this.configured_(false).then((runtime) => runtime.setupButtons());
  }

  /** Process result from checkentitlements view */
  processEntitlements() {
    return this.configured_(false).then((runtime) =>
      runtime.processEntitlements()
    );
  }
}

/**
 * @implements  {../api/basic-subscriptions.BasicSubscriptions}
 * @implements {./deps.DepsDef}
 */
// eslint-disable-next-line no-unused-vars
export class ConfiguredBasicRuntime {
  /**
   * @param {!Window|!Document|!../model/doc.Doc} winOrDoc
   * @param {!../model/page-config.PageConfig} pageConfig
   * @param {{
   *     fetcher: (!./fetcher.Fetcher|undefined),
   *     configPromise: (!Promise|undefined),
   *   }=} integr
   * @param {!../api/subscriptions.Config=} config
   * @param {!../api/basic-subscriptions.ClientOptions=} clientOptions
   */
  constructor(winOrDoc, pageConfig, integr, config, clientOptions) {
    /** @private @const {!../model/doc.Doc} */
    this.doc_ = resolveDoc(winOrDoc);

    /** @private @const {!Window} */
    this.win_ = this.doc_.getWin();

    integr = integr || {};
    integr.configPromise = integr.configPromise || Promise.resolve();
    integr.fetcher = integr.fetcher || new XhrFetcher(this.win_);
    integr.enableGoogleAnalytics = true;

    /** @private @const {!./fetcher.Fetcher} */
    this.fetcher_ = integr.fetcher;

    /** @private @const {!ConfiguredRuntime} */
    this.configuredClassicRuntime_ = new ConfiguredRuntime(
      winOrDoc,
      pageConfig,
      integr,
      config,
      clientOptions
    );

    // Do not show toast in swgz.
    this.entitlementsManager().blockNextNotification();

    // Fetches entitlements.
    this.configuredClassicRuntime_.start();

    // Fetch the client config.
    this.configuredClassicRuntime_.clientConfigManager().fetchClientConfig();

    /** @private @const {!AutoPromptManager} */
    this.autoPromptManager_ = new AutoPromptManager(this);

    /** @private @const {!ButtonApi} */
    this.buttonApi_ = new ButtonApi(
      this.doc_,
      Promise.resolve(this.configuredClassicRuntime_)
    );
    this.buttonApi_.init(); // Injects swg-button stylesheet.
  }

  /** Getter for the ConfiguredRuntime, exposed for testing. */
  configuredClassicRuntime() {
    return this.configuredClassicRuntime_;
  }

  /** @override */
  doc() {
    return this.doc_;
  }

  /** @override */
  win() {
    return this.win_;
  }

  /** @override */
  config() {
    return this.configuredClassicRuntime_.config();
  }

  /** @override */
  pageConfig() {
    return this.configuredClassicRuntime_.pageConfig();
  }

  /** @override */
  activities() {
    return this.configuredClassicRuntime_.activities();
  }

  /** @override */
  payClient() {
    return this.configuredClassicRuntime_.payClient();
  }

  /** @override */
  dialogManager() {
    return this.configuredClassicRuntime_.dialogManager();
  }

  /** @override */
  entitlementsManager() {
    return this.configuredClassicRuntime_.entitlementsManager();
  }

  /** @override */
  callbacks() {
    return this.configuredClassicRuntime_.callbacks();
  }

  /** @override */
  storage() {
    return this.configuredClassicRuntime_.storage();
  }

  /** @override */
  analytics() {
    return this.configuredClassicRuntime_.analytics();
  }

  /** @override */
  jserror() {
    return this.configuredClassicRuntime_.jserror();
  }

  /** @override */
  eventManager() {
    return this.configuredClassicRuntime_.eventManager();
  }

  /** @override */
  clientConfigManager() {
    return this.configuredClassicRuntime_.clientConfigManager();
  }

  /** @override */
  init() {
    // Implemented by the 'BasicRuntime' class.
  }

  /** @override */
  setOnEntitlementsResponse(callback) {
    this.configuredClassicRuntime_.setOnEntitlementsResponse(callback);
  }

  /** @override */
  setOnPaymentResponse(callback) {
    this.configuredClassicRuntime_.setOnPaymentResponse(callback);
  }

  /** @override */
  setOnLoginRequest() {
    this.configuredClassicRuntime_.setOnLoginRequest(() => {
      const publicationId = this.pageConfig().getPublicationId();
      const args = feArgs({
        'publicationId': publicationId,
      });

      this.activities().open(
        CHECK_ENTITLEMENTS_REQUEST_ID,
        feUrl('/checkentitlements', '', {
          'publicationId': publicationId,
        }),
        '_blank',
        args,
        {'width': 600, 'height': 600}
      );
    });
  }

  /** Process result from checkentitlements view */
  processEntitlements() {
    this.activities().onResult(
      CHECK_ENTITLEMENTS_REQUEST_ID,
      this.entitlementsResponseHandler.bind(this)
    );
  }

  /**
   * Handler function to process EntitlementsResponse.
   * @param {!../components/activities.ActivityPortDef} port
   */
  entitlementsResponseHandler(port) {
    const promise = acceptPortResultData(
      port,
      feOrigin(),
      /* requireOriginVerified */ true,
      /* requireSecureChannel */ true
    );
    return promise.then((response) => {
      const jwt = response['jwt'];
      if (jwt) {
        // If entitlements are returned, close the subscription/contribution offers iframe
        this.configuredClassicRuntime_.closeDialog();

        // Also save the entitlements and user token
        this.entitlementsManager().pushNextEntitlements(jwt);
        const userToken = response['usertoken'];
        if (userToken) {
          this.storage().set(Constants.USER_TOKEN, userToken, true);
        }

        // Show 'Signed in as abc@gmail.com' toast on the pub page.
        new Toast(this, feUrl('/toastiframe')).open();
      } else {
        // If no entitlements are returned, subscription/contribution offers iframe will show
        // a toast with label "no subscription/contribution found"
        const lastOffersFlow =
          this.configuredClassicRuntime_.getLastOffersFlow();
        if (lastOffersFlow) {
          lastOffersFlow.showNoEntitlementFoundToast();
          return;
        }

        const lastContributionsFlow =
          this.configuredClassicRuntime_.getLastContributionsFlow();
        if (lastContributionsFlow) {
          lastContributionsFlow.showNoEntitlementFoundToast();
          return;
        }
      }
    });
  }

  /** @override */
  setupAndShowAutoPrompt(options) {
    if (options.autoPromptType === AutoPromptType.SUBSCRIPTION) {
      options.displayForLockedContentFn = () => {
        this.configuredClassicRuntime_.showOffers({
          isClosable: !this.pageConfig().isLocked(),
        });
      };
    } else if (options.autoPromptType === AutoPromptType.CONTRIBUTION) {
      options.displayForLockedContentFn = () => {
        this.configuredClassicRuntime_.showContributionOptions({
          isClosable: !this.pageConfig().isLocked(),
        });
      };
    }
    return this.autoPromptManager_.showAutoPrompt(options);
  }

  /** @override */
  /** Dismiss displayed SwG UI */
  dismissSwgUI() {
    this.dialogManager().completeAll();
  }

  /**
   * Sets up all the buttons on the page with attribute
   * 'swg-standard-button:subscription' or 'swg-standard-button:contribution'.
   */
  setupButtons() {
    this.clientConfigManager()
      .shouldEnableButton()
      .then((enable) => {
        this.buttonApi_.attachButtonsWithAttribute(
          BUTTON_ATTRIUBUTE,
          [
            ButtonAttributeValues.SUBSCRIPTION,
            ButtonAttributeValues.CONTRIBUTION,
          ],
          {
            theme: this.clientConfigManager().getTheme(),
            lang: this.clientConfigManager().getLanguage(),
            enable,
          },
          {
            [ButtonAttributeValues.SUBSCRIPTION]: () => {
              this.configuredClassicRuntime_.showOffers({
                isClosable: !this.pageConfig().isLocked(),
              });
            },
            [ButtonAttributeValues.CONTRIBUTION]: () => {
              this.configuredClassicRuntime_.showContributionOptions({
                isClosable: !this.pageConfig().isLocked(),
              });
            },
          }
        );
      });
  }
}

/**
 * Creates and returns the public facing BasicSubscription object.
 * @param {!BasicRuntime} basicRuntime
 * @return {!../api/basic-subscriptions.BasicSubscriptions}
 */
function createPublicBasicRuntime(basicRuntime) {
  return /** @type {!../api/basic-subscriptions.BasicSubscriptions} */ ({
    init: basicRuntime.init.bind(basicRuntime),
    setOnEntitlementsResponse:
      basicRuntime.setOnEntitlementsResponse.bind(basicRuntime),
    setOnPaymentResponse: basicRuntime.setOnPaymentResponse.bind(basicRuntime),
    setOnLoginRequest: basicRuntime.setOnLoginRequest.bind(basicRuntime),
    setupAndShowAutoPrompt:
      basicRuntime.setupAndShowAutoPrompt.bind(basicRuntime),
    dismissSwgUI: basicRuntime.dismissSwgUI.bind(basicRuntime),
  });
}
<|endoftext|>


<commit_before>/*    Copyright 2016-2020 Firewalla Inc.
 *
 *    This program is free software: you can redistribute it and/or  modify
 *    it under the terms of the GNU Affero General Public License, version 3,
 *    as published by the Free Software Foundation.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU Affero General Public License for more details.
 *
 *    You should have received a copy of the GNU Affero General Public License
 *    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
'use strict';

const log = require('../net2/logger.js')(__filename);

const util = require('util');

const sem = require('../sensor/SensorEventManager.js').getInstance();

const Sensor = require('./Sensor.js').Sensor;

const Message = require('../net2/Message.js');

class DHCPSensor extends Sensor {
  constructor() {
    super();
    this.cache = {};
  }

  scheduleReload() {
    if (this.reloadTask)
      clearTimeout(this.reloadTask);
    this.reloadTask = setTimeout(() => {
      this.dhcpDump.start(false, (obj) => {
        if (obj && obj.mac) {
          // dedup
          if (this.cache[obj.mac])
            return;

          this.cache[obj.mac] = 1;
          setTimeout(() => {
            delete this.cache[obj.mac];
          }, 60 * 1000); // cache for one minute

          log.info(util.format("New Device Found: %s (%s)", obj.name, obj.mac));
          let eventMessage = {
            type: "NewDeviceWithMacOnly",
            mac: obj.mac,
            intf_mac: obj.intf_mac,
            intf_uuid: obj.intf_uuid,
            mtype: obj.mtype,
            from: 'dhcp',
            message: "may found a new device by dhcp"
          }
          if (!obj.ignoreName) {
            eventMessage.name = obj.name;
          }
          sem.emitEvent(eventMessage);
        }
      });
    }, 5000);
  }

  run() {
    let DhcpDump = require("../extension/dhcpdump/dhcpdump.js");
    this.dhcpDump = new DhcpDump();
    this.dhcpDump.install((obj) => {
      log.info("DHCPDUMP is installed");
      this.scheduleReload();

      sem.on(Message.MSG_SYS_NETWORK_INFO_RELOADED, () => {
        log.info("Schedule reload DHCPSensor since network info is reloaded");
        this.scheduleReload();
      })
    });
  }
}

module.exports = DHCPSensor;

<commit_msg>only process DHCPDISCOVER and DHCPREQUEST in DHCPSensor<commit_after>/*    Copyright 2016-2020 Firewalla Inc.
 *
 *    This program is free software: you can redistribute it and/or  modify
 *    it under the terms of the GNU Affero General Public License, version 3,
 *    as published by the Free Software Foundation.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU Affero General Public License for more details.
 *
 *    You should have received a copy of the GNU Affero General Public License
 *    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
'use strict';

const log = require('../net2/logger.js')(__filename);

const util = require('util');

const sem = require('../sensor/SensorEventManager.js').getInstance();

const Sensor = require('./Sensor.js').Sensor;

const Message = require('../net2/Message.js');

class DHCPSensor extends Sensor {
  constructor() {
    super();
    this.cache = {};
  }

  scheduleReload() {
    if (this.reloadTask)
      clearTimeout(this.reloadTask);
    this.reloadTask = setTimeout(() => {
      this.dhcpDump.start(false, (obj) => {
        if (obj && obj.mac) {
          // dedup
          if (this.cache[obj.mac])
            return;

          // only process DHCP packets from the client, so DHCPOFFER and DHCPACK do not count
          if (obj.mtype !== "DHCPDISCOVER" && obj.mtype !== "DHCPREQUEST")
            return;

          this.cache[obj.mac] = 1;
          setTimeout(() => {
            delete this.cache[obj.mac];
          }, 60 * 1000); // cache for one minute

          log.info(util.format("New Device Found: %s (%s)", obj.name, obj.mac));
          let eventMessage = {
            type: "NewDeviceWithMacOnly",
            mac: obj.mac,
            intf_mac: obj.intf_mac,
            intf_uuid: obj.intf_uuid,
            mtype: obj.mtype,
            from: 'dhcp',
            message: "may found a new device by dhcp"
          }
          if (!obj.ignoreName) {
            eventMessage.name = obj.name;
          }
          sem.emitEvent(eventMessage);
        }
      });
    }, 5000);
  }

  run() {
    let DhcpDump = require("../extension/dhcpdump/dhcpdump.js");
    this.dhcpDump = new DhcpDump();
    this.dhcpDump.install((obj) => {
      log.info("DHCPDUMP is installed");
      this.scheduleReload();

      sem.on(Message.MSG_SYS_NETWORK_INFO_RELOADED, () => {
        log.info("Schedule reload DHCPSensor since network info is reloaded");
        this.scheduleReload();
      })
    });
  }
}

module.exports = DHCPSensor;

<|endoftext|>


<commit_before>import SPELLS from 'common/SPELLS';
import ITEMS from 'common/ITEMS';

import CoreChecklist, { Rule } from 'Parser/Core/Modules/Features/Checklist';
import Abilities from 'Parser/Core/Modules/Abilities';
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 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 AlwaysBeCasting from './AlwaysBeCasting';

class Checklist extends CoreChecklist {
  static dependencies = {
    abilities: Abilities,
    castEfficiency: CastEfficiency,
    legendaryCountChecker: LegendaryCountChecker,
    legendaryUpgradeChecker: LegendaryUpgradeChecker,
    prePotion: PrePotion,
    alwaysBeCasting: AlwaysBeCasting,
    enchantChecker: EnchantChecker,
  };

  rules = [
    new Rule({
      name: 'Use your short cooldowns',
      description: 'These should generally always be recharging to maximize efficiency.',
      requirements: () => {
        return [
          new GenericCastEfficiencyRequirement({
            spell: SPELLS.IMMOLATION_AURA,
            onlyWithSuggestion: false,
          }),
          new GenericCastEfficiencyRequirement({
            spell: SPELLS.FRACTURE_TALENT,
            when: this.selectedCombatant.hasTalent(SPELLS.FRACTURE_TALENT.id),
            onlyWithSuggestion: false,
          }),
          new GenericCastEfficiencyRequirement({
            spell: SPELLS.FELBLADE_TALENT,
            when: this.selectedCombatant.hasTalent(SPELLS.FELBLADE_TALENT.id),
            onlyWithSuggestion: false,
          }),
        ];
      },
    }),

    new Rule({
      name: 'Maintain your buffs and debuffs',
      description: 'It is important to maintain these as they contribute a large amount to your DPS and HPS.',
      requirements: () => {
        return [
          new GenericCastEfficiencyRequirement({
            spell: SPELLS.SIGIL_OF_FLAME_CONCENTRATED,
            onlyWithSuggestion: false,
          }),
          new GenericCastEfficiencyRequirement({
            spell: SPELLS.DEMON_SPIKES,
            onlyWithSuggestion: false,
          }),
        ];
      },
    }),

    new Rule({
      name: 'Use your defensive cooldowns',
      description: 'Use these to block damage spikes and keep damage smooth to reduce external healing required.',
      requirements: () => {
        return [
          new GenericCastEfficiencyRequirement({
            spell: SPELLS.FIERY_BRAND,
            onlyWithSuggestion: false,
          }),
          new GenericCastEfficiencyRequirement({
            spell: SPELLS.METAMORPHOSIS_TANK,
            onlyWithSuggestion: false,
          }),
          new GenericCastEfficiencyRequirement({
            spell: SPELLS.ARCHIMONDES_HATRED_REBORN_ABSORB,
            when: this.selectedCombatant.hasTrinket(ITEMS.ARCHIMONDES_HATRED_REBORN.id),
            onlyWithSuggestion: false,
          }),
        ];
      },
    }),

    new PreparationRule(),
  ]
}

export default Checklist;
<commit_msg>added fel devastation to check list<commit_after>import SPELLS from 'common/SPELLS';
import ITEMS from 'common/ITEMS';

import CoreChecklist, { Rule } from 'Parser/Core/Modules/Features/Checklist';
import Abilities from 'Parser/Core/Modules/Abilities';
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 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 AlwaysBeCasting from './AlwaysBeCasting';

class Checklist extends CoreChecklist {
  static dependencies = {
    abilities: Abilities,
    castEfficiency: CastEfficiency,
    legendaryCountChecker: LegendaryCountChecker,
    legendaryUpgradeChecker: LegendaryUpgradeChecker,
    prePotion: PrePotion,
    alwaysBeCasting: AlwaysBeCasting,
    enchantChecker: EnchantChecker,
  };

  rules = [
    new Rule({
      name: 'Use your short cooldowns',
      description: 'These should generally always be recharging to maximize efficiency.',
      requirements: () => {
        return [
          new GenericCastEfficiencyRequirement({
            spell: SPELLS.IMMOLATION_AURA,
            onlyWithSuggestion: false,
          }),
          new GenericCastEfficiencyRequirement({
            spell: SPELLS.FRACTURE_TALENT,
            when: this.selectedCombatant.hasTalent(SPELLS.FRACTURE_TALENT.id),
            onlyWithSuggestion: false,
          }),
          new GenericCastEfficiencyRequirement({
            spell: SPELLS.FELBLADE_TALENT,
            when: this.selectedCombatant.hasTalent(SPELLS.FELBLADE_TALENT.id),
            onlyWithSuggestion: false,
          }),
          new GenericCastEfficiencyRequirement({
            spell: SPELLS.FEL_DEVASTATION_TALENT,
            when: this.selectedCombatant.hasTalent(SPELLS.FEL_DEVASTATION_TALENT.id),
            onlyWithSuggestion: false,
          }),
        ];
      },
    }),

    new Rule({
      name: 'Maintain your buffs and debuffs',
      description: 'It is important to maintain these as they contribute a large amount to your DPS and HPS.',
      requirements: () => {
        return [
          new GenericCastEfficiencyRequirement({
            spell: SPELLS.SIGIL_OF_FLAME_CONCENTRATED,
            onlyWithSuggestion: false,
          }),
          new GenericCastEfficiencyRequirement({
            spell: SPELLS.DEMON_SPIKES,
            onlyWithSuggestion: false,
          }),
        ];
      },
    }),

    new Rule({
      name: 'Use your defensive cooldowns',
      description: 'Use these to block damage spikes and keep damage smooth to reduce external healing required.',
      requirements: () => {
        return [
          new GenericCastEfficiencyRequirement({
            spell: SPELLS.FIERY_BRAND,
            onlyWithSuggestion: false,
          }),
          new GenericCastEfficiencyRequirement({
            spell: SPELLS.METAMORPHOSIS_TANK,
            onlyWithSuggestion: false,
          }),
          new GenericCastEfficiencyRequirement({
            spell: SPELLS.ARCHIMONDES_HATRED_REBORN_ABSORB,
            when: this.selectedCombatant.hasTrinket(ITEMS.ARCHIMONDES_HATRED_REBORN.id),
            onlyWithSuggestion: false,
          }),
        ];
      },
    }),

    new PreparationRule(),
  ]
}

export default Checklist;
<|endoftext|>


<commit_before>var Micro = Y.Template.Micro;

function Template() {}

Template.prototype = {

    // -- Public Properties ----------------------------------------------------

    baseTemplate:
        '<form class="form-horizontal">' +
        '</form',

    tabTemplate: Micro.compile(
            '<fieldset>' +
                '<div id="<%= data.id %>">' +
                '
' +
            '</fieldset>'
    ),

    inputTemplate: Micro.compile(
        '
' +
            '' +
                '<%= data.title %> <%= data.sub_title %>' +
                '
' +
                    '<input type="text" data-path="<%= data.id %>" id="<%= data.id %>" name="<%= data.id %>" placeholder="<%= data.title %>" value="<%= data.value %>">' +
                '
' +
            '' +
        '
'
    ),

    selectTemplate: Micro.compile(
        '
' +
            '' +
                '<%= data.title %> <%= data.sub_title %>' +
                '
' +
                    '<select data-path="<%= data.id %>" id="<%= data.id %>" name="<%= data.id %>">' +
                        '<% Y.Object.each(data.options, function (option, i) { %>' +
                            '<option <%= option.selected ? \'selected="selected"\' : \'\' %> value="<%= option.value %>"><%= option.title %></option>' +
                        '<% }); %>' +
                    '</select>' +
                '
' +
            '' +
        '
'
    ),

    textareaTemplate: Micro.compile(
        '
' +
            '' +
                '<%= data.title %> <%= data.sub_title %>' +
                '
' +
                    '<textarea data-path="<%= data.id %>" id="<%= data.id %>" name="<%= data.id %>" placeholder="<%= data.title %>"><%= data.value %></textarea>' +
                '
' +
            '' +
        '
'
    ),

    // -- Lifecycle Methods ----------------------------------------------------

    initializer: function () {
        this._TemplateEvents = [
            Y.Do.before(this._beforeFocusInput, this, '_focusInput', this)
        ];

        this.template = Y.Node.create(this.baseTemplate);
    },

    destructor: function () {
        (new Y.EventHandle(this._TemplateEvents)).detach();
    },

    _beforeFocusInput: function () {
        var tabs       = Y.Object.isEmpty(this.get('tabs')),
            properties = Y.Object.isEmpty(this.get('properties'));

        if (tabs === false) {
            this._createTabs();
        }

        if (properties === false) {
            this._createTemplate();
        }

        if (tabs === false) {
            this._renderTabs();
        }

        if (tabs === false || properties === false) {
            this.panel.set('bodyContent', this.template);
        }
    },

    _createTabs: function () {
        var self = this,
            tabs = this.get('tabs');

        Y.Object.each(tabs, function (tab) {
            self.template.append(self.tabTemplate(tab));
        });
    },

    _createTemplate: function () {
        var self       = this,
            properties = this.get('properties'),
            tabs       = Y.Object.isEmpty(this.get('tabs')),
            template;

        Y.Object.each(properties, function (property) {
            switch (property.type) {
                case 'input':
                    template = self.inputTemplate;
                    break;
                case 'select':
                    template = self.selectTemplate;
                    break;
                case 'textarea':
                    template = self.textareaTemplate;
                    break;
                default:
                    template = function(){ console.error('Type "%s" in property "%s" is not supported.', property.type, property.title); };
                    break;
            }

            if (property.tab && tabs === false) {
                self.template.one('#' + property.tab).append(template(property));
            } else {
                self.template.append(template(property));
            }
        });
    },

    _renderTabs: function () {
        var container = this.template,
            tabs      = this.get('tabs');

        Y.Object.each(tabs, function (tab, i) {
            if (i === '0') {
                tab.active = true;
            }

            tab.container = container.one('div#' + tab.id);
        });

        this.tabView = new Y.Rednose.TabView({
                tabs: tabs
            });

        this.tabView.render(container);
    }
};

Template.ATTRS = {
    /**
     * @attribute tabs
     * @type Object [tabs]
     *   @param {Array}
     *     @param {String} [tabs.id] The element id.
     *     @param {String} [tabs.title] The element title.
     */
    tabs: {
        value: []
    },

    /**
     * @attribute properties
     * @type {Object} [properties] The following options can be specified:
     *   @param {Array}
     *     @param {String} [properties.type] The element type. input|select|textarea
     *     @param {String} [properties.id] The element id.
     *     @param {String} [properties.title] The element title.
     *     @param {String} [properties.sub_title] The the element subtitle.
     *     @param {String} [properties.value] The the element subtitle.
     *     @param {Object} [properties.options] The select options if type is select.
     *       @param {String} [properties.options.value] The option value.
     *       @param {String} [properties.options.title] The option title.
     *       @param {String} [properties.options.selected] True if selected.
     */
    properties: {
        value: []
    }
};

// -- Namespace ----------------------------------------------------------------
Y.Rednose.Dialog.Template = Template;
Y.Base.mix(Y.Rednose.Dialog, [Template]);<commit_msg>Indent fix<commit_after>var Micro = Y.Template.Micro;

function Template() {}

Template.prototype = {

    // -- Public Properties ----------------------------------------------------

    baseTemplate:
        '<form class="form-horizontal">' +
        '</form',

    tabTemplate: Micro.compile(
        '<fieldset>' +
            '<div id="<%= data.id %>">' +
            '
' +
        '</fieldset>'
    ),

    inputTemplate: Micro.compile(
        '
' +
            '' +
                '<%= data.title %> <%= data.sub_title %>' +
                '
' +
                    '<input type="text" data-path="<%= data.id %>" id="<%= data.id %>" name="<%= data.id %>" placeholder="<%= data.title %>" value="<%= data.value %>">' +
                '
' +
            '' +
        '
'
    ),

    selectTemplate: Micro.compile(
        '
' +
            '' +
                '<%= data.title %> <%= data.sub_title %>' +
                '
' +
                    '<select data-path="<%= data.id %>" id="<%= data.id %>" name="<%= data.id %>">' +
                        '<% Y.Object.each(data.options, function (option, i) { %>' +
                            '<option <%= option.selected ? \'selected="selected"\' : \'\' %> value="<%= option.value %>"><%= option.title %></option>' +
                        '<% }); %>' +
                    '</select>' +
                '
' +
            '' +
        '
'
    ),

    textareaTemplate: Micro.compile(
        '
' +
            '' +
                '<%= data.title %> <%= data.sub_title %>' +
                '
' +
                    '<textarea data-path="<%= data.id %>" id="<%= data.id %>" name="<%= data.id %>" placeholder="<%= data.title %>"><%= data.value %></textarea>' +
                '
' +
            '' +
        '
'
    ),

    // -- Lifecycle Methods ----------------------------------------------------

    initializer: function () {
        this._TemplateEvents = [
            Y.Do.before(this._beforeFocusInput, this, '_focusInput', this)
        ];

        this.template = Y.Node.create(this.baseTemplate);
    },

    destructor: function () {
        (new Y.EventHandle(this._TemplateEvents)).detach();
    },

    _beforeFocusInput: function () {
        var tabs       = Y.Object.isEmpty(this.get('tabs')),
            properties = Y.Object.isEmpty(this.get('properties'));

        if (tabs === false) {
            this._createTabs();
        }

        if (properties === false) {
            this._createTemplate();
        }

        if (tabs === false) {
            this._renderTabs();
        }

        if (tabs === false || properties === false) {
            this.panel.set('bodyContent', this.template);
        }
    },

    _createTabs: function () {
        var self = this,
            tabs = this.get('tabs');

        Y.Object.each(tabs, function (tab) {
            self.template.append(self.tabTemplate(tab));
        });
    },

    _createTemplate: function () {
        var self       = this,
            properties = this.get('properties'),
            tabs       = Y.Object.isEmpty(this.get('tabs')),
            template;

        Y.Object.each(properties, function (property) {
            switch (property.type) {
                case 'input':
                    template = self.inputTemplate;
                    break;
                case 'select':
                    template = self.selectTemplate;
                    break;
                case 'textarea':
                    template = self.textareaTemplate;
                    break;
                default:
                    template = function(){ console.error('Type "%s" in property "%s" is not supported.', property.type, property.title); };
                    break;
            }

            if (property.tab && tabs === false) {
                self.template.one('#' + property.tab).append(template(property));
            } else {
                self.template.append(template(property));
            }
        });
    },

    _renderTabs: function () {
        var container = this.template,
            tabs      = this.get('tabs');

        Y.Object.each(tabs, function (tab, i) {
            if (i === '0') {
                tab.active = true;
            }

            tab.container = container.one('div#' + tab.id);
        });

        this.tabView = new Y.Rednose.TabView({
            tabs: tabs
        });

        this.tabView.render(container);
    }
};

Template.ATTRS = {
    /**
     * @attribute tabs
     * @type Object [tabs]
     *   @param {Array}
     *     @param {String} [tabs.id] The element id.
     *     @param {String} [tabs.title] The element title.
     */
    tabs: {
        value: []
    },

    /**
     * @attribute properties
     * @type {Object} [properties] The following options can be specified:
     *   @param {Array}
     *     @param {String} [properties.type] The element type. input|select|textarea
     *     @param {String} [properties.id] The element id.
     *     @param {String} [properties.title] The element title.
     *     @param {String} [properties.sub_title] The the element subtitle.
     *     @param {String} [properties.value] The the element subtitle.
     *     @param {Object} [properties.options] The select options if type is select.
     *       @param {String} [properties.options.value] The option value.
     *       @param {String} [properties.options.title] The option title.
     *       @param {String} [properties.options.selected] True if selected.
     */
    properties: {
        value: []
    }
};

// -- Namespace ----------------------------------------------------------------
Y.Rednose.Dialog.Template = Template;
Y.Base.mix(Y.Rednose.Dialog, [Template]);
<|endoftext|>


<commit_before>
describe('PlayerCtrl', function () {
  var   scope,
        mockSquareRepository,
        player = ["player"];

    beforeEach(function () {
        module("app");

        inject(function ($rootScope, $controller, SquareRepository) {
            scope = $rootScope.$new();

            // mockFirebase = sinon.stub($firebase);
            mockSquareRepository = sinon.stub(SquareRepository);

            $controller("PlayerCtrl", {
                $scope: scope
                // firebase: mockFirebase
            });

        });
    });

    // describe('when the controller first loads', function () {

        // it('the game is retrieved', function () {
        //     sinon.assert.calledOnce(mockFirebase.getGame);
        // });
        // it('puts the catalog on the scope', function() {
        //     expect(scope.catalog).toEqual(catalog);
        // });

    // });

    describe('when the game player page is loaded', function () {
        it('loads the first square', function () {
            sinon.assert.calledOnce(mockSquareRepository.getSquare);
        });

        it('loads all squares for the first lap', function () {
            // iterates through all the squares in SquareRepository
            // and displays them in player.
        });
    });

    describe('audio_path', function() {

        // it('creates the path', function() {
        //   expect($scope.audio_path("onething", "twothing")).toBe("onethingtwothing");

        // });

    });

});

<commit_msg>Cleanup code<commit_after>
describe('PlayerCtrl', function () {
  var   scope,
        mockSquareRepository,
        player = ["player"];

    beforeEach(function () {
        module("app");

        inject(function ($rootScope, $controller, SquareRepository) {
            scope = $rootScope.$new();

            mockSquareRepository = sinon.stub(SquareRepository);

            $controller("PlayerCtrl", {
                $scope: scope
            });

        });
    });

    // describe('when the controller first loads', function () {

        // it('the game is retrieved', function () {
        //     sinon.assert.calledOnce(mockFirebase.getGame);
        // });
        // it('puts the catalog on the scope', function() {
        //     expect(scope.catalog).toEqual(catalog);
        // });

    // });

    describe('when the game player page is loaded', function () {
        it('loads the first square', function () {
            sinon.assert.calledOnce(mockSquareRepository.getSquare);
        });

        it('loads all squares for the first lap', function () {
            // iterates through all the squares in SquareRepository
            // and displays them in player.
        });
    });

    describe('audio_path', function() {

        // it('creates the path', function() {
        //   expect($scope.audio_path("onething", "twothing")).toBe("onethingtwothing");

        // });

    });

});

<|endoftext|>