<commit_before>/**
 * @license
 * Copyright 2015 Google Inc. 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
 */

CLASS({
  package: 'foam.apps.builder.events',
  name: 'EventsDetailView',

  extendsModel: 'foam.ui.md.DetailView',

  requires: [
    'foam.ui.ImageView',
  ],

  properties: [
    ['className', 'events-detail-view'],
  ],

  templates: [
    function toHTML() {/*
      <div id="%%id" <%= this.cssClassAttr() %>>

          $$image{ model_: 'foam.ui.ImageView' }
          $$name{ model_: 'foam.ui.md.TextFieldView', floatingLabel: false, mode: 'read-only', extraClassName: 'md-subhead' }
          $$date{ model_: 'foam.ui.md.TextFieldView', floatingLabel: false, mode: 'read-only' }
          <div class="description-box" style="background: <%= this.data.color %>">
            $$description{ model_: 'foam.ui.md.TextFieldView', floatingLabel: false, mode: 'read-only' }

          $$tags{ model_: 'foam.ui.md.TextFieldView', floatingLabel: false, mode: 'read-only' }

    */},
    function CSS() {/*
      .events-detail-view {
        display: flex;
        flex-direction: column;
        padding: 16px;
      }

      .events-detail-view .description-box {
        background: #7777FF;
        color: white;
      }

    */},
  ],

});
<commit_msg>Patched up events packaing to include some missing views<commit_after>/**
 * @license
 * Copyright 2015 Google Inc. 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
 */

CLASS({
  package: 'foam.apps.builder.events',
  name: 'EventsDetailView',

  extendsModel: 'foam.ui.md.DetailView',

  requires: [
    'foam.ui.ImageView',
    'foam.ui.StringArrayView',
    'foam.ui.md.ColorFieldView',
    'foam.ui.DAOKeyView',
  ],

  properties: [
    ['className', 'events-detail-view'],
  ],

  templates: [
    function toHTML() {/*
      <div id="%%id" <%= this.cssClassAttr() %>>

          $$image{ model_: 'foam.ui.ImageView' }
          $$name{ model_: 'foam.ui.md.TextFieldView', floatingLabel: false, mode: 'read-only', extraClassName: 'md-subhead' }
          $$date{ model_: 'foam.ui.md.TextFieldView', floatingLabel: false, mode: 'read-only' }
          <div class="description-box" style="background: <%= this.data.color %>">
            $$description{ model_: 'foam.ui.md.TextFieldView', floatingLabel: false, mode: 'read-only' }

          $$tags{ model_: 'foam.ui.md.TextFieldView', floatingLabel: false, mode: 'read-only' }

    */},
    function CSS() {/*
      .events-detail-view {
        display: flex;
        flex-direction: column;
        padding: 16px;
      }

      .events-detail-view .description-box {
        background: #7777FF;
        color: white;
      }

    */},
  ],

});
<|endoftext|>


<commit_before>import React from 'react';
import PropTypes from 'prop-types';
import reactMixin from 'react-mixin';
import autoBind from 'react-autobind';
import Reflux from 'reflux';
import mixins from 'js/mixins';
import {stores} from 'js/stores';
import assetStore from 'js/assetStore';
import {actions} from 'js/actions';
import bem from 'js/bem';
import LoadingSpinner from 'js/components/common/loadingSpinner';
import {buildUserUrl} from 'utils';
import {
  ASSET_TYPES,
  ANON_USERNAME,
} from 'js/constants';
import './sharingForm.scss';
import {ROUTES} from 'js/router/routerConstants';
// parts
import CopyTeamPermissions from './copyTeamPermissions';
import UserAssetPermsEditor from './userAssetPermsEditor';
import PublicShareSettings from './publicShareSettings';
import UserPermissionRow from './userPermissionRow';
import {permParser} from './permParser';

class SharingForm extends React.Component {
  constructor(props) {
    super(props);
    autoBind(this);
    this.state = {
      allAssetsCount: 0,
      isAddUserEditorVisible: false,
    };
  }

  componentDidMount() {
    this.listenTo(assetStore, this.onAssetChange);
    this.listenTo(stores.allAssets, this.onAllAssetsChange);
    this.listenTo(actions.permissions.bulkSetAssetPermissions.completed, this.onAssetPermissionsUpdated);
    this.listenTo(actions.permissions.getAssetPermissions.completed, this.onAssetPermissionsUpdated);

    if (this.props.uid) {
      actions.resources.loadAsset({id: this.props.uid});
    }

    this.onAllAssetsChange();
  }

  onAllAssetsChange() {
    this.setState({allAssetsCount: Object.keys(stores.allAssets.byUid).length});
  }

  onAssetPermissionsUpdated(permissionAssignments) {
    const parsedPerms = permParser.parseBackendData(permissionAssignments, this.state.asset.owner, true);
    const anonUserUrl = buildUserUrl(ANON_USERNAME);
    const publicPerms = permissionAssignments.filter((assignment) => {
      return assignment.user === anonUserUrl;
    });
    const nonOwnerPerms = permParser.parseUserWithPermsList(parsedPerms).filter((perm) => {
      return perm.user !== buildUserUrl(this.state.asset.owner);
    });

    this.setState({
      permissions: parsedPerms,
      nonOwnerPerms: nonOwnerPerms,
      publicPerms: publicPerms,
    });
  }

  onAssetChange(data) {
    const uid = this.props.uid || this.currentAssetID;
    const asset = data[uid];

    if (asset) {
      this.setState({asset: asset});
    }

    this.setState({
      assignablePerms: this.getAssignablePermsMap(asset.assignable_permissions)
    });
    // we need to fetch permissions after asset has loaded,
    // as we need the owner username to parse permissions
    actions.permissions.getAssetPermissions(uid);
  }

  getAssignablePermsMap(backendPerms) {
    const assignablePerms = new Map();
    backendPerms.forEach((backendPerm) => {
      assignablePerms.set(backendPerm.url, backendPerm.label);
    });
    return assignablePerms;
  }

  toggleAddUserEditor() {
    this.setState({isAddUserEditorVisible: !this.state.isAddUserEditorVisible});
  }

  onPermissionsEditorSubmitEnd(isSuccess) {
    if (isSuccess) {
      this.setState({isAddUserEditorVisible: false});
    }
  }

  render() {
    if (!this.state.permissions) {
      return (<LoadingSpinner/>);
    }

    let uid = this.state.asset.uid,
        asset_type = this.state.asset.asset_type,
        objectUrl = this.state.asset.url;

    return (
      <bem.FormModal m='sharing-form'>
        <bem.Modal__subheader>
          {this.state.asset.name}
        </bem.Modal__subheader>

        {stores.session.currentAccount.extra_details?.require_auth !== true &&
          <bem.FormModal__item>
            <bem.FormView__cell m='warning'>

                {t('Anyone with the link can see this blank form and add submissions to it because you have not set ')}
                <a href={`/#${ROUTES.ACCOUNT_SETTINGS}`}>
                  {t('your account')}

                {t(' to require authentication.')}

            </bem.FormView__cell>
          </bem.FormModal__item>
        }

        {/* list of users and their permissions */}
        <bem.FormModal__item>

{t('Who has access')}

          {this.state.permissions.map((perm) => {
            // don't show anonymous user permissions in UI
            if (perm.user.name === ANON_USERNAME) {
              return null;
            }
            return <UserPermissionRow
              key={`perm.${uid}.${perm.user.name}`}
              uid={uid}
              nonOwnerPerms={this.state.nonOwnerPerms}
              assignablePerms={this.state.assignablePerms}
              assetType={asset_type}
              {...perm}
            />;
          })}

          {!this.state.isAddUserEditorVisible &&
            <bem.KoboButton
              m='blue'
              onClick={this.toggleAddUserEditor}
            >
              {t('Add user')}
            </bem.KoboButton>
          }

          {this.state.isAddUserEditorVisible &&
            <bem.FormModal__item m={['gray-row', 'copy-team-permissions']}>
              <bem.Button
                m='icon'
                className='user-permissions-editor-closer'
                onClick={this.toggleAddUserEditor}
              >

              </bem.Button>

              <UserAssetPermsEditor
                uid={uid}
                assignablePerms={this.state.assignablePerms}
                nonOwnerPerms={this.state.nonOwnerPerms}
                onSubmitEnd={this.onPermissionsEditorSubmitEnd}
              />

            </bem.FormModal__item>
          }
        </bem.FormModal__item>

        {/* public sharing settings */}
        { asset_type === ASSET_TYPES.survey.id &&
          <React.Fragment>
            <bem.Modal__hr/>

            <bem.FormModal__item m='share-settings'>

{t('Share publicly by link')}

              <PublicShareSettings
                publicPerms={this.state.publicPerms}
                uid={uid}
                objectUrl={objectUrl}
                deploymentActive={this.state.asset.deployment__active}
              />
            </bem.FormModal__item>
          </React.Fragment>
        }

        {/* copying permissions from other assets */}
        { asset_type !== ASSET_TYPES.collection.id && this.state.allAssetsCount === 0 &&
          <React.Fragment>
            <bem.Modal__hr/>
            {t('Waiting for all projects to load…')}
          </React.Fragment>
        }
        { asset_type !== ASSET_TYPES.collection.id && this.state.allAssetsCount >= 2 &&
          <React.Fragment>
            <bem.Modal__hr/>
            <CopyTeamPermissions uid={uid}/>
          </React.Fragment>
        }
      </bem.FormModal>
    );
  }
}

SharingForm.contextTypes = {router: PropTypes.object};

reactMixin(SharingForm.prototype, mixins.permissions);
reactMixin(SharingForm.prototype, mixins.contextRouter);
reactMixin(SharingForm.prototype, Reflux.ListenerMixin);

export default SharingForm;
<commit_msg>undo the message text change<commit_after>import React from 'react';
import PropTypes from 'prop-types';
import reactMixin from 'react-mixin';
import autoBind from 'react-autobind';
import Reflux from 'reflux';
import mixins from 'js/mixins';
import {stores} from 'js/stores';
import assetStore from 'js/assetStore';
import {actions} from 'js/actions';
import bem from 'js/bem';
import LoadingSpinner from 'js/components/common/loadingSpinner';
import {buildUserUrl} from 'utils';
import {
  ASSET_TYPES,
  ANON_USERNAME,
} from 'js/constants';
import './sharingForm.scss';
import {ROUTES} from 'js/router/routerConstants';
// parts
import CopyTeamPermissions from './copyTeamPermissions';
import UserAssetPermsEditor from './userAssetPermsEditor';
import PublicShareSettings from './publicShareSettings';
import UserPermissionRow from './userPermissionRow';
import {permParser} from './permParser';

class SharingForm extends React.Component {
  constructor(props) {
    super(props);
    autoBind(this);
    this.state = {
      allAssetsCount: 0,
      isAddUserEditorVisible: false,
    };
  }

  componentDidMount() {
    this.listenTo(assetStore, this.onAssetChange);
    this.listenTo(stores.allAssets, this.onAllAssetsChange);
    this.listenTo(actions.permissions.bulkSetAssetPermissions.completed, this.onAssetPermissionsUpdated);
    this.listenTo(actions.permissions.getAssetPermissions.completed, this.onAssetPermissionsUpdated);

    if (this.props.uid) {
      actions.resources.loadAsset({id: this.props.uid});
    }

    this.onAllAssetsChange();
  }

  onAllAssetsChange() {
    this.setState({allAssetsCount: Object.keys(stores.allAssets.byUid).length});
  }

  onAssetPermissionsUpdated(permissionAssignments) {
    const parsedPerms = permParser.parseBackendData(permissionAssignments, this.state.asset.owner, true);
    const anonUserUrl = buildUserUrl(ANON_USERNAME);
    const publicPerms = permissionAssignments.filter((assignment) => {
      return assignment.user === anonUserUrl;
    });
    const nonOwnerPerms = permParser.parseUserWithPermsList(parsedPerms).filter((perm) => {
      return perm.user !== buildUserUrl(this.state.asset.owner);
    });

    this.setState({
      permissions: parsedPerms,
      nonOwnerPerms: nonOwnerPerms,
      publicPerms: publicPerms,
    });
  }

  onAssetChange(data) {
    const uid = this.props.uid || this.currentAssetID;
    const asset = data[uid];

    if (asset) {
      this.setState({asset: asset});
    }

    this.setState({
      assignablePerms: this.getAssignablePermsMap(asset.assignable_permissions)
    });
    // we need to fetch permissions after asset has loaded,
    // as we need the owner username to parse permissions
    actions.permissions.getAssetPermissions(uid);
  }

  getAssignablePermsMap(backendPerms) {
    const assignablePerms = new Map();
    backendPerms.forEach((backendPerm) => {
      assignablePerms.set(backendPerm.url, backendPerm.label);
    });
    return assignablePerms;
  }

  toggleAddUserEditor() {
    this.setState({isAddUserEditorVisible: !this.state.isAddUserEditorVisible});
  }

  onPermissionsEditorSubmitEnd(isSuccess) {
    if (isSuccess) {
      this.setState({isAddUserEditorVisible: false});
    }
  }

  render() {
    if (!this.state.permissions) {
      return (<LoadingSpinner/>);
    }

    let uid = this.state.asset.uid,
        asset_type = this.state.asset.asset_type,
        objectUrl = this.state.asset.url;

    return (
      <bem.FormModal m='sharing-form'>
        <bem.Modal__subheader>
          {this.state.asset.name}
        </bem.Modal__subheader>

        {stores.session.currentAccount.extra_details?.require_auth !== true &&
          <bem.FormModal__item>
            <bem.FormView__cell m='warning'>

                {t('Anyone can see this blank form and add submissions to it because you have not set ')}
                <a href={`/#${ROUTES.ACCOUNT_SETTINGS}`}>
                  {t('your account')}

                {t(' to require authentication.')}

            </bem.FormView__cell>
          </bem.FormModal__item>
        }

        {/* list of users and their permissions */}
        <bem.FormModal__item>

{t('Who has access')}

          {this.state.permissions.map((perm) => {
            // don't show anonymous user permissions in UI
            if (perm.user.name === ANON_USERNAME) {
              return null;
            }
            return <UserPermissionRow
              key={`perm.${uid}.${perm.user.name}`}
              uid={uid}
              nonOwnerPerms={this.state.nonOwnerPerms}
              assignablePerms={this.state.assignablePerms}
              assetType={asset_type}
              {...perm}
            />;
          })}

          {!this.state.isAddUserEditorVisible &&
            <bem.KoboButton
              m='blue'
              onClick={this.toggleAddUserEditor}
            >
              {t('Add user')}
            </bem.KoboButton>
          }

          {this.state.isAddUserEditorVisible &&
            <bem.FormModal__item m={['gray-row', 'copy-team-permissions']}>
              <bem.Button
                m='icon'
                className='user-permissions-editor-closer'
                onClick={this.toggleAddUserEditor}
              >

              </bem.Button>

              <UserAssetPermsEditor
                uid={uid}
                assignablePerms={this.state.assignablePerms}
                nonOwnerPerms={this.state.nonOwnerPerms}
                onSubmitEnd={this.onPermissionsEditorSubmitEnd}
              />

            </bem.FormModal__item>
          }
        </bem.FormModal__item>

        {/* public sharing settings */}
        { asset_type === ASSET_TYPES.survey.id &&
          <React.Fragment>
            <bem.Modal__hr/>

            <bem.FormModal__item m='share-settings'>

{t('Share publicly by link')}

              <PublicShareSettings
                publicPerms={this.state.publicPerms}
                uid={uid}
                objectUrl={objectUrl}
                deploymentActive={this.state.asset.deployment__active}
              />
            </bem.FormModal__item>
          </React.Fragment>
        }

        {/* copying permissions from other assets */}
        { asset_type !== ASSET_TYPES.collection.id && this.state.allAssetsCount === 0 &&
          <React.Fragment>
            <bem.Modal__hr/>
            {t('Waiting for all projects to load…')}
          </React.Fragment>
        }
        { asset_type !== ASSET_TYPES.collection.id && this.state.allAssetsCount >= 2 &&
          <React.Fragment>
            <bem.Modal__hr/>
            <CopyTeamPermissions uid={uid}/>
          </React.Fragment>
        }
      </bem.FormModal>
    );
  }
}

SharingForm.contextTypes = {router: PropTypes.object};

reactMixin(SharingForm.prototype, mixins.permissions);
reactMixin(SharingForm.prototype, mixins.contextRouter);
reactMixin(SharingForm.prototype, Reflux.ListenerMixin);

export default SharingForm;
<|endoftext|>


<commit_before>gSimulator.ACCEPT = "ACCEPT";
gSimulator.REJECT = "REJECT";
gSimulator.NEITHER = "NEITHER";

gSimulator.runTests = function () {
  var inputs = [];
  var illegalsExist = false;

  d3.selectAll (".bulkInput").each (function () {
    var test = intersection (this.value, gGraph.charSet);
    illegalsExist |= test.length != this.value.length;
    this.value = test;
    inputs.push ({
      testCase: test,
      expected: gSimulator.NEITHER
    });
  });
  d3.selectAll (".bulkAccept").each (function (junk, i) {
    if (this.checked) {
      inputs[i].expected = gSimulator.ACCEPT;
    }
  });
  d3.selectAll (".bulkReject").each (function (junk, i) {
    if (this.checked) {
      inputs[i].expected = gSimulator.REJECT;
    }
  });

  if (illegalsExist) {
    gErrorMenu.displayModalError ("testing", "Removing characters not in alphabet");
  }

  var results = [];
  var graph = gGraph.save ();
  inputs.forEach (function (input) {
    results.push ({
        accepted: gSimulator.run (graph, input.testCase),
        input: gSimulator.output,
        expected: input.expected
      });
  });

  var rows = d3.select (".testingResults")
    .selectAll ("tr")
    .data (results);

  var entered = rows.enter ()
    .append ("tr");
  entered.append ("td")
    .attr ("width", "60%")
    .style ("text-align", "left");
  entered.append ("td")
    .attr ("width", "20%")
    .style ("text-align", "center");
  entered.append ("td")
    .attr ("width", "20%")
    .style ("text-align", "center");

  rows.each (function (result) {
      d3.select (this).selectAll ("td").text (function (junk, i) {
        switch (i) {
          case 0:
            return result.input;
          case 1:
            return result.accepted ? "A" : "R";
          case 2:
            if (result.expected === gSimulator.NEITHER) {
              return "-";
            } else {
              var correct = !((result.expected === gSimulator.ACCEPT) ^ result.accepted);
              return correct ? "Y" : "N";
            }
        }
      });
    });

  rows.exit ().remove ();
};<commit_msg>Added warning about timeout<commit_after>gSimulator.ACCEPT = "ACCEPT";
gSimulator.REJECT = "REJECT";
gSimulator.NEITHER = "NEITHER";

gSimulator.runTests = function () {
  var inputs = [];
  var illegalsExist = false;

  d3.selectAll (".bulkInput").each (function () {
    var test = intersection (this.value, gGraph.charSet);
    illegalsExist |= test.length != this.value.length;
    this.value = test;
    inputs.push ({
      testCase: test,
      expected: gSimulator.NEITHER
    });
  });
  d3.selectAll (".bulkAccept").each (function (junk, i) {
    if (this.checked) {
      inputs[i].expected = gSimulator.ACCEPT;
    }
  });
  d3.selectAll (".bulkReject").each (function (junk, i) {
    if (this.checked) {
      inputs[i].expected = gSimulator.REJECT;
    }
  });

  if (illegalsExist) {
    gErrorMenu.displayModalError ("testing", "Removing characters not in alphabet");
  }

  var results = [];
  var graph = gGraph.save ();
  inputs.forEach (function (input) {
    results.push ({
        accepted: gSimulator.run (graph, input.testCase),
        input: gSimulator.output,
        expected: input.expected
      });
  });

  var rows = d3.select (".testingResults")
    .selectAll ("tr")
    .data (results);

  var entered = rows.enter ()
    .append ("tr");
  entered.append ("td")
    .attr ("width", "60%")
    .style ("text-align", "left");
  entered.append ("td")
    .attr ("width", "20%")
    .style ("text-align", "center");
  entered.append ("td")
    .attr ("width", "20%")
    .style ("text-align", "center");

  rows.each (function (result) {
      d3.select (this).selectAll ("td").text (function (junk, i) {
        switch (i) {
          case 0:
            return result.accepted == gSimulator.TIMEOUT ? "Timed out" : result.input;
          case 1:
            if (result.accepted == gSimulator.TIMEOUT) {
              return "-";
            }
            return result.accepted ? "A" : "R";
          case 2:
            if (result.expected === gSimulator.NEITHER || result.accepted == gSimulator.TIMEOUT) {
              return "-";
            } else {
              var correct = !((result.expected === gSimulator.ACCEPT) ^ result.accepted);
              return correct ? "Y" : "N";
            }
        }
      });
    });

  rows.exit ().remove ();
};<|endoftext|>


<commit_before>import Immutable from 'immutable'
import {
  EVENT_META,
  EVENT_META_TEXT,
  EVENT_MIDI_NOTE_ON,
  EVENT_MIDI_NOTE_OFF,
  createParser as createEventParser,
} from 'midievents'
import {
  PLAYING,
  PAUSED,
  STOPPED,
} from '../constants/playbackStates'
import {
  MIDI,
} from '../constants/resourceTypes'
import MIDIFile from 'midifile'
import MIDIPlayer from 'midiplayer'

export default class Player {
  constructor(actions) {
    this.actions = actions
    this.ctx = new AudioContext()
    this.tracks = new Map()
    this.midiPlayers = new Map()
  }

  update(state) {
    const tracks = state.tracks.get(state.song, Immutable.Map())

    // clean up removed/replaced tracks
    for (let [ trackKey, trackState ] of this.tracks) {
      if (!tracks.has(trackKey) || trackState.module !== tracks.get(trackKey).module) {
        trackState.gainNode.disconnect()
        trackState.analyzerNode.disconnect()
        trackState.spyNode.disconnect()
        this.tracks.delete(trackKey)
      }
    }

    // create new tracks
    if (state.loaded) {
      for (let [ trackKey, track ] of tracks) {
        if (!this.tracks.has(trackKey)) {
          this.tracks.set(trackKey, this._createTrack(trackKey, state, track))
        }
      }
    }

    // take stock of needed MIDI players
    var usedMIDI = new Set()
    for (let [ trackKey, trackState ] of this.tracks) {
      for (let [ midiURL, ] of trackState.midis) {
        usedMIDI.add(midiURL)
      }
    }

    // create new MIDI players
    let songDuration = 0
    for (let midiURL of usedMIDI) {
      if (!this.midiPlayers.has(midiURL)) {
        const resource = state.resources.get(midiURL)
        if (!resource.data) {
          continue
        }
        let midiFile = new MIDIFile(resource.data)
        let midiPlayer = new MIDIPlayer({
          output: { send: this._handleMIDIEvent.bind(this, midiURL) },
        })
        midiPlayer.load(midiFile)
        const midiDuration = midiPlayer.events[midiPlayer.events.length - 1].playTime
        songDuration = Math.max(songDuration, midiDuration)
        this.midiPlayers.set(midiURL, midiPlayer)
      }
    }

    this.timeOffset = performance.now() / 1000 - this.ctx.currentTime
    for (let [ midiURL, midiPlayer ] of this.midiPlayers) {
      if (!usedMIDI.has(midiURL)) {
        // remove unused MIDI players
        midiPlayer.stop()
        this.midiPlayers.delete(midiURL)
      } else {
        // synchronize MIDI player state
        if (state.state === PLAYING && state.loaded) {
          midiPlayer.stop()
          midiPlayer.play()
          midiPlayer.startTime = state.startTime
        } else {
          midiPlayer.stop()
        }
      }
    }

    this.actions.playerUpdateFinish(songDuration)
  }

  _createTrack(trackKey, state, track) {
    const gainNode = this.ctx.createGain()
    gainNode.connect(this.ctx.destination)

    const analyzerNode = this.ctx.createScriptProcessor(0, 2, 2)
    analyzerNode.addEventListener('audioprocess', this._handleTrackAudio.bind(this, trackKey))
    analyzerNode.connect(this.ctx.destination)

    const spyNode = this.ctx.createGain()
    spyNode.connect(gainNode)
    spyNode.connect(analyzerNode)

    let trackState = {
      module: track.module,
      gainNode,
      analyzerNode,
      spyNode,
      midis: new Map(),
    }

    const trackTransport = {
      playMIDI(resource, onEvent) {
        if (!resource.type === MIDI) {
          throw new Error('playMIDI requires a resource of type MIDI')
        }
        trackState.midis.set(resource.url, onEvent)
      }
    }

    const trackResources = track.resources.map(res =>
      res.toMap().set('data', state.resources.get(res.url).data)
    ).toJS()

    track.module.create({
      transport: trackTransport,
      res: trackResources,
      ctx: this.ctx,
      out: spyNode,
    })

    return trackState
  }

  _handleMIDIEvent(midiURL, data, time) {
    // FIXME: work around midievents expecting more than one event.
    // also 3rd and 7th arguments are placeholder times. should pull req
    // midievents to make it easier to handle single events, or modify midiplayer.
    data.splice(0, 0, 0, EVENT_META, EVENT_META_TEXT, 0, 0)
    const dataView = new DataView(new Uint8Array(data).buffer)
    const parser = createEventParser(dataView)
    parser.next()
    const ev = parser.next()
    delete ev.index
    delete ev.delta
    ev.time = time / 1000 - this.timeOffset
    ev.midi = {data, time}

    if (ev.time <= this.ctx.currentTime) {
      // skip events in the past (due to seek, etc.)
      return
    }

    if (ev.type === EVENT_MIDI_NOTE_ON || ev.type === EVENT_MIDI_NOTE_OFF) {
      ev.note = ev.param1
      // https://en.wikipedia.org/wiki/MIDI_Tuning_Standard#Frequency_values
      ev.frequency = Math.pow(2, (ev.note - 69) / 12) * 440
    }

    for (let [trackKey, track] of this.tracks) {
      const subscription = track.midis.get(midiURL)
      if (subscription) {
        subscription(Object.assign({}, ev))
      }
    }
  }

  _handleTrackAudio(trackKey, ev) {
    const sampleCount = 100
    const levels = {}
    const buf = ev.inputBuffer
    for (let c = 0; c < buf.numberOfChannels; c++) {
      const data = buf.getChannelData(c)
      const step = Math.floor(data.length / sampleCount)
      let sum = 0
      let max = 0
      for (let i = 0; i < data.length; i += step) {
        sum += Math.abs(data[i])
        max = Math.max(max, data[i])
      }
      const avg = sum / sampleCount

      const channel = c === 0 ? 'left' : 'right'
      levels[channel + 'Avg'] = avg
      levels[channel + 'Max'] = max
      levels[channel + 'Clip'] = max > 1
    }

    this.actions.updateTrackLevels(trackKey, levels)
  }
}
<commit_msg>Fix song duration determination when midi files stay the same<commit_after>import Immutable from 'immutable'
import {
  EVENT_META,
  EVENT_META_TEXT,
  EVENT_MIDI_NOTE_ON,
  EVENT_MIDI_NOTE_OFF,
  createParser as createEventParser,
} from 'midievents'
import {
  PLAYING,
  PAUSED,
  STOPPED,
} from '../constants/playbackStates'
import {
  MIDI,
} from '../constants/resourceTypes'
import MIDIFile from 'midifile'
import MIDIPlayer from 'midiplayer'

export default class Player {
  constructor(actions) {
    this.actions = actions
    this.ctx = new AudioContext()
    this.tracks = new Map()
    this.midiPlayers = new Map()
  }

  update(state) {
    const tracks = state.tracks.get(state.song, Immutable.Map())

    // clean up removed/replaced tracks
    for (let [ trackKey, trackState ] of this.tracks) {
      if (!tracks.has(trackKey) || trackState.module !== tracks.get(trackKey).module) {
        trackState.gainNode.disconnect()
        trackState.analyzerNode.disconnect()
        trackState.spyNode.disconnect()
        this.tracks.delete(trackKey)
      }
    }

    // create new tracks
    if (state.loaded) {
      for (let [ trackKey, track ] of tracks) {
        if (!this.tracks.has(trackKey)) {
          this.tracks.set(trackKey, this._createTrack(trackKey, state, track))
        }
      }
    }

    // take stock of needed MIDI players
    var usedMIDI = new Set()
    for (let [ trackKey, trackState ] of this.tracks) {
      for (let [ midiURL, ] of trackState.midis) {
        usedMIDI.add(midiURL)
      }
    }

    // create new MIDI players
    let songDuration = 0
    for (let midiURL of usedMIDI) {
      if (!this.midiPlayers.has(midiURL)) {
        const resource = state.resources.get(midiURL)
        if (!resource.data) {
          continue
        }
        let midiFile = new MIDIFile(resource.data)
        let midiPlayer = new MIDIPlayer({
          output: { send: this._handleMIDIEvent.bind(this, midiURL) },
        })
        midiPlayer.load(midiFile)
        this.midiPlayers.set(midiURL, midiPlayer)
      }
      const midiEvents = this.midiPlayers.get(midiURL).events
      const midiDuration = midiEvents[midiEvents.length - 1].playTime
      songDuration = Math.max(songDuration, midiDuration)
    }

    this.timeOffset = performance.now() / 1000 - this.ctx.currentTime
    for (let [ midiURL, midiPlayer ] of this.midiPlayers) {
      if (!usedMIDI.has(midiURL)) {
        // remove unused MIDI players
        midiPlayer.stop()
        this.midiPlayers.delete(midiURL)
      } else {
        // synchronize MIDI player state
        if (state.state === PLAYING && state.loaded) {
          midiPlayer.stop()
          midiPlayer.play()
          midiPlayer.startTime = state.startTime
        } else {
          midiPlayer.stop()
        }
      }
    }

    this.actions.playerUpdateFinish(songDuration)
  }

  _createTrack(trackKey, state, track) {
    const gainNode = this.ctx.createGain()
    gainNode.connect(this.ctx.destination)

    const analyzerNode = this.ctx.createScriptProcessor(0, 2, 2)
    analyzerNode.addEventListener('audioprocess', this._handleTrackAudio.bind(this, trackKey))
    analyzerNode.connect(this.ctx.destination)

    const spyNode = this.ctx.createGain()
    spyNode.connect(gainNode)
    spyNode.connect(analyzerNode)

    let trackState = {
      module: track.module,
      gainNode,
      analyzerNode,
      spyNode,
      midis: new Map(),
    }

    const trackTransport = {
      playMIDI(resource, onEvent) {
        if (!resource.type === MIDI) {
          throw new Error('playMIDI requires a resource of type MIDI')
        }
        trackState.midis.set(resource.url, onEvent)
      }
    }

    const trackResources = track.resources.map(res =>
      res.toMap().set('data', state.resources.get(res.url).data)
    ).toJS()

    track.module.create({
      transport: trackTransport,
      res: trackResources,
      ctx: this.ctx,
      out: spyNode,
    })

    return trackState
  }

  _handleMIDIEvent(midiURL, data, time) {
    // FIXME: work around midievents expecting more than one event.
    // also 3rd and 7th arguments are placeholder times. should pull req
    // midievents to make it easier to handle single events, or modify midiplayer.
    data.splice(0, 0, 0, EVENT_META, EVENT_META_TEXT, 0, 0)
    const dataView = new DataView(new Uint8Array(data).buffer)
    const parser = createEventParser(dataView)
    parser.next()
    const ev = parser.next()
    delete ev.index
    delete ev.delta
    ev.time = time / 1000 - this.timeOffset
    ev.midi = {data, time}

    if (ev.time <= this.ctx.currentTime) {
      // skip events in the past (due to seek, etc.)
      return
    }

    if (ev.type === EVENT_MIDI_NOTE_ON || ev.type === EVENT_MIDI_NOTE_OFF) {
      ev.note = ev.param1
      // https://en.wikipedia.org/wiki/MIDI_Tuning_Standard#Frequency_values
      ev.frequency = Math.pow(2, (ev.note - 69) / 12) * 440
    }

    for (let [trackKey, track] of this.tracks) {
      const subscription = track.midis.get(midiURL)
      if (subscription) {
        subscription(Object.assign({}, ev))
      }
    }
  }

  _handleTrackAudio(trackKey, ev) {
    const sampleCount = 100
    const levels = {}
    const buf = ev.inputBuffer
    for (let c = 0; c < buf.numberOfChannels; c++) {
      const data = buf.getChannelData(c)
      const step = Math.floor(data.length / sampleCount)
      let sum = 0
      let max = 0
      for (let i = 0; i < data.length; i += step) {
        sum += Math.abs(data[i])
        max = Math.max(max, data[i])
      }
      const avg = sum / sampleCount

      const channel = c === 0 ? 'left' : 'right'
      levels[channel + 'Avg'] = avg
      levels[channel + 'Max'] = max
      levels[channel + 'Clip'] = max > 1
    }

    this.actions.updateTrackLevels(trackKey, levels)
  }
}
<|endoftext|>


<commit_before>describe("src/organism/Organism", () => {
    let Organism = require('../../../src/organism/Organism').default;
    let Config   = require('../../../src/global/Config').Config;

    it("Checking organism creation", () => {
        let org = new Organism(0, 1, 2, true, null);

        expect(org.id).toEqual(0);
        expect(org.x).toEqual(1);
        expect(org.y).toEqual(2);
        expect(org.alive).toEqual(true);
    });

    it("Checking organism destroy because of age", () => {
        let org = new Organism(0, 1, 2, true, null, ()=>{});

        for (let i = 0; i < Config.orgAlivePeriod; i++) {
            expect(org.alive).toEqual(true);
            org.run(i);
        }

        expect(org.alive).toEqual(true);
        org.run(Config.orgAlivePeriod);
        expect(org.alive).toEqual(false);
    });

    it("Checking organism destroy because of zero energy", () => {
        Config.orgAlivePeriod       = Config.orgStartEnergy + 1;
        Config.orgEnergySpendPeriod = 1;
        let org = new Organism(0, 1, 2, true, null, ()=>{});

        for (let i = 0; i < Config.orgStartEnergy; i++) {
            org.run();
        }

        expect(org.alive).toEqual(false);
    });

    it("Checking grabbing energy", () => {
        let energy = Config.orgStartEnergy;
        let org    = new Organism(0, 1, 2, true, null, ()=>{});

        org.grabEnergy(10);
        expect(org.energy).toEqual(energy - 10);
    });

    it("Checking destroy() method", () => {
        let org = new Organism(0, 1, 2, true, null, ()=>{});

        expect(org.alive).toEqual(true);
        expect(org.energy > 0).toEqual(true);
        org.destroy();
        expect(org.alive).toEqual(false);
        expect(org.energy > 0).toEqual(false);
    });
});<commit_msg>new tests (in progress) #14<commit_after>describe("src/organism/Organism", () => {
    let Organism = require('../../../src/organism/Organism').default;
    let Config   = require('../../../src/global/Config').Config;
    let api      = require('../../../src/global/Config').api;
    let cls;

    beforeEach(() => {cls = Config.codeOperatorsCls;api.set('codeOperatorsCls', 'ops')});
    afterEach(() => api.set('codeOperatorsCls', cls));

    it("Checking organism creation", () => {
        const clss = {ops: ()=>{}};
        let   org  = new Organism(0, 1, 2, true, null, ()=>{}, clss);

        expect(org.id).toEqual(0);
        expect(org.x).toEqual(1);
        expect(org.y).toEqual(2);
        expect(org.item).toEqual(null);
        expect(org.alive).toEqual(true);

        org.destroy();
    });

    it("Checking organism destroy because of age", () => {
        const clss = {ops: ()=>{}};
        let   org  = new Organism(0, 1, 2, true, null, ()=>{}, clss);

        for (let i = 0; i < Config.orgAlivePeriod; i++) {
            expect(org.alive).toEqual(true);
            org.run();
        }

        expect(org.alive).toEqual(true);
        org.run();
        expect(org.alive).toEqual(false);

        org.destroy();
    });
    //
    // it("Checking organism destroy because of zero energy", () => {
    //     Config.orgAlivePeriod       = Config.orgStartEnergy + 1;
    //     Config.orgEnergySpendPeriod = 1;
    //     let org = new Organism(0, 1, 2, true, null, ()=>{});
    //
    //     for (let i = 0; i < Config.orgStartEnergy; i++) {
    //         org.run();
    //     }
    //
    //     expect(org.alive).toEqual(false);
    // });
    //
    // it("Checking grabbing energy", () => {
    //     let energy = Config.orgStartEnergy;
    //     let org    = new Organism(0, 1, 2, true, null, ()=>{});
    //
    //     org.grabEnergy(10);
    //     expect(org.energy).toEqual(energy - 10);
    // });
    //
    // it("Checking destroy() method", () => {
    //     let org = new Organism(0, 1, 2, true, null, ()=>{});
    //
    //     expect(org.alive).toEqual(true);
    //     expect(org.energy > 0).toEqual(true);
    //     org.destroy();
    //     expect(org.alive).toEqual(false);
    //     expect(org.energy > 0).toEqual(false);
    // });
});<|endoftext|>


<commit_before>import m from 'mithril';
import prop from 'mithril/stream';
import _ from 'underscore';
import {
    catarse,
    commonPayment
} from '../api';
import models from '../models';
import h from '../h';
import loadMoreBtn from '../c/load-more-btn';
import filterText from '../c/filter-text';
import FilterDropdown from '../c/filter-dropdown';
import filterDropdownNumberRange from '../c/filter-dropdown-number-range';
import projectDashboardMenu from '../c/project-dashboard-menu';
import dashboardSubscriptionCard from '../c/dashboard-subscription-card';
import projectsSubscriptionReportVM from '../vms/projects-subscription-report-vm';
import projectsContributionReportVM from '../vms/projects-contribution-report-vm';
import projectSubscriptionsListVM from '../vms/project-subscriptions-list-vm';

const statusCustomFilter = {
    view: () => m('.fontsize-smaller.u-text-center', [
        'Status ',
        m('a.fontsize-smallest.tooltip-wrapper.fa.fa-question-circle.fontcolor-secondary', {
            href: 'https://suporte.catarse.me/hc/pt-br/articles/360024090391-O-que-significa-cada-status-das-assinaturas-e-pagamentos-',
            target: '_blank'
        })
    ])
};

const dropdownFilterCustomLabel = {
    view: function({attrs})
    {
        return m('.fontsize-smaller.u-text-center', attrs.label);
    }
};

const projectSubscriptionReport = {
    oninit: function (vnode) {
        const filterVM = projectsSubscriptionReportVM,
            catarseVM = projectsContributionReportVM,
            dropdownNumber = prop(0),
            error = prop(false),
            loader = prop(true),
            isProjectDataLoaded = prop(false),
            isRewardsDataLoaded = prop(false),
            rewards = prop([]),
            subscriptions = projectSubscriptionsListVM(),
            submit = () => {
                // Set order by last paid on filters too
                filterVM.order({ last_payment_data_created_at: 'desc' });
                if (filterVM.reward_external_id() === 'null') {
                    subscriptions.firstPage(filterVM.withNullParameters());
                } else {
                    subscriptions.firstPage(filterVM.parameters());
                }

                return false;
            },
            filterBuilder = [{
                component: filterText,
                label: 'text_filter',
                data: {
                    label: 'Nome ou email',
                    vm: filterVM.search_index,
                    onchange: submit,
                    wrapper_class: '.u-marginbottom-20.w-col.w-col-3',
                    placeholder: 'Busque por assinantes',
                    onclick: submit
                }
            },
            {
                label: 'status_filter',
                component: FilterDropdown,
                data: {
                    custom_label: [
                        statusCustomFilter,
                        null
                    ],
                    onchange: submit,
                    name: 'status',
                    vm: filterVM.status,
                    wrapper_class: '.w-col.w-col-3',
                    options: [{
                        value: '',
                        option: 'Todos'
                    },
                    {
                        value: 'active',
                        option: 'Ativa'
                    },
                    {
                        value: 'started',
                        option: 'Iniciada'
                    },
                    {
                        value: 'canceling',
                        option: 'Cancelamento solicitado'
                    },
                    {
                        value: 'canceled',
                        option: 'Cancelada'
                    },
                    {
                        value: 'inactive',
                        option: 'Inativa'
                    }
                    ]
                }
            },
            {
                label: 'reward_filter',
                component: FilterDropdown,
                data: {
                    custom_label: [
                        dropdownFilterCustomLabel,
                        {label: 'Recompensa'}
                    ],
                    onchange: submit,
                    name: 'reward_external_id',
                    vm: filterVM.reward_external_id,
                    wrapper_class: '.w-col.w-col-3',
                    options: []
                }
            },
            {
                label: 'payment_filter',
                component: FilterDropdown,
                data: {
                    custom_label: [
                        dropdownFilterCustomLabel,
                        {label: 'Meio de pgto.'}
                    ],
                    onchange: submit,
                    name: 'payment_method',
                    vm: filterVM.payment_method,
                    wrapper_class: '.w-col.w-col-2',
                    options: [{
                        value: '',
                        option: 'Todos'
                    },
                    {
                        value: 'credit_card',
                        option: 'Cartão de crédito'
                    },
                    {
                        value: 'boleto',
                        option: 'Boleto'
                    }
                    ]
                }
            },
            {
                label: 'total_paid_filter',
                component: filterDropdownNumberRange,
                data: {
                    index: 1,
                    selectable: dropdownNumber,
                    label: 'Total apoiado',
                    name: 'total_paid',
                    onapply: submit,
                    vm: filterVM.total_paid,
                    wrapper_class: '.w-col.w-col-2',
                    init_lower_value: '0',
                    init_higher_value: 'mais',
                    value_change_placeholder: 'R$#V1 ou #V2',
                    value_change_both_placeholder: 'R$#V1 a R$#V2',
                    inner_field_placeholder: '0',
                    inner_field_label: 'R$',
                    value_multiplier: 100,
                    min: 0
                }
            },
            {
                label: 'paid_count_filter',
                component: filterDropdownNumberRange,
                data: {
                    index: 2,
                    selectable: dropdownNumber,
                    label: 'Qtde. de apoios',
                    name: 'paid_count',
                    onapply: submit,
                    vm: filterVM.paid_count,
                    wrapper_class: '.w-col.w-col-2',
                    init_lower_value: '0',
                    init_higher_value: 'mais',
                    value_change_placeholder: '#V1 ou #V2',
                    value_change_both_placeholder: '#V1 a #V2',
                    inner_field_placeholder: '0',
                    value_multiplier: 1,
                    min: 0,
                    dropdown_inline_style: {
                        right: '0'
                    }
                }
            }

            ],
            handleError = () => {
                error(true);
                loader(false);
                isProjectDataLoaded(true);
                m.redraw();
            },
            project = prop([{}]);

        catarseVM.project_id(vnode.attrs.project_id);

        const lReward = catarse.loaderWithToken(models.rewardDetail.getPageOptions({
            project_id: `eq.${catarseVM.project_id()}`
        }));

        lReward.load().then((loadedRewards) => {
            rewards(loadedRewards);
            isRewardsDataLoaded(true);
            m.redraw();
        });
        const mapRewardsToOptions = () => {
            let options = [];
            if (!lReward()) {
                options = _.map(rewards(), r => ({
                    value: r.id,
                    option: `R$ ${h.formatNumber(r.minimum_value, 2, 3)} - ${(r.title ? r.title : r.description).substring(0, 20)}`
                }));
            }

            options.unshift({
                value: null,
                option: 'Sem recompensa'
            });

            options.unshift({
                value: '',
                option: 'Todas'
            });

            return options;
        };

        const lProject = catarse.loaderWithToken(models.projectDetail.getPageOptions({
            project_id: `eq.${catarseVM.project_id()}`
        }));

        lProject.load().then((data) => {
            filterVM.project_id(_.first(data).common_id);
            // override default 'created_at' order on vm
            filterVM.order({ last_payment_data_created_at: 'desc' });
            subscriptions.firstPage(filterVM.parameters()).then(result => {
                loader(false);
                isProjectDataLoaded(true);
                m.redraw();
            }).catch(err => {
                handleError(err);
                m.redraw();
            });
            project(data);
            m.redraw();
        });

        vnode.state = {
            filterVM,
            mapRewardsToOptions,
            filterBuilder,
            submit,
            subscriptions,
            lProject,
            project,
            isProjectDataLoaded,
            isRewardsDataLoaded
        };
    },
    view: function ({state, attrs}) {
        const subsCollection = state.subscriptions.collection(),
            filterBuilder = state.filterBuilder,
            statusFilter = _.findWhere(filterBuilder, {
                label: 'status_filter'
            }),
            textFilter = _.findWhere(filterBuilder, {
                label: 'text_filter'
            }),
            rewardFilter = _.findWhere(filterBuilder, {
                label: 'reward_filter'
            }),
            paymentFilter = _.findWhere(filterBuilder, {
                label: 'payment_filter'
            }),
            totalPaidFilter = _.findWhere(filterBuilder, {
                label: 'total_paid_filter'
            }),
            paidCountFilter = _.findWhere(filterBuilder, {
                label: 'paid_count_filter'
            });
        rewardFilter.data.options = state.mapRewardsToOptions();
        if (state.isProjectDataLoaded() && state.isRewardsDataLoaded()) {
            return m('div', [
                m(projectDashboardMenu, {
                    project: prop(_.first(state.project()))
                }),
                m('.dashboard-header', [
                    m('.w-container',
                        m('.w-row', [
                            m('.w-col.w-col-3'),
                            m('.w-col.w-col-6',
                                m('.fontsize-larger.fontweight-semibold.lineheight-looser.u-marginbottom-30.u-text-center',
                                    'Base de assinantes'
                                )
                            ),
                            m('.w-col.w-col-3')
                        ])
                    ),
                    m('.u-marginbottom-30.w-container',
                        m('.w-form', [
                            m('form', {
                                onsubmit: state.submit
                            },
                                m('w-row', [
                                    m(textFilter.component, textFilter.data),
                                    m('.w-col.w-col-9',
                                        m('.w-row', [
                                            m(statusFilter.component, statusFilter.data),
                                            m(rewardFilter.component, rewardFilter.data),
                                            m(paymentFilter.component, paymentFilter.data),
                                            m(totalPaidFilter.component, totalPaidFilter.data),
                                            m(paidCountFilter.component, paidCountFilter.data),
                                        ])
                                    )
                                ])
                            )
                        ])
                    )
                ]),
                m('.divider'),
                m('.before-footer.bg-gray.section', [
                    m('.w-container', [
                        m('div',
                            m('.w-row', [
                                m('.u-marginbottom-20.u-text-center-small-only.w-col.w-col-6',
                                    m('.w-inline-block.fontsize-base.u-marginright-10', [
                                        m('span.fontweight-semibold',
                                            state.subscriptions.total()
                                        ),
                                        ' pessoas',
                                        m.trust(' ')
                                    ])
                                ),
                                m('.w-col.w-col-6',
                                    m(`a.alt-link.fontsize-small.u-right[href='/projects/${attrs.project_id}/subscriptions_report_download']`, {
                                        oncreate: m.route.link
                                    }, [
                                            m('span.fa.fa-download',
                                                m.trust(' ')
                                            ),
                                            'Baixar relatórios'
                                        ])
                                )
                            ])
                        ),
                        m('.u-marginbottom-60', [
                            m('.card.card-secondary.fontsize-smallest.fontweight-semibold.lineheight-tighter.u-marginbottom-10',
                                m('.w-row', [
                                    m('.table-col.w-col.w-col-3',
                                        m('div',
                                            'Assinante'
                                        )
                                    ),
                                    m('.table-col.w-col.w-col-2',
                                        m('div',
                                            'Recompensa'
                                        )
                                    ),
                                    m('.table-col.w-col.w-col-1.u-text-center',
                                        m('div',
                                            'Apoio mensal'
                                        )
                                    ),
                                    m('.table-col.w-col.w-col-2.u-text-center',
                                        m('div',
                                            'Total apoiado'
                                        )
                                    ),
                                    m('.table-col.w-col.w-col-2.u-text-center',
                                        m('div',
                                            'Última cobrança'
                                        )
                                    ),
                                    m('.table-col.w-col.w-col-2.u-text-center',
                                        m('div',
                                            'Status da Assinatura'
                                        )
                                    )
                                ])
                            ),
                            m('.fontsize-small', [
                                _.map(subsCollection, subscription =>
                                    m(dashboardSubscriptionCard, {
                                        subscription
                                    }))
                            ])
                        ])
                    ]),
                    m('.bg-gray.section',
                        m('.w-container',
                            m('.u-marginbottom-60.w-row', [
                                m(loadMoreBtn, {
                                    collection: state.subscriptions,
                                    cssClass: '.w-col-push-4'
                                })
                            ])
                        )
                    )
                ])
            ]);
        }
        return m('', h.loader());
    }
};

export default projectSubscriptionReport;
<commit_msg>Update Base de Assinantes copy<commit_after>import m from 'mithril';
import prop from 'mithril/stream';
import _ from 'underscore';
import {
    catarse,
    commonPayment
} from '../api';
import models from '../models';
import h from '../h';
import loadMoreBtn from '../c/load-more-btn';
import filterText from '../c/filter-text';
import FilterDropdown from '../c/filter-dropdown';
import filterDropdownNumberRange from '../c/filter-dropdown-number-range';
import projectDashboardMenu from '../c/project-dashboard-menu';
import dashboardSubscriptionCard from '../c/dashboard-subscription-card';
import projectsSubscriptionReportVM from '../vms/projects-subscription-report-vm';
import projectsContributionReportVM from '../vms/projects-contribution-report-vm';
import projectSubscriptionsListVM from '../vms/project-subscriptions-list-vm';

const statusCustomFilter = {
    view: () => m('.fontsize-smaller.u-text-center', [
        'Status ',
        m('a.fontsize-smallest.tooltip-wrapper.fa.fa-question-circle.fontcolor-secondary', {
            href: 'https://suporte.catarse.me/hc/pt-br/articles/360024090391-O-que-significa-cada-status-das-assinaturas-e-pagamentos-',
            target: '_blank'
        })
    ])
};

const dropdownFilterCustomLabel = {
    view: function({attrs})
    {
        return m('.fontsize-smaller.u-text-center', attrs.label);
    }
};

const projectSubscriptionReport = {
    oninit: function (vnode) {
        const filterVM = projectsSubscriptionReportVM,
            catarseVM = projectsContributionReportVM,
            dropdownNumber = prop(0),
            error = prop(false),
            loader = prop(true),
            isProjectDataLoaded = prop(false),
            isRewardsDataLoaded = prop(false),
            rewards = prop([]),
            subscriptions = projectSubscriptionsListVM(),
            submit = () => {
                // Set order by last paid on filters too
                filterVM.order({ last_payment_data_created_at: 'desc' });
                if (filterVM.reward_external_id() === 'null') {
                    subscriptions.firstPage(filterVM.withNullParameters());
                } else {
                    subscriptions.firstPage(filterVM.parameters());
                }

                return false;
            },
            filterBuilder = [{
                component: filterText,
                label: 'text_filter',
                data: {
                    label: 'Nome ou email',
                    vm: filterVM.search_index,
                    onchange: submit,
                    wrapper_class: '.u-marginbottom-20.w-col.w-col-3',
                    placeholder: 'Busque por assinantes',
                    onclick: submit
                }
            },
            {
                label: 'status_filter',
                component: FilterDropdown,
                data: {
                    custom_label: [
                        statusCustomFilter,
                        null
                    ],
                    onchange: submit,
                    name: 'status',
                    vm: filterVM.status,
                    wrapper_class: '.w-col.w-col-3',
                    options: [{
                        value: '',
                        option: 'Todos'
                    },
                    {
                        value: 'active',
                        option: 'Ativa'
                    },
                    {
                        value: 'started',
                        option: 'Iniciada'
                    },
                    {
                        value: 'canceling',
                        option: 'Cancelamento solicitado'
                    },
                    {
                        value: 'canceled',
                        option: 'Cancelada'
                    },
                    {
                        value: 'inactive',
                        option: 'Inativa'
                    }
                    ]
                }
            },
            {
                label: 'reward_filter',
                component: FilterDropdown,
                data: {
                    custom_label: [
                        dropdownFilterCustomLabel,
                        {label: 'Recompensa'}
                    ],
                    onchange: submit,
                    name: 'reward_external_id',
                    vm: filterVM.reward_external_id,
                    wrapper_class: '.w-col.w-col-3',
                    options: []
                }
            },
            {
                label: 'payment_filter',
                component: FilterDropdown,
                data: {
                    custom_label: [
                        dropdownFilterCustomLabel,
                        {label: 'Meio de pgto.'}
                    ],
                    onchange: submit,
                    name: 'payment_method',
                    vm: filterVM.payment_method,
                    wrapper_class: '.w-col.w-col-2',
                    options: [{
                        value: '',
                        option: 'Todos'
                    },
                    {
                        value: 'credit_card',
                        option: 'Cartão de crédito'
                    },
                    {
                        value: 'boleto',
                        option: 'Boleto'
                    }
                    ]
                }
            },
            {
                label: 'total_paid_filter',
                component: filterDropdownNumberRange,
                data: {
                    index: 1,
                    selectable: dropdownNumber,
                    label: 'Total pago',
                    name: 'total_paid',
                    onapply: submit,
                    vm: filterVM.total_paid,
                    wrapper_class: '.w-col.w-col-2',
                    init_lower_value: '0',
                    init_higher_value: 'mais',
                    value_change_placeholder: 'R$#V1 ou #V2',
                    value_change_both_placeholder: 'R$#V1 a R$#V2',
                    inner_field_placeholder: '0',
                    inner_field_label: 'R$',
                    value_multiplier: 100,
                    min: 0
                }
            },
            {
                label: 'paid_count_filter',
                component: filterDropdownNumberRange,
                data: {
                    index: 2,
                    selectable: dropdownNumber,
                    label: 'Meses pagos',
                    name: 'paid_count',
                    onapply: submit,
                    vm: filterVM.paid_count,
                    wrapper_class: '.w-col.w-col-2',
                    init_lower_value: '0',
                    init_higher_value: 'mais',
                    value_change_placeholder: '#V1 ou #V2',
                    value_change_both_placeholder: '#V1 a #V2',
                    inner_field_placeholder: '0',
                    value_multiplier: 1,
                    min: 0,
                    dropdown_inline_style: {
                        right: '0'
                    }
                }
            }

            ],
            handleError = () => {
                error(true);
                loader(false);
                isProjectDataLoaded(true);
                m.redraw();
            },
            project = prop([{}]);

        catarseVM.project_id(vnode.attrs.project_id);

        const lReward = catarse.loaderWithToken(models.rewardDetail.getPageOptions({
            project_id: `eq.${catarseVM.project_id()}`
        }));

        lReward.load().then((loadedRewards) => {
            rewards(loadedRewards);
            isRewardsDataLoaded(true);
            m.redraw();
        });
        const mapRewardsToOptions = () => {
            let options = [];
            if (!lReward()) {
                options = _.map(rewards(), r => ({
                    value: r.id,
                    option: `R$ ${h.formatNumber(r.minimum_value, 2, 3)} - ${(r.title ? r.title : r.description).substring(0, 20)}`
                }));
            }

            options.unshift({
                value: null,
                option: 'Sem recompensa'
            });

            options.unshift({
                value: '',
                option: 'Todas'
            });

            return options;
        };

        const lProject = catarse.loaderWithToken(models.projectDetail.getPageOptions({
            project_id: `eq.${catarseVM.project_id()}`
        }));

        lProject.load().then((data) => {
            filterVM.project_id(_.first(data).common_id);
            // override default 'created_at' order on vm
            filterVM.order({ last_payment_data_created_at: 'desc' });
            subscriptions.firstPage(filterVM.parameters()).then(result => {
                loader(false);
                isProjectDataLoaded(true);
                m.redraw();
            }).catch(err => {
                handleError(err);
                m.redraw();
            });
            project(data);
            m.redraw();
        });

        vnode.state = {
            filterVM,
            mapRewardsToOptions,
            filterBuilder,
            submit,
            subscriptions,
            lProject,
            project,
            isProjectDataLoaded,
            isRewardsDataLoaded
        };
    },
    view: function ({state, attrs}) {
        const subsCollection = state.subscriptions.collection(),
            filterBuilder = state.filterBuilder,
            statusFilter = _.findWhere(filterBuilder, {
                label: 'status_filter'
            }),
            textFilter = _.findWhere(filterBuilder, {
                label: 'text_filter'
            }),
            rewardFilter = _.findWhere(filterBuilder, {
                label: 'reward_filter'
            }),
            paymentFilter = _.findWhere(filterBuilder, {
                label: 'payment_filter'
            }),
            totalPaidFilter = _.findWhere(filterBuilder, {
                label: 'total_paid_filter'
            }),
            paidCountFilter = _.findWhere(filterBuilder, {
                label: 'paid_count_filter'
            });
        rewardFilter.data.options = state.mapRewardsToOptions();
        if (state.isProjectDataLoaded() && state.isRewardsDataLoaded()) {
            return m('div', [
                m(projectDashboardMenu, {
                    project: prop(_.first(state.project()))
                }),
                m('.dashboard-header', [
                    m('.w-container',
                        m('.w-row', [
                            m('.w-col.w-col-3'),
                            m('.w-col.w-col-6',
                                m('.fontsize-larger.fontweight-semibold.lineheight-looser.u-marginbottom-30.u-text-center',
                                    'Base de assinantes'
                                )
                            ),
                            m('.w-col.w-col-3')
                        ])
                    ),
                    m('.u-marginbottom-30.w-container',
                        m('.w-form', [
                            m('form', {
                                onsubmit: state.submit
                            },
                                m('w-row', [
                                    m(textFilter.component, textFilter.data),
                                    m('.w-col.w-col-9',
                                        m('.w-row', [
                                            m(statusFilter.component, statusFilter.data),
                                            m(rewardFilter.component, rewardFilter.data),
                                            m(paymentFilter.component, paymentFilter.data),
                                            m(totalPaidFilter.component, totalPaidFilter.data),
                                            m(paidCountFilter.component, paidCountFilter.data),
                                        ])
                                    )
                                ])
                            )
                        ])
                    )
                ]),
                m('.divider'),
                m('.before-footer.bg-gray.section', [
                    m('.w-container', [
                        m('div',
                            m('.w-row', [
                                m('.u-marginbottom-20.u-text-center-small-only.w-col.w-col-6',
                                    m('.w-inline-block.fontsize-base.u-marginright-10', [
                                        m('span.fontweight-semibold',
                                            state.subscriptions.total()
                                        ),
                                        ' assinaturas',
                                        m.trust(' ')
                                    ])
                                ),
                                m('.w-col.w-col-6',
                                    m(`a.alt-link.fontsize-small.u-right[href='/projects/${attrs.project_id}/subscriptions_report_download']`, {
                                        oncreate: m.route.link
                                    }, [
                                            m('span.fa.fa-download',
                                                m.trust(' ')
                                            ),
                                            'Baixar relatórios'
                                        ])
                                )
                            ])
                        ),
                        m('.u-marginbottom-60', [
                            m('.card.card-secondary.fontsize-smallest.fontweight-semibold.lineheight-tighter.u-marginbottom-10',
                                m('.w-row', [
                                    m('.table-col.w-col.w-col-3',
                                        m('div',
                                            'Assinante'
                                        )
                                    ),
                                    m('.table-col.w-col.w-col-2',
                                        m('div',
                                            'Recompensa'
                                        )
                                    ),
                                    m('.table-col.w-col.w-col-1.u-text-center',
                                        m('div',
                                            'Pagamento mensal'
                                        )
                                    ),
                                    m('.table-col.w-col.w-col-2.u-text-center',
                                        m('div',
                                            'Total pago'
                                        )
                                    ),
                                    m('.table-col.w-col.w-col-2.u-text-center',
                                        m('div',
                                            'Última cobrança'
                                        )
                                    ),
                                    m('.table-col.w-col.w-col-2.u-text-center',
                                        m('div',
                                            'Status da Assinatura'
                                        )
                                    )
                                ])
                            ),
                            m('.fontsize-small', [
                                _.map(subsCollection, subscription =>
                                    m(dashboardSubscriptionCard, {
                                        subscription
                                    }))
                            ])
                        ])
                    ]),
                    m('.bg-gray.section',
                        m('.w-container',
                            m('.u-marginbottom-60.w-row', [
                                m(loadMoreBtn, {
                                    collection: state.subscriptions,
                                    cssClass: '.w-col-push-4'
                                })
                            ])
                        )
                    )
                ])
            ]);
        }
        return m('', h.loader());
    }
};

export default projectSubscriptionReport;
<|endoftext|>


<commit_before>/* Copyright (c) 2006-2012 by OpenLayers Contributors (see authors.txt for
 * full list of contributors). Published under the 2-clause BSD license.
 * See license.txt in the OpenLayers distribution or repository for the
 * full text of the license. */

/**
 * @requires OpenLayers/Format/WCSCapabilities/v1.js
 * @requires OpenLayers/Format/OWSCommon/v1.js
 */

/**
 * Class: OpenLayers.Format.WCSCapabilities/v1_1_0
 * Read WCS Capabilities version 1.1.0.
 *
 * Inherits from:
 *  - <OpenLayers.Format.WCSCapabilities.v1>
 */
OpenLayers.Format.WCSCapabilities.v1_1_0 = OpenLayers.Class(
    OpenLayers.Format.WCSCapabilities.v1, {

    /**
     * Property: namespaces
     * {Object} Mapping of namespace aliases to namespace URIs.
     */
    namespaces: {
        wcs: "http://www.opengis.net/wcs/1.1",
        xlink: "http://www.w3.org/1999/xlink",
        xsi: "http://www.w3.org/2001/XMLSchema-instance",
        ows: "http://www.opengis.net/ows/1.1"
    },

    /**
     * APIProperty: errorProperty
     * {String} Which property of the returned object to check for in order to
     * determine whether or not parsing has failed. In the case that the
     * errorProperty is undefined on the returned object, the document will be
     * run through an OGCExceptionReport parser.
     */
    errorProperty: "operationsMetadata",

    /**
     * Constructor: OpenLayers.Format.WCSCapabilities.v1_1_0
     * Create a new parser for WCS capabilities version 1.1.0.
     *
     * Parameters:
     * options - {Object} An optional object whose properties will be set on
     *     this instance.
     */

    /**
     * Property: readers
     * Contains public functions, grouped by namespace prefix, that will
     *     be applied when a namespaced node is found matching the function
     *     name.  The function will be applied in the scope of this parser
     *     with two arguments: the node being read and a context object passed
     *     from the parent.
     */
    readers: {
        "wcs": OpenLayers.Util.applyDefaults({
            // In 1.0.0, this was WCS_Capabilties, in 1.1.0, it's Capabilities
            "Capabilities": function(node, obj) {
                this.readChildNodes(node, obj);
            },
            "Contents": function(node, request) {
                request.contentMetadata = [];
                this.readChildNodes(node, request.contentMetadata);
            },
            "CoverageSummary": function(node, contentMetadata) {
                var coverageSummary = {};
                // Read the summary:
                this.readChildNodes(node, coverageSummary);

                // Add it to the contentMetadata array:
                contentMetadata.push(coverageSummary);
            },
            "Identifier": function(node, coverageSummary) {
                coverageSummary.identifier = this.getChildValue(node);
            },
            "Title": function(node, coverageSummary) {
              coverageSummary.title = this.getChildValue(node);
            },
            "Abstract": function(node, coverageSummary) {
                coverageSummary.abstract = this.getChildValue(node);
            },
            "SupportedCRS": function(node, coverageSummary) {
                var crs = this.getChildValue(node);
                if(crs) {
                    if(!coverageSummary["supportedCRS"]) {
                        coverageSummary["supportedCRS"] = [];
                    }
                    coverageSummary["supportedCRS"].push(crs);
                }
            },
            "SupportedFormat": function(node, coverageSummary) {
                var format = this.getChildValue(node);
                if(format) {
                    if(!coverageSummary["supportedFormat"]) {
                        coverageSummary["supportedFormat"] = [];
                    }
                    coverageSummary["supportedFormat"].push(format);
                }
            },

        }, OpenLayers.Format.WCSCapabilities.v1.prototype.readers["wcs"]),
        "ows": OpenLayers.Format.OWSCommon.v1_1_0.prototype.readers["ows"],
    },

    CLASS_NAME: "OpenLayers.Format.WCSCapabilities.v1_1_0"

});<commit_msg>Prefer dot notation<commit_after>/* Copyright (c) 2006-2012 by OpenLayers Contributors (see authors.txt for
 * full list of contributors). Published under the 2-clause BSD license.
 * See license.txt in the OpenLayers distribution or repository for the
 * full text of the license. */

/**
 * @requires OpenLayers/Format/WCSCapabilities/v1.js
 * @requires OpenLayers/Format/OWSCommon/v1.js
 */

/**
 * Class: OpenLayers.Format.WCSCapabilities/v1_1_0
 * Read WCS Capabilities version 1.1.0.
 *
 * Inherits from:
 *  - <OpenLayers.Format.WCSCapabilities.v1>
 */
OpenLayers.Format.WCSCapabilities.v1_1_0 = OpenLayers.Class(
    OpenLayers.Format.WCSCapabilities.v1, {

    /**
     * Property: namespaces
     * {Object} Mapping of namespace aliases to namespace URIs.
     */
    namespaces: {
        wcs: "http://www.opengis.net/wcs/1.1",
        xlink: "http://www.w3.org/1999/xlink",
        xsi: "http://www.w3.org/2001/XMLSchema-instance",
        ows: "http://www.opengis.net/ows/1.1"
    },

    /**
     * APIProperty: errorProperty
     * {String} Which property of the returned object to check for in order to
     * determine whether or not parsing has failed. In the case that the
     * errorProperty is undefined on the returned object, the document will be
     * run through an OGCExceptionReport parser.
     */
    errorProperty: "operationsMetadata",

    /**
     * Constructor: OpenLayers.Format.WCSCapabilities.v1_1_0
     * Create a new parser for WCS capabilities version 1.1.0.
     *
     * Parameters:
     * options - {Object} An optional object whose properties will be set on
     *     this instance.
     */

    /**
     * Property: readers
     * Contains public functions, grouped by namespace prefix, that will
     *     be applied when a namespaced node is found matching the function
     *     name.  The function will be applied in the scope of this parser
     *     with two arguments: the node being read and a context object passed
     *     from the parent.
     */
    readers: {
        "wcs": OpenLayers.Util.applyDefaults({
            // In 1.0.0, this was WCS_Capabilties, in 1.1.0, it's Capabilities
            "Capabilities": function(node, obj) {
                this.readChildNodes(node, obj);
            },
            "Contents": function(node, request) {
                request.contentMetadata = [];
                this.readChildNodes(node, request.contentMetadata);
            },
            "CoverageSummary": function(node, contentMetadata) {
                var coverageSummary = {};
                // Read the summary:
                this.readChildNodes(node, coverageSummary);

                // Add it to the contentMetadata array:
                contentMetadata.push(coverageSummary);
            },
            "Identifier": function(node, coverageSummary) {
                coverageSummary.identifier = this.getChildValue(node);
            },
            "Title": function(node, coverageSummary) {
              coverageSummary.title = this.getChildValue(node);
            },
            "Abstract": function(node, coverageSummary) {
                coverageSummary.abstract = this.getChildValue(node);
            },
            "SupportedCRS": function(node, coverageSummary) {
                var crs = this.getChildValue(node);
                if(crs) {
                    if(!coverageSummary.supportedCRS) {
                        coverageSummary.supportedCRS = [];
                    }
                    coverageSummary.supportedCRS.push(crs);
                }
            },
            "SupportedFormat": function(node, coverageSummary) {
                var format = this.getChildValue(node);
                if(format) {
                    if(!coverageSummary.supportedFormat) {
                        coverageSummary.supportedFormat = [];
                    }
                    coverageSummary.supportedFormat.push(format);
                }
            },

        }, OpenLayers.Format.WCSCapabilities.v1.prototype.readers["wcs"]),
        "ows": OpenLayers.Format.OWSCommon.v1_1_0.prototype.readers["ows"],
    },

    CLASS_NAME: "OpenLayers.Format.WCSCapabilities.v1_1_0"

});<|endoftext|>


<commit_before>
  function ExplanationWindow() {
    this.create();
  }

  ExplanationWindow.prototype = {};

  ExplanationWindow.prototype.create = function() {
    var me = this;
    this.div = document.createElement('div');
    this.div.setAttribute('id','appInfo');
    this.div.innerHTML =
      ''+
      '
'+
        ''+
        '
¿DE DÓNDE SALEN LOS DATOS?
'+
        '
Datos demográficos
'+
        '
'+
          '
Edad Media
'+
          '
Other
'+
        '
'+
      '
'+
      '

';

    $('body').append(this.div);
    $(this.div).children('a.close_info').click(function(ev){ev.stopPropagation();ev.preventDefault();me.hide();});

    $(this.div).find('li').children('a').click(function(ev){
      ev.stopPropagation(); ev.preventDefault();
      if (!$(this).parent().hasClass('selected')) {
        $('div#appInfo ul li').each(function(i,ele){$(ele).removeClass('selected')});
        $(this).parent().addClass('selected');
        console.log($(this).parent().text());
        $('div#appInfo div.explain').append(explanationContent[$(this).parent().text()].htmlContent)
        var offset = $(this).position().top;
        $('div#appInfo span.arrow').animate({top:offset+'px'},300);
        //TODO - change div to show in the right main window

      }
    });
    $(this.div).draggable({containment: 'parent'});
  }

  ExplanationWindow.prototype.show = function() {
    $(this.div).css({margin:'-237px 0 0 -385px',top:'50%',left:'50%'});
    $(this.div).fadeIn();
  }

  ExplanationWindow.prototype.hide = function() {
    $(this.div).fadeOut();
  }
<commit_msg>binding explanationWindow contents<commit_after>
  function ExplanationWindow() {
    this.create();
  }

  ExplanationWindow.prototype = {};

  ExplanationWindow.prototype.create = function() {
    var me = this;
    this.div = document.createElement('div');
    this.div.setAttribute('id','appInfo');
    this.div.innerHTML =
      ''+
      '
'+
        ''+
        '
¿DE DÓNDE SALEN LOS DATOS?
'+
        '
Datos demográficos
'+
        '
'+
          '
Edad Media
'+
          '
Other
'+
        '
'+
      '
'+
      '

';

    $('body').append(this.div);
    $(this.div).children('a.close_info').click(function(ev){ev.stopPropagation();ev.preventDefault();me.hide();});

    $(this.div).find('li').children('a').click(function(ev){
      ev.stopPropagation(); ev.preventDefault();
      if (!$(this).parent().hasClass('selected')) {
        $('div#appInfo ul li').each(function(i,ele){$(ele).removeClass('selected')});
        $(this).parent().addClass('selected');
        console.log($(this).parent().text());
        $('div#appInfo div.explain').children().remove();
        $('div#appInfo div.explain').append(explanationContent[$(this).parent().text()].htmlContent)
        var offset = $(this).position().top;
        $('div#appInfo span.arrow').animate({top:offset+'px'},300);
        //TODO - change div to show in the right main window

      }
    });
    $(this.div).draggable({containment: 'parent'});
  }

  ExplanationWindow.prototype.show = function() {
    $(this.div).css({margin:'-237px 0 0 -385px',top:'50%',left:'50%'});
    $(this.div).fadeIn();
  }

  ExplanationWindow.prototype.hide = function() {
    $(this.div).fadeOut();
  }
<|endoftext|>


<commit_before>// Add a google map
var template = window.template || {};
    template.googlemap = {};

(function(googlemap)
{

    googlemap.init = function()
    {

    	_setupGooglemap();

    };

    googlemap.initialize = function()
    {

        // Initialize a single marker
        var myOptions = {
            zoom: 10,
            center: new google.maps.LatLng(0, 0),
            mapTypeId: google.maps.MapTypeId.ROADMAP,
        };
        var map = new google.maps.Map(document.getElementById('js-map_address'), myOptions);
        template.googlemap.geocode(company_address, map);

    };

    googlemap.geocode = function(list, map)
    {

        // Geocode a location
        for (var i = 0; i < list.length; i++) {
            list = window[list] || list;

            var addressId = list[i].id;
            var geocoder = new google.maps.Geocoder();
            var geoOptions = {
                address: list[i].location,
                region: 'NO'
            };

            geocoder.geocode(geoOptions, template.googlemap.geocodeCallback(list[i], map, addressId));
        }

    };

    googlemap.geocodeCallback = function(item, map, addressId)
    {

        // Add a marker
        return function(results, status) {
            if (status == google.maps.GeocoderStatus.OK) {
                template.googlemap.addMarker(map, item, results[0].geometry.location, addressId);
            }
        }

    };

    googlemap.addMarker = function(map, item, location, addressId)
    {

        // Add a single marker
        var marker = new google.maps.Marker({
            map: map,
            position: location
        })

        var bounds = new google.maps.LatLngBounds();
        bounds.extend(location);
        map.fitBounds(bounds);

        zoomChangeBoundsListener =
        google.maps.event.addListenerOnce(map, 'bounds_changed', function(event) {
            if (this.getZoom()) {
                this.setZoom(14);
            }
        });

    };

    function _setupGooglemap()
    {

        // Add the google maps api only when we want it to via labjs
        if ($('#js-map_address').length > 0) {
            $L = $L
                .script('//maps.googleapis.com/maps/api/js?key=[ADD YOUR OWN API KEY]&sensor=false&callback=template.googlemap.initialize').wait();
        }

    };

})(template.googlemap);
<commit_msg>Removed the maps sensor<commit_after>// Add a google map
var template = window.template || {};
    template.googlemap = {};

(function(googlemap)
{

    googlemap.init = function()
    {

    	_setupGooglemap();

    };

    googlemap.initialize = function()
    {

        // Initialize a single marker
        var myOptions = {
            zoom: 10,
            center: new google.maps.LatLng(0, 0),
            mapTypeId: google.maps.MapTypeId.ROADMAP,
        };
        var map = new google.maps.Map(document.getElementById('js-map_address'), myOptions);
        template.googlemap.geocode(company_address, map);

    };

    googlemap.geocode = function(list, map)
    {

        // Geocode a location
        for (var i = 0; i < list.length; i++) {
            list = window[list] || list;

            var addressId = list[i].id;
            var geocoder = new google.maps.Geocoder();
            var geoOptions = {
                address: list[i].location,
                region: 'NO'
            };

            geocoder.geocode(geoOptions, template.googlemap.geocodeCallback(list[i], map, addressId));
        }

    };

    googlemap.geocodeCallback = function(item, map, addressId)
    {

        // Add a marker
        return function(results, status) {
            if (status == google.maps.GeocoderStatus.OK) {
                template.googlemap.addMarker(map, item, results[0].geometry.location, addressId);
            }
        }

    };

    googlemap.addMarker = function(map, item, location, addressId)
    {

        // Add a single marker
        var marker = new google.maps.Marker({
            map: map,
            position: location
        })

        var bounds = new google.maps.LatLngBounds();
        bounds.extend(location);
        map.fitBounds(bounds);

        zoomChangeBoundsListener =
        google.maps.event.addListenerOnce(map, 'bounds_changed', function(event) {
            if (this.getZoom()) {
                this.setZoom(14);
            }
        });

    };

    function _setupGooglemap()
    {

        // Add the google maps api only when we want it to via labjs
        if ($('#js-map_address').length > 0) {
            $L = $L
                .script('//maps.googleapis.com/maps/api/js?key=[ADD YOUR OWN API KEY]&callback=template.googlemap.initialize').wait();
        }

    };

})(template.googlemap);
<|endoftext|>


<commit_before>! function() {
  'use strict';

  define([
    'backbone',
    'Components/Base/ComponentView',
    'Components/Basic/textinput',
    'Components/Basic/button'
  ], function(Backbone, ComponentView, InputComponent, ButtonComponent) {

    var SearchComponent = ComponentView.extend({

      initialize: function() {
        this.options.manualUpdate = true;
        SearchComponent.__super__.initialize.apply(this, arguments);

        var that = this;

        this.input = new InputComponent({
          className: "input-text input-search",
          model: this.model,
          property: this.options.property,
          onKeyup: function (val, e) {
            that.keyup(val, e);
          },
          onChange: function (val, e) {
            that.change(val, e);
          }
        });

        this.cancel = new ButtonComponent({
          className: "input-search-cancel",
          tagName: "div",
          onClick: function (e) {
            that.clearSearch(e);
          }
        });

        this.components[this.getTagName()] = [this.input, this.cancel];
      },

      render: function () {
        SearchComponent.__super__.render.apply(this, arguments);

        this.toggleCancel(this.model.get(this.options.property));
      },

      setModel: function (model) {
        SearchComponent.__super__.setModel.apply(this, arguments);

        this.eachComponent(function (component) {
          component.setModel(model);
        });
      },

      clearSearch: function (e) {
        this.model.set(this.options.property, "");
        this.change("",e);
      },

      toggleCancel: function (val) {
        if(val !== "" && val !== -1) this.showCancel();
        else this.hideCancel();
      },

      showCancel: function () {
        this.cancel.$el.show();
      },

      hideCancel: function () {
        this.cancel.$el.hide();
      },

      change: function (val, e) {
        if(this.options.onChange) this.options.onChange(val, e);
      },

      keyup: function (val, e) {
        this.toggleCancel(val);
        if(this.options.onKeyup) this.options.onKeyup(val, e);
      }

    });

    return SearchComponent;
  });
}();<commit_msg>fixed bug where search didn't toggle cancel on change event<commit_after>! function() {
  'use strict';

  define([
    'backbone',
    'Components/Base/ComponentView',
    'Components/Basic/textinput',
    'Components/Basic/button'
  ], function(Backbone, ComponentView, InputComponent, ButtonComponent) {

    var SearchComponent = ComponentView.extend({

      initialize: function() {
        this.options.manualUpdate = true;
        SearchComponent.__super__.initialize.apply(this, arguments);

        var that = this;

        this.input = new InputComponent({
          className: "input-text input-search",
          model: this.model,
          property: this.options.property,
          onKeyup: function (val, e) {
            that.keyup(val, e);
          },
          onChange: function (val, e) {
            that.change(val, e);
          }
        });

        this.cancel = new ButtonComponent({
          className: "input-search-cancel",
          tagName: "div",
          onClick: function (e) {
            that.clearSearch(e);
          }
        });

        this.components[this.getTagName()] = [this.input, this.cancel];
      },

      render: function () {
        SearchComponent.__super__.render.apply(this, arguments);

        this.toggleCancel(this.model.get(this.options.property));
      },

      setModel: function (model) {
        SearchComponent.__super__.setModel.apply(this, arguments);

        this.eachComponent(function (component) {
          component.setModel(model);
        });
      },

      clearSearch: function (e) {
        this.model.set(this.options.property, "");
        this.change("",e);
      },

      toggleCancel: function (val) {
        if(val !== "" && val !== -1) this.showCancel();
        else this.hideCancel();
      },

      showCancel: function () {
        this.cancel.$el.show();
      },

      hideCancel: function () {
        this.cancel.$el.hide();
      },

      change: function (val, e) {
        this.toggleCancel(val);
        if(this.options.onChange) this.options.onChange(val, e);
      },

      keyup: function (val, e) {
        this.toggleCancel(val);
        if(this.options.onKeyup) this.options.onKeyup(val, e);
      }

    });

    return SearchComponent;
  });
}();<|endoftext|>