<commit_before>import { ActionTypes } from '../config/constants';

const initialState = {
  itemList: {},
  fields: [
    {
      filterType: 'textfield',
      name: 'summary',
      label: 'Summary',
      type: 'text',
      required: true,
    },
    {
      filterType: 'textfield',
      name: 'phase',
      label: 'Phase',
      type: 'text',
      required: true,
    },
    {
      filterType: 'custom',
      name: 't&m',
      label: 'T&M',
      type: 'dropdown',
      options: [
        {
          name: 't&m',
          label: 'Fixed Fee',
          value: 'FALSE',
        },
        {
          name: 't&m',
          label: 'T&M',
          value: 'TRUE',
        },
      ],
    },
    {
      filterType: 'textfield',
      name: 'feature',
      label: 'Feature',
      type: 'text',
    },
    {
      filterType: 'textfield',
      name: 'budgetHours.column',
      label: 'Column',
      type: 'text',
    },
    {
      filterType: 'textfield',
      name: 'budgetHours.value',
      label: 'Hours',
      type: 'number',
    },
    {
      filterType: 'none',
      name: 'descriptions.workplan',
      label: 'Workplan description',
      type: 'text',
      required: true,
    },
    {
      filterType: 'none',
      name: 'descriptions.budget',
      label: 'Budget description',
      type: 'text',
    },
    {
      filterType: 'none',
      name: 'descriptions.assumptions',
      label: 'Assumptions',
      type: 'text',
    },
    {
      filterType: 'none',
      name: 'descriptions.clientResponsibilities',
      label: 'Client Responsibilities',
      type: 'text',
    },
    {
      filterType: 'none',
      name: 'descriptions.exclusions',
      label: 'Exclusions',
      type: 'text',
    },
    {
      filterType: 'custom',
      name: 'tags',
      label: 'Tags',
      required: true,
      type: 'text',
    },
    {
      filterType: 'none',
      isInteractive: true,
      name: 'edit',
      label: 'Edit',
    },
  ],
  query: {},
  userColumns: {
    summary: true,
    phase: false,
    feature: true,
    'budgetHours.column': false,
    'budgetHours.value': false,
    tags: true,
    edit: true,
  },
  visibleItemList: {},
  dispatchingPlan: {
    inProgress: false,
    response: null,
  },
  presets: {},
};

export default (state = initialState, action) => {
  switch(action.type) {
    case ActionTypes.BUDGETS_ADD:
      const payload = action.payload;
      return {
        ...state,
        [payload.elementType]: {
          ...state[payload.elementType],
          [payload.element.id]: payload.element,
        },
      };

    case ActionTypes.BUDGETS_DISPATCH_PLAN:
      return {
        ...state,
        dispatchingPlan: {
          inProgress: true,
          response: null,
        }
      };

    case ActionTypes.BUDGETS_DISPATCH_PLAN_SUCCESS:
      return {
        ...state,
        dispatchingPlan: {
          inProgress: false,
          response: action.payload.result,
        }
      };

    case ActionTypes.BUDGETS_REMOVE:
      delete state[action.payload.elementType][action.payload.elementId];

      return state;

    case ActionTypes.BUDGETS_SEARCH:
      const visibleItemList = {};
      action.payload.visibleItems.map((item) => {
          visibleItemList[item.id] = item;

          return item;
      });

      console.log('Search:', action.payload.query);
      return {
        ...state,
        query: action.payload.query,
        visibleItemList,
      };

    case ActionTypes.BUDGETS_SUBSCRIBE:
      return state;

    case ActionTypes.BUDGETS_TOGGLE_COL:
      return {
        ...state,
        userColumns: {
          ...state.userColumns,
          [action.payload.columnName]: !state.userColumns[action.payload.columnName],
        }
      };

    case ActionTypes.BUDGETS_UPDATE:
      return {
        ...state,
        ...action.payload,
      };

    case ActionTypes.BUDGETS_UPDATE_ITEM:
      return {
        ...state,
        itemList: {
          ...state.itemList,
          [action.payload.updatedItem.id]: action.payload.updatedItem,
        },
      };

    default:
      return state;
  }
};
<commit_msg>Remove debugging<commit_after>import { ActionTypes } from '../config/constants';

const initialState = {
  itemList: {},
  fields: [
    {
      filterType: 'textfield',
      name: 'summary',
      label: 'Summary',
      type: 'text',
      required: true,
    },
    {
      filterType: 'textfield',
      name: 'phase',
      label: 'Phase',
      type: 'text',
      required: true,
    },
    {
      filterType: 'custom',
      name: 't&m',
      label: 'T&M',
      type: 'dropdown',
      options: [
        {
          name: 't&m',
          label: 'Fixed Fee',
          value: 'FALSE',
        },
        {
          name: 't&m',
          label: 'T&M',
          value: 'TRUE',
        },
      ],
    },
    {
      filterType: 'textfield',
      name: 'feature',
      label: 'Feature',
      type: 'text',
    },
    {
      filterType: 'textfield',
      name: 'budgetHours.column',
      label: 'Column',
      type: 'text',
    },
    {
      filterType: 'textfield',
      name: 'budgetHours.value',
      label: 'Hours',
      type: 'number',
    },
    {
      filterType: 'none',
      name: 'descriptions.workplan',
      label: 'Workplan description',
      type: 'text',
      required: true,
    },
    {
      filterType: 'none',
      name: 'descriptions.budget',
      label: 'Budget description',
      type: 'text',
    },
    {
      filterType: 'none',
      name: 'descriptions.assumptions',
      label: 'Assumptions',
      type: 'text',
    },
    {
      filterType: 'none',
      name: 'descriptions.clientResponsibilities',
      label: 'Client Responsibilities',
      type: 'text',
    },
    {
      filterType: 'none',
      name: 'descriptions.exclusions',
      label: 'Exclusions',
      type: 'text',
    },
    {
      filterType: 'custom',
      name: 'tags',
      label: 'Tags',
      required: true,
      type: 'text',
    },
    {
      filterType: 'none',
      isInteractive: true,
      name: 'edit',
      label: 'Edit',
    },
  ],
  query: {},
  userColumns: {
    summary: true,
    phase: false,
    feature: true,
    'budgetHours.column': false,
    'budgetHours.value': false,
    tags: true,
    edit: true,
  },
  visibleItemList: {},
  dispatchingPlan: {
    inProgress: false,
    response: null,
  },
  presets: {},
};

export default (state = initialState, action) => {
  switch(action.type) {
    case ActionTypes.BUDGETS_ADD:
      const payload = action.payload;
      return {
        ...state,
        [payload.elementType]: {
          ...state[payload.elementType],
          [payload.element.id]: payload.element,
        },
      };

    case ActionTypes.BUDGETS_DISPATCH_PLAN:
      return {
        ...state,
        dispatchingPlan: {
          inProgress: true,
          response: null,
        }
      };

    case ActionTypes.BUDGETS_DISPATCH_PLAN_SUCCESS:
      return {
        ...state,
        dispatchingPlan: {
          inProgress: false,
          response: action.payload.result,
        }
      };

    case ActionTypes.BUDGETS_REMOVE:
      delete state[action.payload.elementType][action.payload.elementId];

      return state;

    case ActionTypes.BUDGETS_SEARCH:
      const visibleItemList = {};
      action.payload.visibleItems.map((item) => {
          visibleItemList[item.id] = item;

          return item;
      });

      return {
        ...state,
        query: action.payload.query,
        visibleItemList,
      };

    case ActionTypes.BUDGETS_SUBSCRIBE:
      return state;

    case ActionTypes.BUDGETS_TOGGLE_COL:
      return {
        ...state,
        userColumns: {
          ...state.userColumns,
          [action.payload.columnName]: !state.userColumns[action.payload.columnName],
        }
      };

    case ActionTypes.BUDGETS_UPDATE:
      return {
        ...state,
        ...action.payload,
      };

    case ActionTypes.BUDGETS_UPDATE_ITEM:
      return {
        ...state,
        itemList: {
          ...state.itemList,
          [action.payload.updatedItem.id]: action.payload.updatedItem,
        },
      };

    default:
      return state;
  }
};
<|endoftext|>


<commit_before>import audioContext from '../audioContext'
import {
  adjust,
  append,
  compose,
  contains,
  curry,
  dec,
  equals,
  find,
  findIndex,
  flip,
  forEach,
  inc,
  isEmpty,
  last,
  length,
  lensProp,
  nth,
  over,
  pluck,
  propEq,
  sortBy,
  view,
  without
} from 'ramda'
import {
  ADD_CHANNEL,
  ADD_EFFECT_TO_CHANNEL,
  ADD_INSTRUMENT_TO_CHANNEL,
  INSTANTIATE_EFFECT,
  INSTANTIATE_INSTRUMENT,
  LOAD_PLUGIN_EFFECT,
  LOAD_PLUGIN_INSTRUMENT,
  REMOVE_CHANNEL,
  REMOVE_EFFECT_FROM_CHANNEL,
  REMOVE_INSTRUMENT_FROM_CHANNEL
} from '../actions'

const channelsLens = lensProp('channels')
const effectsLens = lensProp('effects')
const effectInstancesLens = lensProp('effectInstances')
const instrumentInstancesLens = lensProp('instrumentInstances')
const instrumentsLens = lensProp('instruments')

const channels = view(channelsLens)
const constructor = view(lensProp('constructor'))
const destination = view(lensProp('destination'))
const effects = view(effectsLens)
const effectInstances = view(effectInstancesLens)
const instance = view(lensProp('instance'))
const instruments = view(instrumentsLens)
const instrumentInstances = view(instrumentInstancesLens)
const name = view(lensProp('name'))

const overChannels = over(channelsLens)
const overEffectInstances = over(effectInstancesLens)
const overEffectPlugins = over(lensProp('effectPlugins'))
const overEffects = over(effectsLens)
const overInstrumentInstances = over(instrumentInstancesLens)
const overInstrumentPlugins = over(lensProp('instrumentPlugins'))
const overInstruments = over(instrumentsLens)

const nameEquals = x => compose(equals(x), name)
const findNameEquals = curry((x, y) => compose(find, nameEquals)(x)(y))
const findConstructor = compose(constructor, findNameEquals)
const sortByName = sortBy(name)

const overChannelEffects = curry((f, channelId, state) => overChannels(
  adjust(overEffects(f), findIndex(nameEquals(channelId), channels(state))),
  state
))
const overChannelInstruments = curry((f, channelId, state) => overChannels(
  adjust(overInstruments(f), findIndex(nameEquals(channelId), channels(state))),
  state
))
const channel = curry((a, b) => findNameEquals(a, channels(b)))
const effectInstance = curry((a, b) => instance(findNameEquals(a, effectInstances(b))))
const effectInstanceDestination = compose(destination, effectInstance)
const instrumentInstance = curry((a, b) => instance(findNameEquals(a, instrumentInstances(b))))

const createChannel = name => ({name, effects: [], instruments: []})
const lowestUniqueNatural = xs => {
  let i = 0
  while (contains(i, xs)) i++
  return i
}
const initialState = {
  channels: [
    {name: 0, effects: [], instruments: []},
    {name: 1, effects: [], instruments: []},
    {name: 2, effects: [], instruments: []}
  ],
  effectInstances: [],
  effectPlugins: [],
  instrumentInstances: [],
  instrumentPlugins: []
}

const connectToAudioCtx = x => (x.connect(destination(audioContext)), x)
const disconnect = x => (x.disconnect(), x)

export default (state = initialState, {type, payload}) => {
  switch (type) {
    case ADD_CHANNEL: return overChannels(
      compose(
        sortByName,
        append(createChannel(lowestUniqueNatural(pluck(
          'name',
          channels(state)
        ))))
      ),
      state
    )
    case ADD_EFFECT_TO_CHANNEL: {
      const thisEffectInstance = effectInstance(payload.name, state)
      const lastEffect = last(effects(channel(payload.channel, state)))
      if (!lastEffect) connectToAudioCtx(thisEffectInstance)
      else {
        thisEffectInstance.connect(effectInstanceDestination(
          lastEffect,
          state
        ))
      }
      forEach(
        name => disconnect(instrumentInstance(name, state))
          .connect(destination(thisEffectInstance)),
        instruments(channel(payload.channel, state))
      )
      return overChannelEffects(append(payload.name), payload.channel, state)
    }
    case ADD_INSTRUMENT_TO_CHANNEL: {
      const instrument = instance(find(
        propEq('name', payload.name),
        instrumentInstances(state)
      ))
      instrument.disconnect()
      const lastEffect = last(effects(channel(payload.channel, state)))
      if (!lastEffect) connectToAudioCtx(instrument)
      else instrument.connect(effectInstanceDestination(lastEffect, state))
      return overChannelInstruments(append(payload.name), payload.channel, state)
    }
    case INSTANTIATE_EFFECT: {
      const instance = new (findConstructor(
        payload.plugin,
        state.effectPlugins
      ))({audioContext})
      const thisChannel = channel(payload.channel, state)
      if (!thisChannel) connectToAudioCtx(instance)
      const thisChannelEffects = effects(thisChannel)
      if (isEmpty(thisChannelEffects)) connectToAudioCtx(instance)
      else {
        instance.connect(effectInstanceDestination(
          last(thisChannelEffects),
          state
        ))
      }
      return overChannelEffects(
        append(payload.name),
        payload.channel,
        overEffectInstances(append({
          instance,
          name: payload.name
        }), state)
      )
    }
    case INSTANTIATE_INSTRUMENT: {
      const instance = new (findConstructor(
        payload.plugin,
        state.instrumentPlugins
      ))({audioContext})
      connectToAudioCtx(instance)
      return overInstrumentInstances(
        append({instance, name: payload.name}),
        state
      )
    }
    case LOAD_PLUGIN_INSTRUMENT:
      return overInstrumentPlugins(append(payload), state)
    case LOAD_PLUGIN_EFFECT:
      return overEffectPlugins(append(payload), state)
    case REMOVE_CHANNEL: {
      const channel = findNameEquals(
        payload,
        channels(state)
      )
      forEach(compose(
        disconnect,
        x => effectInstance(x, state)
      ), effects(channel))
      forEach(compose(
        connectToAudioCtx,
        disconnect,
        flip(instrumentInstance)(state)
      ), instruments(channel))
      return overChannels(without([channel]), state)
    }
    case REMOVE_EFFECT_FROM_CHANNEL: {
      const channelEffects = effects(channel(payload.channel, state))
      const channelInstruments = instruments(channel(payload.channel, state))

      disconnect(effectInstance(payload.name, state))
      const effectIndex = findIndex(equals(payload.name), channelEffects)

      if (length(channelEffects) === 1) {
        forEach(
          name => connectToAudioCtx(instrumentInstance(name, state)),
          channelInstruments
        )
      } else if (effectIndex === dec(length(channelEffects))) {
        forEach(
          name => instrumentInstance(name, state)
            .connect(effectInstanceDestination(
              nth(dec(effectIndex), channelEffects),
              state
            )),
          channelInstruments
        )
      } else {
        disconnect(effectInstance(
          nth(inc(effectIndex), channelEffects),
          state
        )).connect(effectInstanceDestination(
          nth(dec(effectIndex), channelEffects),
          state
        ))
      }

      return overChannelEffects(without([payload.name]), payload.channel, state)
    }
    case REMOVE_INSTRUMENT_FROM_CHANNEL: {
      connectToAudioCtx(disconnect(instrumentInstance(payload.name, state)))
      return overChannelInstruments(
        without([payload.name]),
        payload.channel,
        state
      )
    }
    default: return state
  }
}
<commit_msg>bug(channel) fix effect connection bug<commit_after>import audioContext from '../audioContext'
import {
  adjust,
  append,
  compose,
  contains,
  curry,
  dec,
  equals,
  find,
  findIndex,
  flip,
  forEach,
  inc,
  isEmpty,
  last,
  length,
  lensProp,
  nth,
  over,
  pluck,
  propEq,
  sortBy,
  view,
  without
} from 'ramda'
import {
  ADD_CHANNEL,
  ADD_EFFECT_TO_CHANNEL,
  ADD_INSTRUMENT_TO_CHANNEL,
  INSTANTIATE_EFFECT,
  INSTANTIATE_INSTRUMENT,
  LOAD_PLUGIN_EFFECT,
  LOAD_PLUGIN_INSTRUMENT,
  REMOVE_CHANNEL,
  REMOVE_EFFECT_FROM_CHANNEL,
  REMOVE_INSTRUMENT_FROM_CHANNEL
} from '../actions'

const channelsLens = lensProp('channels')
const effectsLens = lensProp('effects')
const effectInstancesLens = lensProp('effectInstances')
const instrumentInstancesLens = lensProp('instrumentInstances')
const instrumentsLens = lensProp('instruments')

const channels = view(channelsLens)
const constructor = view(lensProp('constructor'))
const destination = view(lensProp('destination'))
const effects = view(effectsLens)
const effectInstances = view(effectInstancesLens)
const instance = view(lensProp('instance'))
const instruments = view(instrumentsLens)
const instrumentInstances = view(instrumentInstancesLens)
const name = view(lensProp('name'))

const overChannels = over(channelsLens)
const overEffectInstances = over(effectInstancesLens)
const overEffectPlugins = over(lensProp('effectPlugins'))
const overEffects = over(effectsLens)
const overInstrumentInstances = over(instrumentInstancesLens)
const overInstrumentPlugins = over(lensProp('instrumentPlugins'))
const overInstruments = over(instrumentsLens)

const nameEquals = x => compose(equals(x), name)
const findNameEquals = curry((x, y) => compose(find, nameEquals)(x)(y))
const findConstructor = compose(constructor, findNameEquals)
const sortByName = sortBy(name)

const overChannelEffects = curry((f, channelId, state) => overChannels(
  adjust(overEffects(f), findIndex(nameEquals(channelId), channels(state))),
  state
))
const overChannelInstruments = curry((f, channelId, state) => overChannels(
  adjust(overInstruments(f), findIndex(nameEquals(channelId), channels(state))),
  state
))
const channel = curry((a, b) => findNameEquals(a, channels(b)))
const effectInstance = curry((a, b) => instance(findNameEquals(a, effectInstances(b))))
const effectInstanceDestination = compose(destination, effectInstance)
const instrumentInstance = curry((a, b) => instance(findNameEquals(a, instrumentInstances(b))))

const createChannel = name => ({name, effects: [], instruments: []})
const lowestUniqueNatural = xs => {
  let i = 0
  while (contains(i, xs)) i++
  return i
}
const initialState = {
  channels: [
    {name: 0, effects: [], instruments: []},
    {name: 1, effects: [], instruments: []},
    {name: 2, effects: [], instruments: []}
  ],
  effectInstances: [],
  effectPlugins: [],
  instrumentInstances: [],
  instrumentPlugins: []
}

const connectToAudioCtx = x => (x.connect(destination(audioContext)), x)
const disconnect = x => (x.disconnect(), x)

export default (state = initialState, {type, payload}) => {
  switch (type) {
    case ADD_CHANNEL: return overChannels(
      compose(
        sortByName,
        append(createChannel(lowestUniqueNatural(pluck(
          'name',
          channels(state)
        ))))
      ),
      state
    )
    case ADD_EFFECT_TO_CHANNEL: {
      const thisEffectInstance = effectInstance(payload.name, state)
      const lastEffect = last(effects(channel(payload.channel, state)))
      if (!lastEffect) connectToAudioCtx(thisEffectInstance)
      else {
        thisEffectInstance.connect(effectInstanceDestination(
          lastEffect,
          state
        ))
      }
      forEach(
        name => disconnect(instrumentInstance(name, state))
          .connect(destination(thisEffectInstance)),
        instruments(channel(payload.channel, state))
      )
      return overChannelEffects(append(payload.name), payload.channel, state)
    }
    case ADD_INSTRUMENT_TO_CHANNEL: {
      const instrument = instance(find(
        propEq('name', payload.name),
        instrumentInstances(state)
      ))
      instrument.disconnect()
      const lastEffect = last(effects(channel(payload.channel, state)))
      if (!lastEffect) connectToAudioCtx(instrument)
      else instrument.connect(effectInstanceDestination(lastEffect, state))
      return overChannelInstruments(append(payload.name), payload.channel, state)
    }
    case INSTANTIATE_EFFECT: {
      const instance = new (findConstructor(
        payload.plugin,
        state.effectPlugins
      ))({audioContext})
      const thisChannel = channel(payload.channel, state)
      if (!thisChannel) connectToAudioCtx(instance)
      const thisChannelEffects = effects(thisChannel)
      if (isEmpty(thisChannelEffects)) connectToAudioCtx(instance)
      else {
        instance.connect(effectInstanceDestination(
          last(thisChannelEffects),
          state
        ))
      }
      return overChannelEffects(
        append(payload.name),
        payload.channel,
        overEffectInstances(append({
          instance,
          name: payload.name
        }), state)
      )
    }
    case INSTANTIATE_INSTRUMENT: {
      const instance = new (findConstructor(
        payload.plugin,
        state.instrumentPlugins
      ))({audioContext})
      connectToAudioCtx(instance)
      return overInstrumentInstances(
        append({instance, name: payload.name}),
        state
      )
    }
    case LOAD_PLUGIN_INSTRUMENT:
      return overInstrumentPlugins(append(payload), state)
    case LOAD_PLUGIN_EFFECT:
      return overEffectPlugins(append(payload), state)
    case REMOVE_CHANNEL: {
      const channel = findNameEquals(
        payload,
        channels(state)
      )
      forEach(compose(
        disconnect,
        x => effectInstance(x, state)
      ), effects(channel))
      forEach(compose(
        connectToAudioCtx,
        disconnect,
        flip(instrumentInstance)(state)
      ), instruments(channel))
      return overChannels(without([channel]), state)
    }
    case REMOVE_EFFECT_FROM_CHANNEL: {
      const channelEffects = effects(channel(payload.channel, state))
      const channelInstruments = instruments(channel(payload.channel, state))

      disconnect(effectInstance(payload.name, state))
      const effectIndex = findIndex(equals(payload.name), channelEffects)

      if (length(channelEffects) === 1) {
        forEach(
          name => connectToAudioCtx(instrumentInstance(name, state)),
          channelInstruments
        )
      } else if (effectIndex === dec(length(channelEffects))) {
        forEach(
          name => instrumentInstance(name, state)
            .connect(effectInstanceDestination(
              nth(dec(effectIndex), channelEffects),
              state
            )),
          channelInstruments
        )
      } else if (effectIndex === 0) {
        connectToAudioCtx(disconnect(effectInstance(
          nth(inc(effectIndex), channelEffects),
          state
        )))
      } else {
        disconnect(effectInstance(
          nth(inc(effectIndex), channelEffects),
          state
        )).connect(effectInstanceDestination(
          nth(dec(effectIndex), channelEffects),
          state
        ))
      }

      return overChannelEffects(without([payload.name]), payload.channel, state)
    }
    case REMOVE_INSTRUMENT_FROM_CHANNEL: {
      connectToAudioCtx(disconnect(instrumentInstance(payload.name, state)))
      return overChannelInstruments(
        without([payload.name]),
        payload.channel,
        state
      )
    }
    default: return state
  }
}
<|endoftext|>


<commit_before>require.config({
  paths: {
    "angular": "/bower_components/angular/angular.min",
    "angular-animate": "/bower_components/angular-animate/angular-animate.min",
    "angular-route": "/bower_components/angular-route/angular-route.min",
    "assert": "/bower_components/assert/dist/amd/assert",
    "gapi": "https://apis.google.com/js/client",
    "twitter-bootstrap": "/bower_components/bootstrap/dist/js/bootstrap.min",
    "jquery": "/bower_components/jquery/dist/jquery.min",
    "moment": "/bower_components/moment/min/moment-with-locales.min"
  },

  shim: {
    "angular": {
      deps: ["jquery"],
      exports: "angular"
    },
    "angular-animate": {
      deps: ["angular"]
    },
    "angular-route": {
      deps: ["angular"]
    },
    "gapi": {
      exports: "gapi"
    }
  }
});
<commit_msg>Made the array polyfil a dependeny of angular to ensure that its always loaded in the UI<commit_after>require.config({
  paths: {
    "angular": "/bower_components/angular/angular.min",
    "angular-animate": "/bower_components/angular-animate/angular-animate.min",
    "angular-route": "/bower_components/angular-route/angular-route.min",
    "assert": "/bower_components/assert/dist/amd/assert",
    "gapi": "https://apis.google.com/js/client",
    "twitter-bootstrap": "/bower_components/bootstrap/dist/js/bootstrap.min",
    "jquery": "/bower_components/jquery/dist/jquery.min",
    "moment": "/bower_components/moment/min/moment-with-locales.min"
  },

  shim: {
    "angular": {
      deps: ["jquery", "/dist/polyfill/Array.js"],
      exports: "angular"
    },
    "angular-animate": {
      deps: ["angular"]
    },
    "angular-route": {
      deps: ["angular"]
    },
    "gapi": {
      exports: "gapi"
    }
  }
});
<|endoftext|>


<commit_before>'use strict';

import TinyEmitter from 'tiny-emitter';
import * as _ from 'lodash';
import request from 'request';
import FeedParser from 'feedparser';
import Promise from 'bluebird';

class RssFeedEmitter extends TinyEmitter {

	constructor() {
		super();
		this._feedList = [];
	}

	// PUBLIC METHODS

	add(feed) {
		this._validateFeedObject(feed);

		let defaults = {
			refresh: 60000
		}

		feed = _.defaults(feed, defaults);

		this._addOrUpdateFeedList(feed);
		return this._feedList;
	}

	remove(url) {

		if ( typeof url !== 'string' ) {
			throw new Error('You must call #remove with a string containing the feed url');
		}

		let feed = this._findFeed({
			url: url
		});

		return this._removeFromFeedList(feed);

	}

	list() {
		return this._feedList;
	}

	destroy() {
		for (let i = this._feedList.length -1; i >=0; i--) {
			let feed = this._feedList[i];
			this._removeFromFeedList(feed);
		}
	}

	// PRIVATE METHODS

	_validateFeedObject(feed) {
		if ( !feed ) {
			throw new Error('You must call #add method with a feed configuration object.');
		}

		if ( !feed.url || typeof feed.url !== 'string' || feed.url === '' ) {
			throw new Error('Your configuration object should have an "url" key with a string value');
		}

		if ( feed.refresh && typeof feed.refresh !== 'number' ) {
			throw new Error('Your configuration object should have a "refresh" key with a number value');
		}
	}

	_addOrUpdateFeedList(feed) {

		let feedInList = this._findFeed(feed);

		if ( feedInList ) {
			this._removeFromFeedList(feedInList);
		}

		return this._addToFeedList(feed);

	}

	_findFeed(feed) {
		return _.find(this._feedList, { url: feed.url });
	}

	_findItem(feed, item) {
		if (!feed) {
			return;
		}

		return _.find(feed.items, { url: item.url });
	}

	_addToFeedList(feed) {

		if (!feed.items) {
			feed.items = [];
		}

		feed.setInterval = this._createSetInterval(feed);
		this._feedList.push(feed);

		return this._feedList;
	}

	_removeFromFeedList(feed) {

		if ( !feed || !feed.setInterval ) {
			return;
		}

		clearInterval(feed.setInterval);
		_.remove(this._feedList, { url: feed.url } );

	}

	_createSetInterval(feed) {

		let getContent = () => {
			this._fetchFeed(feed)
				.bind(this)
				.then(this._mapItemsData)
				.tap(this._redefineItemHistoryMaxLength)
				.then(this._sortItemsByDate)
				.then(this._identifyOnlyNewItems)
				.then(this._populateItemsInFeed)
				.catch((error) => {
					console.log('Error in getContent() chain inside _createSetInterval()');
					console.log(error.stack)
				})
		}

		getContent();

		return setInterval( getContent, feed.refresh );
	}

	_redefineItemHistoryMaxLength(items) {

		let maxLength = items.length;

		items.map( (item) => {

			let feed = this._findFeed({
				url: item.feed.url
			});

			if (!feed) {
				return;
			}

			feed.maxHistoryLength = maxLength;

		});

	}

	_sortItemsByDate(items) {
		return _.sortBy(items, 'date');
	}

	_populateItemsInFeed(items) {

		items.forEach((item) => {
			this._addItemToItemList(item);
		});

	}

	_addItemToItemList(item) {
		let feed = this._findFeed({
			url: item.feed.url
		});

		if (!feed) {
			return;
		}

		feed.items.push(item);
		feed.items = _.takeRight(feed.items, feed.maxHistoryLength);

		this.emit('new-item', item);

	}

	_identifyOnlyNewItems(items) {
		return items.filter((item) => {
			let feed = this._findFeed({
				url: item.feed.url
			});

			let itemFound = this._findItem(feed, item);

			return !itemFound;

		});
	}

	_mapItemsData(items) {
		return items.map((oldItem) => {
			let newItem = {
				title: oldItem.title,
				description: oldItem.description,
				summary: oldItem.summary,
				date: oldItem.pubdate,
				url: oldItem.link,
				author: oldItem.author,
				image: oldItem.image,
				feed: {
					url: oldItem.meta.xmlurl,
					title: oldItem.meta.title
				}
			}

			return newItem;
		})
	}

	_fetchFeed(feed) {

		return new Promise( (resolve, reject) => {

			let feedparser = new FeedParser();
			let items = [];

			request.get({
				url: feed.url,
				headers: {
					'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.63 Safari/537.36',
					'accept': 'text/html,application/xhtml+xml'
				}
			})
			.pipe(feedparser)
			.on('end', () => resolve(items) );

			feedparser.on('readable', () => {
				let item;

				while(item = feedparser.read()) {
					items.push(item);
				}

				return items;
			});

			feedparser.on('error', (error) => {
				reject(error);
			});

		})
	}

}

export default RssFeedEmitter;
<commit_msg>refactor(fetch): altera toda lógica do fetch<commit_after>'use strict';

import TinyEmitter from 'tiny-emitter';
import * as _ from 'lodash';
import request from 'request';
import FeedParser from 'feedparser';
import Promise from 'bluebird';

class RssFeedEmitter extends TinyEmitter {

	constructor() {
		super();
		this._feedList = [];
	}

	// PUBLIC METHODS

	add(feed) {
		this._validateFeedObject(feed);

		let defaults = {
			refresh: 60000
		}

		feed = _.defaults(feed, defaults);

		this._addOrUpdateFeedList(feed);
		return this._feedList;
	}

	remove(url) {

		if ( typeof url !== 'string' ) {
			throw new Error('You must call #remove with a string containing the feed url');
		}

		let feed = this._findFeed({
			url: url
		});

		return this._removeFromFeedList(feed);

	}

	list() {
		return this._feedList;
	}

	destroy() {
		for (let i = this._feedList.length -1; i >=0; i--) {
			let feed = this._feedList[i];
			this._removeFromFeedList(feed);
		}
	}

	// PRIVATE METHODS

	_validateFeedObject(feed) {
		if ( !feed ) {
			throw new Error('You must call #add method with a feed configuration object.');
		}

		if ( !feed.url || typeof feed.url !== 'string' || feed.url === '' ) {
			throw new Error('Your configuration object should have an "url" key with a string value');
		}

		if ( feed.refresh && typeof feed.refresh !== 'number' ) {
			throw new Error('Your configuration object should have a "refresh" key with a number value');
		}
	}

	_addOrUpdateFeedList(feed) {

		let feedInList = this._findFeed(feed);

		if ( feedInList ) {
			this._removeFromFeedList(feedInList);
		}

		return this._addToFeedList(feed);

	}

	_findFeed(feed) {
		return _.find(this._feedList, { url: feed.url });
	}

	_findItem(feed, item) {
		if (!feed) {
			return;
		}

		return _.find(feed.items, { link: item.link });
	}

	_addToFeedList(feed) {

		if (!feed.items) {
			feed.items = [];
		}

		feed.setInterval = this._createSetInterval(feed);
		this._feedList.push(feed);

		return this._feedList;
	}

	_removeFromFeedList(feed) {

		if ( !feed || !feed.setInterval ) {
			return;
		}

		clearInterval(feed.setInterval);
		_.remove(this._feedList, { url: feed.url } );

	}

	_createSetInterval(feed) {

		let getContent = () => {
			this._fetchFeed(feed.url)
				.bind(this)
				.tap(findFeedObject)
				.tap(redefineItemHistoryMaxLength)
				.tap(sortItemsByDate)
				.tap(identifyOnlyNewItems)
				.tap(populateNewItemsInFeed)
				.catch( (error) => {

					if (error.message === 'Feed not found') {
						return;
					}

					console.log('Error inside _createSetInterval() -> this._fetchFeed() chain');
					console.log(error.stack)

				})

				function findFeedObject(data) {

					let feed = this._findFeed({
						url: data.feedUrl
					});

					if (!feed) {
						throw new Error('Feed not found');
					}

					data.feed = feed;

					return data;

				}

				function redefineItemHistoryMaxLength(data) {

					let feedLength = data.items.length;

					data.feed.maxHistoryLength = feedLength * 3;

				}

				function sortItemsByDate(data) {

					data.items = _.sortBy(data.items, 'date');

				}

				function identifyOnlyNewItems(data) {

					data.newItems = data.items.filter((fetchedItem) => {

						let foundItemInsideFeed = this._findItem(data.feed, fetchedItem);

						if (foundItemInsideFeed) {
							return false;
						} else {
							return fetchedItem;
						}

					});

				}

				function populateNewItemsInFeed(data) {

					data.newItems.forEach((item) => {
						this._addItemToItemList(data.feed, item);
					});

				}

		}

		getContent();

		return setInterval( getContent, feed.refresh );
	}

	_addItemToItemList(feed, item) {

		feed.items.push(item);
		feed.items = _.takeRight(feed.items, feed.maxHistoryLength);

		this.emit('new-item', item);

	}

	_fetchFeed(feedUrl) {

		return new Promise( (resolve, reject) => {

			let feedparser = new FeedParser();

			let data = {
				feedUrl: feedUrl,
				items: []
			}

			request.get({
				url: feedUrl,
				headers: {
					'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.63 Safari/537.36',
					'accept': 'text/html,application/xhtml+xml'
				}
			})
			.pipe(feedparser)
			.on('end', () => resolve(data) );

			feedparser.on('readable', () => {
				let item;

				while(item = feedparser.read()) {

					// Force the feed URL inside the feed item
					item.meta.link = feedUrl;
					data.items.push(item);

				}

				return data;
			});

			feedparser.on('error', (error) => {
				reject(error);
			});

		})
	}

}

export default RssFeedEmitter;
<|endoftext|>


<commit_before>'use strict';

var cheerio = require('cheerio');

var map = {
    Produkt: 'products',
    Butikk: 'stores'
};

module.exports = function csvUrlParser(body, callback) {
    var $ = cheerio.load(body),
        urls = {},
        link, match, linkHref, type;

    $('.article-page__content a').each(function() {
        link = $(this);
        linkHref = link.attr('href');
        match = link.text().match(/(Produkt|Butikk)/);

        if (!linkHref.match(/\.csv/) || !match) {
            return;
        }

        type = map[match[1]];
        urls[type] = linkHref;
    });

    callback(
        Object.keys(urls).length > 1 ? null : new Error('CSV URLs not found'),
        urls
    );
};
<commit_msg>Remove unused scraper<commit_after><|endoftext|>


<commit_before>'use strict';

var $ = require('jquery');

var debounce = require('./lodash/debounce');

var Selectivity = require('./selectivity-base');

require('./selectivity-locale');

/**
 * Option listener that implements a convenience query function for performing AJAX requests.
 */
Selectivity.OptionListeners.unshift(function(selectivity, options) {

    var ajax = options.ajax;
    if (ajax && ajax.url) {
        var formatError = ajax.formatError || Selectivity.Locale.ajaxError;
        var minimumInputLength = ajax.minimumInputLength || 0;
        var params = ajax.params;
        var processItem = ajax.processItem || function(item) { return item; };
        var quietMillis = ajax.quietMillis || 0;
        var resultsCb = ajax.results || function(data) { return { results: data, more: false }; };
        var transport = ajax.transport || $.ajax;

        if (quietMillis) {
            transport = debounce(transport, quietMillis);
        }

        options.query = function(queryOptions) {
            var offset = queryOptions.offset;
            var term = queryOptions.term;
            if (term.length < minimumInputLength) {
                queryOptions.error(
                    Selectivity.Locale.needMoreCharacters(minimumInputLength - term.length)
                );
            } else {
                var url = (ajax.url instanceof Function ? ajax.url(queryOptions) : ajax.url);
                if (params) {
                    url += (url.indexOf('?') > -1 ? '&' : '?') + $.param(params(term, offset));
                }

                var success = ajax.success;
                var error = ajax.error;

                transport($.extend({}, ajax, {
                    url: url,
                    success: function(data, textStatus, jqXHR) {
                        if (success) {
                            success(data, textStatus, jqXHR);
                        }

                        var results = resultsCb(data, offset);
                        results.results = results.results.map(processItem);
                        queryOptions.callback(results);
                    },
                    error: function(jqXHR, textStatus, errorThrown) {
                        if (error) {
                            error(jqXHR, textStatus, errorThrown);
                        }

                        queryOptions.error(
                            formatError(term, jqXHR, textStatus, errorThrown),
                            { escape: false }
                        );
                    }
                }));
            }
        };
    }
});
<commit_msg>Use jQuery.map utility as we can deal with objects instead of arrays only<commit_after>'use strict';

var $ = require('jquery');

var debounce = require('./lodash/debounce');

var Selectivity = require('./selectivity-base');

require('./selectivity-locale');

/**
 * Option listener that implements a convenience query function for performing AJAX requests.
 */
Selectivity.OptionListeners.unshift(function(selectivity, options) {

    var ajax = options.ajax;
    if (ajax && ajax.url) {
        var formatError = ajax.formatError || Selectivity.Locale.ajaxError;
        var minimumInputLength = ajax.minimumInputLength || 0;
        var params = ajax.params;
        var processItem = ajax.processItem || function(item) { return item; };
        var quietMillis = ajax.quietMillis || 0;
        var resultsCb = ajax.results || function(data) { return { results: data, more: false }; };
        var transport = ajax.transport || $.ajax;

        if (quietMillis) {
            transport = debounce(transport, quietMillis);
        }

        options.query = function(queryOptions) {
            var offset = queryOptions.offset;
            var term = queryOptions.term;
            if (term.length < minimumInputLength) {
                queryOptions.error(
                    Selectivity.Locale.needMoreCharacters(minimumInputLength - term.length)
                );
            } else {
                var url = (ajax.url instanceof Function ? ajax.url(queryOptions) : ajax.url);
                if (params) {
                    url += (url.indexOf('?') > -1 ? '&' : '?') + $.param(params(term, offset));
                }

                var success = ajax.success;
                var error = ajax.error;

                transport($.extend({}, ajax, {
                    url: url,
                    success: function(data, textStatus, jqXHR) {
                        if (success) {
                            success(data, textStatus, jqXHR);
                        }

                        var results = resultsCb(data, offset);
                        results.results = $.map(results.results, processItem);
                        queryOptions.callback(results);
                    },
                    error: function(jqXHR, textStatus, errorThrown) {
                        if (error) {
                            error(jqXHR, textStatus, errorThrown);
                        }

                        queryOptions.error(
                            formatError(term, jqXHR, textStatus, errorThrown),
                            { escape: false }
                        );
                    }
                }));
            }
        };
    }
});
<|endoftext|>


<commit_before>
"use strict";

import Model from '../classes/Model';
import Collection from '../classes/Collection';

let http = undefined;
let cache = undefined;

function appendTransform(transform) {
    let defaults = http.defaults.transformResponse;
    defaults = angular.isArray(defaults) ? defaults : [defaults];
    return defaults.concat(transform);
};

/**
 * Default base Manager to extend upon.
 */
export default class Manager {

    /**
     * Class constructor.
     *
     * @param object $http Injected $http service.
     * @param object $cacheFactory Injector $cacheFactory service.
     * @return void
     */
    constructor($http, $cacheFactory) {
        http = $http;
        if (cache === undefined) {
            cache = $cacheFactory('monad-managers');
        }
        /**
         * Bindable pointer for specified list filters, if any.
         */
        this.filter = {};
    }

    /**
     * Apply the currently set filter to params.
     *
     * @param object params Hash of params to augment.
     * @return object Augmented params.
     */
    applyFilter(params) {
        if (this.filter) {
            for (let p in this.filter) {
                if (this.filter[p] !== false) {
                    params[p] = this.filter[p];
                }
            }
        }
        return params;
    }

    /**
     * Virtual getter for the cache object.
     */
    get cache() {
        return cache;
    }

    /**
     * Virtual count of total current list items. Here it returns 0; your custom
     * Manager _may_ offer an actual implementation of this (it depends on the
     * API you're using).
     *
     * @return integer Number of total items in the current list.
     */
    get count() {
        return 0;
    }

    /**
     * Retrieve a list (Collection) of items.
     *
     * @param string url The API URL to query.
     * @return Collection A collection of Models found.
     */
    list(url) {
        let collection = new this.constructor.Collection;
        collection.$promise = http({
            url,
            method: 'GET',
            transformResponse: appendTransform(values => {
                values.map(value => collection.push((new this.constructor.Model()).$load(value)));
                return collection;
            }),
            cache
        });
        return collection;
    }

    /**
     * Find a single item.
     *
     * @param string url The API URL to query.
     * @return Model A Model.
     */
    find(url) {
        let model = new this.constructor.Model;
        model.$promise = http({
            url,
            method: 'GET',
            transformResponse: appendTransform(item => model.$load(item)),
            cache
        });
        return model;
    }

    /**
     * Save the specified model back to the API.
     *
     * @param object model The model to save.
     * @return mixed On success, a Promise yielding the API's return value on
     *  `success`. On failure (because e.g. model is not dirty) an empty object.
     */
    save(model) {
        if (model.$new) {
            return this.create(model).success(() => model.$initial = model.$data);
        } else if (model.$deleted) {
            return this['delete'](model).success(() => {
                model.$data = {};
                model.$initial = undefined;
            });
        } else if (model.$dirty) {
            return this.update(model).success(() => model.$initial = model.$data);
        }
        return {};
    }

    /**
     * API interface. These should be overridden by a custom implementation,
     * since we have no way to guesstimate how your API works. Hence, these
     * throw an error as a friendly reminder :)
     *
     * The actual implementations should return promises.
     *
     * @param object model The model to create.
     */
    create(model) {
        throw "Manager.create must use a custom implementation.";
    }

    /**
     * API interface. These should be overridden by a custom implementation,
     * since we have no way to guesstimate how your API works. Hence, these
     * throw an error as a friendly reminder :)
     *
     * The actual implementations should return promises.
     *
     * @param object model The model to update.
     */
    update(model) {
        throw "Manager.update must use a custom implementation.";
    }

    /**
     * API interface. These should be overridden by a custom implementation,
     * since we have no way to guesstimate how your API works. Hence, these
     * throw an error as a friendly reminder :)
     *
     * The actual implementations should return promises.
     *
     * @param object model The model to delete.
     */
    ['delete'](model) {
        throw "Manager.delete must use a custom implementation.";
    }

    /**
     * Virtual property to access the $http service.
     *
     * @return object Angular $http service.
     */
    get http() {
        return http;
    }

};

Manager.$inject = ['$http', '$cacheFactory'];
/**
 * The current Model class to work on. You may set this per derived
 * manager where needed. Defaults to base Monad Model.
 */
Manager.Model = Model;
/**
 * The current Collection object to work on. You may set this per
 * derived manager where needed. Defaults to base Monad Collection.
 */
Manager.Collection = Collection;

<commit_msg>correctly reload the model on successfull saving<commit_after>
"use strict";

import Model from '../classes/Model';
import Collection from '../classes/Collection';

let http = undefined;
let cache = undefined;

function appendTransform(transform) {
    let defaults = http.defaults.transformResponse;
    defaults = angular.isArray(defaults) ? defaults : [defaults];
    return defaults.concat(transform);
};

/**
 * Default base Manager to extend upon.
 */
export default class Manager {

    /**
     * Class constructor.
     *
     * @param object $http Injected $http service.
     * @param object $cacheFactory Injector $cacheFactory service.
     * @return void
     */
    constructor($http, $cacheFactory) {
        http = $http;
        if (cache === undefined) {
            cache = $cacheFactory('monad-managers');
        }
        /**
         * Bindable pointer for specified list filters, if any.
         */
        this.filter = {};
    }

    /**
     * Apply the currently set filter to params.
     *
     * @param object params Hash of params to augment.
     * @return object Augmented params.
     */
    applyFilter(params) {
        if (this.filter) {
            for (let p in this.filter) {
                if (this.filter[p] !== false) {
                    params[p] = this.filter[p];
                }
            }
        }
        return params;
    }

    /**
     * Virtual getter for the cache object.
     */
    get cache() {
        return cache;
    }

    /**
     * Virtual count of total current list items. Here it returns 0; your custom
     * Manager _may_ offer an actual implementation of this (it depends on the
     * API you're using).
     *
     * @return integer Number of total items in the current list.
     */
    get count() {
        return 0;
    }

    /**
     * Retrieve a list (Collection) of items.
     *
     * @param string url The API URL to query.
     * @return Collection A collection of Models found.
     */
    list(url) {
        let collection = new this.constructor.Collection;
        collection.$promise = http({
            url,
            method: 'GET',
            transformResponse: appendTransform(values => {
                values.map(value => collection.push((new this.constructor.Model()).$load(value)));
                return collection;
            }),
            cache
        });
        return collection;
    }

    /**
     * Find a single item.
     *
     * @param string url The API URL to query.
     * @return Model A Model.
     */
    find(url) {
        let model = new this.constructor.Model;
        model.$promise = http({
            url,
            method: 'GET',
            transformResponse: appendTransform(item => model.$load(item)),
            cache
        });
        return model;
    }

    /**
     * Save the specified model back to the API.
     *
     * @param object model The model to save.
     * @return mixed On success, a Promise yielding the API's return value on
     *  `success`. On failure (because e.g. model is not dirty) an empty object.
     */
    save(model) {
        if (model.$new) {
            return this.create(model).then(result => model.$load(result.data));
        } else if (model.$deleted) {
            return this['delete'](model).then(() => {
                model.$data = {};
                model.$initial = undefined;
            });
        } else if (model.$dirty) {
            return this.update(model).then(result => model.$load(result.data));
        }
        return {};
    }

    /**
     * API interface. These should be overridden by a custom implementation,
     * since we have no way to guesstimate how your API works. Hence, these
     * throw an error as a friendly reminder :)
     *
     * The actual implementations should return promises.
     *
     * @param object model The model to create.
     */
    create(model) {
        throw "Manager.create must use a custom implementation.";
    }

    /**
     * API interface. These should be overridden by a custom implementation,
     * since we have no way to guesstimate how your API works. Hence, these
     * throw an error as a friendly reminder :)
     *
     * The actual implementations should return promises.
     *
     * @param object model The model to update.
     */
    update(model) {
        throw "Manager.update must use a custom implementation.";
    }

    /**
     * API interface. These should be overridden by a custom implementation,
     * since we have no way to guesstimate how your API works. Hence, these
     * throw an error as a friendly reminder :)
     *
     * The actual implementations should return promises.
     *
     * @param object model The model to delete.
     */
    ['delete'](model) {
        throw "Manager.delete must use a custom implementation.";
    }

    /**
     * Virtual property to access the $http service.
     *
     * @return object Angular $http service.
     */
    get http() {
        return http;
    }

};

Manager.$inject = ['$http', '$cacheFactory'];
/**
 * The current Model class to work on. You may set this per derived
 * manager where needed. Defaults to base Monad Model.
 */
Manager.Model = Model;
/**
 * The current Collection object to work on. You may set this per
 * derived manager where needed. Defaults to base Monad Collection.
 */
Manager.Collection = Collection;

<|endoftext|>


<commit_before>goog.provide('ngeo.filters');

goog.require('ngeo');
goog.require('ol.math');
goog.require('ol.string');

/**
 * Format a number as a localized scale.
 * For instance:
 *  - For 'fr-CH' the value 25000 will become '1 : 25 000'.
 *  - For 'en-US' the value 25000 will become '1 : 25,000'.
 *
 * Example:
 *
 *
{{25000 | ngeoScalify}}

 *
 *
 * @param {angular.$filter} $filter Angular filter
 * @return {function(number): string} A function to format number into a 'scale'
 *     string.
 * @ngInject
 * @ngdoc filter
 * @ngname ngeoScalify
 */
ngeo.Scalify = function($filter) {
  const numberFilter = $filter('number');
  return function(scale) {
    const text = numberFilter(scale, 0);
    return text ? `1\u00a0:\u00a0${text}` : '';
  };
};

ngeo.module.filter('ngeoScalify', ngeo.Scalify);

/**
 * A filter used to format a number with a precision, using the locale.
 *
 * Arguments:
 * - opt_precision: The used precision, default is 3.
 *
 * Examples:
 *
 *      {{0.1234 | ngeoNumber}} => 0.123
 *      {{1.234 | ngeoNumber}} => 1.23
 *      {{12.34 | ngeoNumber}} => 12.3
 *      {{123.4 | ngeoNumber}} => 123
 *      {{1234 | ngeoNumber}} => 1230
 *
 * @param {angular.$locale} $locale Angular locale
 * @return {ngeox.number} Function used to format number into a string.
 * @ngInject
 * @ngdoc filter
 * @ngname ngeoNumber
 */
ngeo.Number = function($locale) {
  const formats = $locale.NUMBER_FORMATS;

  /**
   * @param {number} number The number to format.
   * @param {number=} opt_precision The used precision, default is 3.
   * @return {string} The formatted string.
   */
  const result = function(number, opt_precision) {
    const groupSep = formats.GROUP_SEP;
    const decimalSep = formats.DECIMAL_SEP;
    if (opt_precision === undefined) {
      opt_precision = 3;
    }

    if (number === Infinity) {
      return '\u221e';
    } else if (number === -Infinity) {
      return '-\u221e';
    } else if (number === 0) {
      // 0 will creates infinity values
      return '0';
    }
    const sign = number < 0;
    number = Math.abs(number);

    const nb_decimal = opt_precision - Math.floor(Math.log(number) / Math.log(10)) - 1;
    const factor = Math.pow(10, nb_decimal);
    number = Math.round(number * factor);
    let decimal = '';
    const unit = Math.floor(number / factor);

    if (nb_decimal > 0) {
      let str_number = `${number}`;
      // 0 padding
      while (str_number.length < nb_decimal) {
        str_number = `0${str_number}`;
      }
      decimal = str_number.substring(str_number.length - nb_decimal);
      while (decimal[decimal.length - 1] === '0') {
        decimal = decimal.substring(0, decimal.length - 1);
      }
    }

    const groups = [];
    let str_unit = `${unit}`;
    while (str_unit.length > 3) {
      const index = str_unit.length - 3;
      groups.unshift(str_unit.substring(index));
      str_unit = str_unit.substring(0, index);
    }
    groups.unshift(str_unit);

    return (sign ? '-' : '') + groups.join(groupSep) + (
      decimal.length === 0 ? '' : decimalSep + decimal
    );
  };
  return result;
};

ngeo.module.filter('ngeoNumber', ngeo.Number);

/**
 * A filter used to format a number with the prefix and unit
 *
 * Arguments:
 * - opt_unit: The unit to used, default is ''.
 * - opt_type: (unit|square|binary) the type of units, default is 'unit'.
 * - opt_precision: The used precision, default is 3.
 *
 * Examples:
 *
 *      {{25000 | ngeoUnitPrefix}} => 25 k
 *      {{25000 | ngeoUnitPrefix:m}} => 25 km
 *      {{25000000 | ngeoUnitPrefix:m²:square}} => 25 km²
 *      {{2048 | ngeoUnitPrefix:o:binary}} => 2 Kio
 *
 *
 * @param {angular.$filter} $filter Angular filter
 * @return {ngeox.unitPrefix} Function used to format number into a string.
 * @ngInject
 * @ngdoc filter
 * @ngname ngeoUnitPrefix
 */
ngeo.UnitPrefix = function($filter) {
  const numberFilter = $filter('ngeoNumber');
  const standardPrefix = ['', 'k', 'M', 'G', 'T', 'P'];
  const binaryPrefix = ['', 'Ki', 'Mi', 'Gi', 'Ti', 'Pi'];
  /**
   * @param {number} number The number to format.
   * @param {string=} opt_unit The unit to used, default is ''.
   * @param {string=} opt_type (unit|square|binary) the type of units, default is 'unit'.
   * @param {number=} opt_precision The used precision, default is 3.
   * @return {string} The formated string.
   */
  const result = function(number, opt_unit, opt_type, opt_precision) {
    if (opt_unit === undefined) {
      opt_unit = '';
    }
    let divisor = 1000;
    let prefix = standardPrefix;
    if (opt_type === 'square') {
      divisor = 1000000;
    } else if (opt_type === 'binary') {
      divisor = 1024;
      prefix = binaryPrefix;
    }

    let index = 0;
    const index_max = prefix.length - 1;
    while (number >= divisor && index < index_max) {
      number = number / divisor;
      index++;
    }

    const postfix = prefix[index] + opt_unit;
    const space = postfix.length == 0 ? '' : '\u00a0';
    return numberFilter(number, opt_precision) + space + postfix;
  };
  return result;
};

ngeo.module.filter('ngeoUnitPrefix', ngeo.UnitPrefix);

/**
 * Format a couple of numbers as number coordinates.
 *
 * Example without parameters (en-US localization):
 *
 *
{{[7.1234, 46.9876] | ngeoNumberCoordinates}}

 *
 *
 * Example with defined fractionDigits and template (en-US localization):
 *
 *
{{[7.1234, 46.9876] | ngeoNumberCoordinates:2:'co {x} E; {y} N'}}

 *
 *
 * Example without fractionDigits but with defined template and localize:
 *
 *
 *
{{[2600000, 1600000] | ngeoNumberCoordinates:'{x}, {y}'}}

 *
 *

 *
 *
{{[2600000, 1600000] | ngeoNumberCoordinates:'{x}, {y}'}}

 *
 *

 *
 *
{{[2600000, 1600000] | ngeoNumberCoordinates:'{x}, {y}'}}

 *
 *
 * @param {angular.$filter} $filter Angular filter
 * @return {function(ol.Coordinate, (number|string)=, string=,
 *     (boolean|string)=): string} A function to format numbers into
 *     coordinates string.
 * @ngInject
 * @ngdoc filter
 * @ngname ngeoNumberCoordinates
 */
ngeo.NumberCoordinates = function($filter) {
  /**
   * @param {ol.Coordinate} coordinates Array of two numbers.
   * @param {(number|string)=} opt_fractionDigits Optional number of digit.
   *     Default to 0.
   * @param {string=} opt_template Optional template. Default to '{x} {y}'.
   *     Where "{x}" will be replaced by the first coordinate and "{y}" by the
   *     second one. Note: Use a html entity to use the semicolon symbole
   *     into a template.
   * @return {string} Number formated coordinates.
   */
  const filterFn = function(coordinates, opt_fractionDigits, opt_template) {
    const template = opt_template ? opt_template : '{x} {y}';
    let x = coordinates[0];
    let y = coordinates[1];
    const fractionDigits = parseInt(opt_fractionDigits, 10) | 0;
    x = $filter('number')(x, fractionDigits);
    y = $filter('number')(y, fractionDigits);
    return template.replace('{x}', x).replace('{y}', y);
  };
  return filterFn;
};

ngeo.module.filter('ngeoNumberCoordinates', ngeo.NumberCoordinates);

/**
 * Format a couple of numbers as DMS coordinates.
 *
 * Example without parameters:
 *
 *
{{[7.1234, 46.9876] | ngeoDMSCoordinates}}

 *
 *
 * Example with defined fractionDigits and a template.
 *
 *
{{[7.1234, 46.9876] | ngeoDMSCoordinates:2:'[{x}; {y}]'}}

 *
 *
 * @return {function(ol.Coordinate, (number|string)=, string=): string} A
 *     function to format numbers into a DMS coordinates string.
 * @ngInject
 * @ngdoc filter
 * @ngname ngeoDMSCoordinates
 */
ngeo.DMSCoordinates = function() {
  const degreesToStringHDMS = function(degrees, hemispheres, fractionDigits) {
    const normalizedDegrees = ol.math.modulo(degrees + 180, 360) - 180;
    const dms = Math.abs(3600 * normalizedDegrees);
    const d = Math.floor(dms / 3600);
    const m = Math.floor((dms / 60) % 60);
    const s = (dms % 60);
    return `${d}\u00b0 ${
        ol.string.padNumber(m, 2)}\u2032 ${
        ol.string.padNumber(s, 2, fractionDigits)}\u2033 ${
        hemispheres.charAt(normalizedDegrees < 0 ? 1 : 0)}`;
  };

  /**
   * @param {ol.Coordinate} coordinates Array of two numbers.
   * @param {(number|string)=} opt_fractionDigits Optional number of digit.
   *     Default to 0.
   * @param {string=} opt_template Optional template. Default to
   *     '{x} {y}'. Where "{x}" will be replaced by the first
   *     coordinate, {y} by the second one. Note: Use a html entity to use the
   *     semicolon symbole into a template.
   * @return {string} DMS formated coordinates.
   */
  const filterFn = function(coordinates, opt_fractionDigits, opt_template) {
    const fractionDigits = parseInt(opt_fractionDigits, 10) | 0;

    const template = opt_template ? opt_template : '{x} {y}';

    const xdms = degreesToStringHDMS(coordinates[1], 'NS', fractionDigits);
    const ydms = degreesToStringHDMS(coordinates[0], 'EW', fractionDigits);

    return template.replace('{x}', xdms).replace('{y}', ydms);
  };

  return filterFn;
};

ngeo.module.filter('ngeoDMSCoordinates', ngeo.DMSCoordinates);

/**
 * A filter to mark a value as trusted HTML.
 *
 * Usage:
 *
 *

 *
 * @return {function(?):string} The filter function.
 * @ngInject
 * @ngdoc filter
 * @param {angular.$sce} $sce Angular sce service.
 * @ngname ngeoTrustHtml
 */
ngeo.trustHtmlFilter = function($sce) {
  return function(input) {
    if (input !== undefined && input !== null) {
      return $sce.trustAsHtml(`${input}`);
    } else {
      return $sce.trustAsHtml(' ');
    }
  };
};

ngeo.module.filter('ngeoTrustHtml', ngeo.trustHtmlFilter);
<commit_msg>Fix the ngeoNumberCoordinates filter documentation<commit_after>goog.provide('ngeo.filters');

goog.require('ngeo');
goog.require('ol.math');
goog.require('ol.string');

/**
 * Format a number as a localized scale.
 * For instance:
 *  - For 'fr-CH' the value 25000 will become '1 : 25 000'.
 *  - For 'en-US' the value 25000 will become '1 : 25,000'.
 *
 * Example:
 *
 *
{{25000 | ngeoScalify}}

 *
 *
 * @param {angular.$filter} $filter Angular filter
 * @return {function(number): string} A function to format number into a 'scale'
 *     string.
 * @ngInject
 * @ngdoc filter
 * @ngname ngeoScalify
 */
ngeo.Scalify = function($filter) {
  const numberFilter = $filter('number');
  return function(scale) {
    const text = numberFilter(scale, 0);
    return text ? `1\u00a0:\u00a0${text}` : '';
  };
};

ngeo.module.filter('ngeoScalify', ngeo.Scalify);

/**
 * A filter used to format a number with a precision, using the locale.
 *
 * Arguments:
 * - opt_precision: The used precision, default is 3.
 *
 * Examples:
 *
 *      {{0.1234 | ngeoNumber}} => 0.123
 *      {{1.234 | ngeoNumber}} => 1.23
 *      {{12.34 | ngeoNumber}} => 12.3
 *      {{123.4 | ngeoNumber}} => 123
 *      {{1234 | ngeoNumber}} => 1230
 *
 * @param {angular.$locale} $locale Angular locale
 * @return {ngeox.number} Function used to format number into a string.
 * @ngInject
 * @ngdoc filter
 * @ngname ngeoNumber
 */
ngeo.Number = function($locale) {
  const formats = $locale.NUMBER_FORMATS;

  /**
   * @param {number} number The number to format.
   * @param {number=} opt_precision The used precision, default is 3.
   * @return {string} The formatted string.
   */
  const result = function(number, opt_precision) {
    const groupSep = formats.GROUP_SEP;
    const decimalSep = formats.DECIMAL_SEP;
    if (opt_precision === undefined) {
      opt_precision = 3;
    }

    if (number === Infinity) {
      return '\u221e';
    } else if (number === -Infinity) {
      return '-\u221e';
    } else if (number === 0) {
      // 0 will creates infinity values
      return '0';
    }
    const sign = number < 0;
    number = Math.abs(number);

    const nb_decimal = opt_precision - Math.floor(Math.log(number) / Math.log(10)) - 1;
    const factor = Math.pow(10, nb_decimal);
    number = Math.round(number * factor);
    let decimal = '';
    const unit = Math.floor(number / factor);

    if (nb_decimal > 0) {
      let str_number = `${number}`;
      // 0 padding
      while (str_number.length < nb_decimal) {
        str_number = `0${str_number}`;
      }
      decimal = str_number.substring(str_number.length - nb_decimal);
      while (decimal[decimal.length - 1] === '0') {
        decimal = decimal.substring(0, decimal.length - 1);
      }
    }

    const groups = [];
    let str_unit = `${unit}`;
    while (str_unit.length > 3) {
      const index = str_unit.length - 3;
      groups.unshift(str_unit.substring(index));
      str_unit = str_unit.substring(0, index);
    }
    groups.unshift(str_unit);

    return (sign ? '-' : '') + groups.join(groupSep) + (
      decimal.length === 0 ? '' : decimalSep + decimal
    );
  };
  return result;
};

ngeo.module.filter('ngeoNumber', ngeo.Number);

/**
 * A filter used to format a number with the prefix and unit
 *
 * Arguments:
 * - opt_unit: The unit to used, default is ''.
 * - opt_type: (unit|square|binary) the type of units, default is 'unit'.
 * - opt_precision: The used precision, default is 3.
 *
 * Examples:
 *
 *      {{25000 | ngeoUnitPrefix}} => 25 k
 *      {{25000 | ngeoUnitPrefix:m}} => 25 km
 *      {{25000000 | ngeoUnitPrefix:m²:square}} => 25 km²
 *      {{2048 | ngeoUnitPrefix:o:binary}} => 2 Kio
 *
 *
 * @param {angular.$filter} $filter Angular filter
 * @return {ngeox.unitPrefix} Function used to format number into a string.
 * @ngInject
 * @ngdoc filter
 * @ngname ngeoUnitPrefix
 */
ngeo.UnitPrefix = function($filter) {
  const numberFilter = $filter('ngeoNumber');
  const standardPrefix = ['', 'k', 'M', 'G', 'T', 'P'];
  const binaryPrefix = ['', 'Ki', 'Mi', 'Gi', 'Ti', 'Pi'];
  /**
   * @param {number} number The number to format.
   * @param {string=} opt_unit The unit to used, default is ''.
   * @param {string=} opt_type (unit|square|binary) the type of units, default is 'unit'.
   * @param {number=} opt_precision The used precision, default is 3.
   * @return {string} The formated string.
   */
  const result = function(number, opt_unit, opt_type, opt_precision) {
    if (opt_unit === undefined) {
      opt_unit = '';
    }
    let divisor = 1000;
    let prefix = standardPrefix;
    if (opt_type === 'square') {
      divisor = 1000000;
    } else if (opt_type === 'binary') {
      divisor = 1024;
      prefix = binaryPrefix;
    }

    let index = 0;
    const index_max = prefix.length - 1;
    while (number >= divisor && index < index_max) {
      number = number / divisor;
      index++;
    }

    const postfix = prefix[index] + opt_unit;
    const space = postfix.length == 0 ? '' : '\u00a0';
    return numberFilter(number, opt_precision) + space + postfix;
  };
  return result;
};

ngeo.module.filter('ngeoUnitPrefix', ngeo.UnitPrefix);

/**
 * Format a couple of numbers as number coordinates.
 *
 * Example without parameters (en-US localization):
 *
 *
{{[7.1234, 46.9876] | ngeoNumberCoordinates}}

 *
 *
 * Example with defined fractionDigits and template (en-US localization):
 *
 *
{{[7.1234, 46.9876] | ngeoNumberCoordinates:2:'co {x} E; {y} N'}}

 *
 *
 * Example without fractionDigits but with defined template and localize:
 *
 *
 *
{{[2600000, 1600000] | ngeoNumberCoordinates:0:'{x}, {y}'}}

 *
 *

 *
 *
{{[2600000, 1600000] | ngeoNumberCoordinates:0:'{x}, {y}'}}

 *
 *

 *
 *
{{[2600000, 1600000] | ngeoNumberCoordinates:0:'{x}, {y}'}}

 *
 *
 * @param {angular.$filter} $filter Angular filter
 * @return {function(ol.Coordinate, (number|string)=, string=,
 *     (boolean|string)=): string} A function to format numbers into
 *     coordinates string.
 * @ngInject
 * @ngdoc filter
 * @ngname ngeoNumberCoordinates
 */
ngeo.NumberCoordinates = function($filter) {
  /**
   * @param {ol.Coordinate} coordinates Array of two numbers.
   * @param {(number|string)=} opt_fractionDigits Optional number of digit.
   *     Default to 0.
   * @param {string=} opt_template Optional template. Default to '{x} {y}'.
   *     Where "{x}" will be replaced by the first coordinate and "{y}" by the
   *     second one. Note: Use a html entity to use the semicolon symbole
   *     into a template.
   * @return {string} Number formated coordinates.
   */
  const filterFn = function(coordinates, opt_fractionDigits, opt_template) {
    const template = opt_template ? opt_template : '{x} {y}';
    let x = coordinates[0];
    let y = coordinates[1];
    const fractionDigits = parseInt(opt_fractionDigits, 10) | 0;
    x = $filter('number')(x, fractionDigits);
    y = $filter('number')(y, fractionDigits);
    return template.replace('{x}', x).replace('{y}', y);
  };
  return filterFn;
};

ngeo.module.filter('ngeoNumberCoordinates', ngeo.NumberCoordinates);

/**
 * Format a couple of numbers as DMS coordinates.
 *
 * Example without parameters:
 *
 *
{{[7.1234, 46.9876] | ngeoDMSCoordinates}}

 *
 *
 * Example with defined fractionDigits and a template.
 *
 *
{{[7.1234, 46.9876] | ngeoDMSCoordinates:2:'[{x}; {y}]'}}

 *
 *
 * @return {function(ol.Coordinate, (number|string)=, string=): string} A
 *     function to format numbers into a DMS coordinates string.
 * @ngInject
 * @ngdoc filter
 * @ngname ngeoDMSCoordinates
 */
ngeo.DMSCoordinates = function() {
  const degreesToStringHDMS = function(degrees, hemispheres, fractionDigits) {
    const normalizedDegrees = ol.math.modulo(degrees + 180, 360) - 180;
    const dms = Math.abs(3600 * normalizedDegrees);
    const d = Math.floor(dms / 3600);
    const m = Math.floor((dms / 60) % 60);
    const s = (dms % 60);
    return `${d}\u00b0 ${
        ol.string.padNumber(m, 2)}\u2032 ${
        ol.string.padNumber(s, 2, fractionDigits)}\u2033 ${
        hemispheres.charAt(normalizedDegrees < 0 ? 1 : 0)}`;
  };

  /**
   * @param {ol.Coordinate} coordinates Array of two numbers.
   * @param {(number|string)=} opt_fractionDigits Optional number of digit.
   *     Default to 0.
   * @param {string=} opt_template Optional template. Default to
   *     '{x} {y}'. Where "{x}" will be replaced by the first
   *     coordinate, {y} by the second one. Note: Use a html entity to use the
   *     semicolon symbole into a template.
   * @return {string} DMS formated coordinates.
   */
  const filterFn = function(coordinates, opt_fractionDigits, opt_template) {
    const fractionDigits = parseInt(opt_fractionDigits, 10) | 0;

    const template = opt_template ? opt_template : '{x} {y}';

    const xdms = degreesToStringHDMS(coordinates[1], 'NS', fractionDigits);
    const ydms = degreesToStringHDMS(coordinates[0], 'EW', fractionDigits);

    return template.replace('{x}', xdms).replace('{y}', ydms);
  };

  return filterFn;
};

ngeo.module.filter('ngeoDMSCoordinates', ngeo.DMSCoordinates);

/**
 * A filter to mark a value as trusted HTML.
 *
 * Usage:
 *
 *

 *
 * @return {function(?):string} The filter function.
 * @ngInject
 * @ngdoc filter
 * @param {angular.$sce} $sce Angular sce service.
 * @ngname ngeoTrustHtml
 */
ngeo.trustHtmlFilter = function($sce) {
  return function(input) {
    if (input !== undefined && input !== null) {
      return $sce.trustAsHtml(`${input}`);
    } else {
      return $sce.trustAsHtml(' ');
    }
  };
};

ngeo.module.filter('ngeoTrustHtml', ngeo.trustHtmlFilter);
<|endoftext|>


<commit_before>module.exports = {
  'Num1': 'AAAAAQAAAAEAAAAAAw==',
  'Num2': 'AAAAAQAAAAEAAAABAw==',
  'Num3': 'AAAAAQAAAAEAAAACAw==',
  'Num4': 'AAAAAQAAAAEAAAADAw==',
  'Num5': 'AAAAAQAAAAEAAAAEAw==',
  'Num6': 'AAAAAQAAAAEAAAAFAw==',
  'Num7': 'AAAAAQAAAAEAAAAGAw==',
  'Num8': 'AAAAAQAAAAEAAAAHAw==',
  'Num9': 'AAAAAQAAAAEAAAAIAw==',
  'Num0': 'AAAAAQAAAAEAAAAJAw==',
  'Num11': 'AAAAAQAAAAEAAAAKAw==',
  'Num12': 'AAAAAQAAAAEAAAALAw==',
  'Enter': 'AAAAAQAAAAEAAAALAw==',
  'GGuide': 'AAAAAQAAAAEAAAAOAw==',
  'ChannelUp': 'AAAAAQAAAAEAAAAQAw==',
  'ChannelDown': 'AAAAAQAAAAEAAAARAw==',
  'VolumeUp': 'AAAAAQAAAAEAAAASAw==',
  'VolumeDown': 'AAAAAQAAAAEAAAATAw==',
  'Mute': 'AAAAAQAAAAEAAAAUAw==',
  'TvPower': 'AAAAAQAAAAEAAAAVAw==',
  'Audio': 'AAAAAQAAAAEAAAAXAw==',
  'MediaAudioTrack': 'AAAAAQAAAAEAAAAXAw==',
  'Tv': 'AAAAAQAAAAEAAAAkAw==',
  'Input': 'AAAAAQAAAAEAAAAlAw==',
  'TvInput': 'AAAAAQAAAAEAAAAlAw==',
  'TvAntennaCable': 'AAAAAQAAAAEAAAAqAw==',
  'WakeUp': 'AAAAAQAAAAEAAAAuAw==',
  'PowerOff': 'AAAAAQAAAAEAAAAvAw==',
  'Sleep': 'AAAAAQAAAAEAAAAvAw==',
  'Right': 'AAAAAQAAAAEAAAAzAw==',
  'Left': 'AAAAAQAAAAEAAAA0Aw==',
  'SleepTimer': 'AAAAAQAAAAEAAAA2Aw==',
  'Analog2': 'AAAAAQAAAAEAAAA4Aw==',
  'TvAnalog': 'AAAAAQAAAAEAAAA4Aw==',
  'Display': 'AAAAAQAAAAEAAAA6Aw==',
  'Jump': 'AAAAAQAAAAEAAAA7Aw==',
  'PicOff': 'AAAAAQAAAAEAAAA+Aw==',
  'PictureOff': 'AAAAAQAAAAEAAAA+Aw==',
  'Teletext': 'AAAAAQAAAAEAAAA/Aw==',
  'Video1': 'AAAAAQAAAAEAAABAAw==',
  'Video2': 'AAAAAQAAAAEAAABBAw==',
  'AnalogRgb1': 'AAAAAQAAAAEAAABDAw==',
  'Home': 'AAAAAQAAAAEAAABgAw==',
  'Exit': 'AAAAAQAAAAEAAABjAw==',
  'PictureMode': 'AAAAAQAAAAEAAABkAw==',
  'Confirm': 'AAAAAQAAAAEAAABlAw==',
  'Up': 'AAAAAQAAAAEAAAB0Aw==',
  'Down': 'AAAAAQAAAAEAAAB1Aw==',
  'ClosedCaption': 'AAAAAgAAAKQAAAAQAw==',
  'Component1': 'AAAAAgAAAKQAAAA2Aw==',
  'Component2': 'AAAAAgAAAKQAAAA3Aw==',
  'Wide': 'AAAAAgAAAKQAAAA9Aw==',
  'EPG': 'AAAAAgAAAKQAAABbAw==',
  'PAP': 'AAAAAgAAAKQAAAB3Aw==',
  'TenKey': 'AAAAAgAAAJcAAAAMAw==',
  'BSCS': 'AAAAAgAAAJcAAAAQAw==',
  'Ddata': 'AAAAAgAAAJcAAAAVAw==',
  'Stop': 'AAAAAgAAAJcAAAAYAw==',
  'Pause': 'AAAAAgAAAJcAAAAZAw==',
  'Play': 'AAAAAgAAAJcAAAAaAw==',
  'Rewind': 'AAAAAgAAAJcAAAAbAw==',
  'Forward': 'AAAAAgAAAJcAAAAcAw==',
  'DOT': 'AAAAAgAAAJcAAAAdAw==',
  'Rec': 'AAAAAgAAAJcAAAAgAw==',
  'Return': 'AAAAAgAAAJcAAAAjAw==',
  'Blue': 'AAAAAgAAAJcAAAAkAw==',
  'Red': 'AAAAAgAAAJcAAAAlAw==',
  'Green': 'AAAAAgAAAJcAAAAmAw==',
  'Yellow': 'AAAAAgAAAJcAAAAnAw==',
  'SubTitle': 'AAAAAgAAAJcAAAAoAw==',
  'CS': 'AAAAAgAAAJcAAAArAw==',
  'BS': 'AAAAAgAAAJcAAAAsAw==',
  'Digital': 'AAAAAgAAAJcAAAAyAw==',
  'Options': 'AAAAAgAAAJcAAAA2Aw==',
  'Media': 'AAAAAgAAAJcAAAA4Aw==',
  'Prev': 'AAAAAgAAAJcAAAA8Aw==',
  'Next': 'AAAAAgAAAJcAAAA9Aw==',
  'DpadCenter': 'AAAAAgAAAJcAAABKAw==',
  'CursorUp': 'AAAAAgAAAJcAAABPAw==',
  'CursorDown': 'AAAAAgAAAJcAAABQAw==',
  'CursorLeft': 'AAAAAgAAAJcAAABNAw==',
  'CursorRight': 'AAAAAgAAAJcAAABOAw==',
  'ShopRemoteControlForcedDynamic': 'AAAAAgAAAJcAAABqAw==',
  'FlashPlus': 'AAAAAgAAAJcAAAB4Aw==',
  'FlashMinus': 'AAAAAgAAAJcAAAB5Aw==',
  'AudioQualityMode': 'AAAAAgAAAJcAAAB7Aw==',
  'DemoMode': 'AAAAAgAAAJcAAAB8Aw==',
  'Analog': 'AAAAAgAAAHcAAAANAw==',
  'Mode3D': 'AAAAAgAAAHcAAABNAw==',
  'DigitalToggle': 'AAAAAgAAAHcAAABSAw==',
  'DemoSurround': 'AAAAAgAAAHcAAAB7Aw==',
  '*AD': 'AAAAAgAAABoAAAA7Aw==',
  'AudioMixUp': 'AAAAAgAAABoAAAA8Aw==',
  'AudioMixDown': 'AAAAAgAAABoAAAA9Aw==',
  'PhotoFrame': 'AAAAAgAAABoAAABVAw==',
  'Tv_Radio': 'AAAAAgAAABoAAABXAw==',
  'SyncMenu': 'AAAAAgAAABoAAABYAw==',
  'Hdmi1': 'AAAAAgAAABoAAABaAw==',
  'Hdmi2': 'AAAAAgAAABoAAABbAw==',
  'Hdmi3': 'AAAAAgAAABoAAABcAw==',
  'Hdmi4': 'AAAAAgAAABoAAABdAw==',
  'TopMenu': 'AAAAAgAAABoAAABgAw==',
  'PopUpMenu': 'AAAAAgAAABoAAABhAw==',
  'OneTouchTimeRec': 'AAAAAgAAABoAAABkAw==',
  'OneTouchView': 'AAAAAgAAABoAAABlAw==',
  'DUX': 'AAAAAgAAABoAAABzAw==',
  'FootballMode': 'AAAAAgAAABoAAAB2Aw==',
  'iManual': 'AAAAAgAAABoAAAB7Aw==',
  'Netflix': 'AAAAAgAAABoAAAB8Aw==',
  'Assists': 'AAAAAgAAAMQAAAA7Aw==',
  'ActionMenu': 'AAAAAgAAAMQAAABLAw==',
  'Help': 'AAAAAgAAAMQAAABNAw==',
  'TvSatellite': 'AAAAAgAAAMQAAABOAw==',
  'WirelessSubwoofer': 'AAAAAgAAAMQAAAB+Aw=='
};
<commit_msg>adding comments<commit_after>// Not used - left in for reference
module.exports = {
  'Num1': 'AAAAAQAAAAEAAAAAAw==',
  'Num2': 'AAAAAQAAAAEAAAABAw==',
  'Num3': 'AAAAAQAAAAEAAAACAw==',
  'Num4': 'AAAAAQAAAAEAAAADAw==',
  'Num5': 'AAAAAQAAAAEAAAAEAw==',
  'Num6': 'AAAAAQAAAAEAAAAFAw==',
  'Num7': 'AAAAAQAAAAEAAAAGAw==',
  'Num8': 'AAAAAQAAAAEAAAAHAw==',
  'Num9': 'AAAAAQAAAAEAAAAIAw==',
  'Num0': 'AAAAAQAAAAEAAAAJAw==',
  'Num11': 'AAAAAQAAAAEAAAAKAw==',
  'Num12': 'AAAAAQAAAAEAAAALAw==',
  'Enter': 'AAAAAQAAAAEAAAALAw==',
  'GGuide': 'AAAAAQAAAAEAAAAOAw==',
  'ChannelUp': 'AAAAAQAAAAEAAAAQAw==',
  'ChannelDown': 'AAAAAQAAAAEAAAARAw==',
  'VolumeUp': 'AAAAAQAAAAEAAAASAw==',
  'VolumeDown': 'AAAAAQAAAAEAAAATAw==',
  'Mute': 'AAAAAQAAAAEAAAAUAw==',
  'TvPower': 'AAAAAQAAAAEAAAAVAw==',
  'Audio': 'AAAAAQAAAAEAAAAXAw==',
  'MediaAudioTrack': 'AAAAAQAAAAEAAAAXAw==',
  'Tv': 'AAAAAQAAAAEAAAAkAw==',
  'Input': 'AAAAAQAAAAEAAAAlAw==',
  'TvInput': 'AAAAAQAAAAEAAAAlAw==',
  'TvAntennaCable': 'AAAAAQAAAAEAAAAqAw==',
  'WakeUp': 'AAAAAQAAAAEAAAAuAw==',
  'PowerOff': 'AAAAAQAAAAEAAAAvAw==',
  'Sleep': 'AAAAAQAAAAEAAAAvAw==',
  'Right': 'AAAAAQAAAAEAAAAzAw==',
  'Left': 'AAAAAQAAAAEAAAA0Aw==',
  'SleepTimer': 'AAAAAQAAAAEAAAA2Aw==',
  'Analog2': 'AAAAAQAAAAEAAAA4Aw==',
  'TvAnalog': 'AAAAAQAAAAEAAAA4Aw==',
  'Display': 'AAAAAQAAAAEAAAA6Aw==',
  'Jump': 'AAAAAQAAAAEAAAA7Aw==',
  'PicOff': 'AAAAAQAAAAEAAAA+Aw==',
  'PictureOff': 'AAAAAQAAAAEAAAA+Aw==',
  'Teletext': 'AAAAAQAAAAEAAAA/Aw==',
  'Video1': 'AAAAAQAAAAEAAABAAw==',
  'Video2': 'AAAAAQAAAAEAAABBAw==',
  'AnalogRgb1': 'AAAAAQAAAAEAAABDAw==',
  'Home': 'AAAAAQAAAAEAAABgAw==',
  'Exit': 'AAAAAQAAAAEAAABjAw==',
  'PictureMode': 'AAAAAQAAAAEAAABkAw==',
  'Confirm': 'AAAAAQAAAAEAAABlAw==',
  'Up': 'AAAAAQAAAAEAAAB0Aw==',
  'Down': 'AAAAAQAAAAEAAAB1Aw==',
  'ClosedCaption': 'AAAAAgAAAKQAAAAQAw==',
  'Component1': 'AAAAAgAAAKQAAAA2Aw==',
  'Component2': 'AAAAAgAAAKQAAAA3Aw==',
  'Wide': 'AAAAAgAAAKQAAAA9Aw==',
  'EPG': 'AAAAAgAAAKQAAABbAw==',
  'PAP': 'AAAAAgAAAKQAAAB3Aw==',
  'TenKey': 'AAAAAgAAAJcAAAAMAw==',
  'BSCS': 'AAAAAgAAAJcAAAAQAw==',
  'Ddata': 'AAAAAgAAAJcAAAAVAw==',
  'Stop': 'AAAAAgAAAJcAAAAYAw==',
  'Pause': 'AAAAAgAAAJcAAAAZAw==',
  'Play': 'AAAAAgAAAJcAAAAaAw==',
  'Rewind': 'AAAAAgAAAJcAAAAbAw==',
  'Forward': 'AAAAAgAAAJcAAAAcAw==',
  'DOT': 'AAAAAgAAAJcAAAAdAw==',
  'Rec': 'AAAAAgAAAJcAAAAgAw==',
  'Return': 'AAAAAgAAAJcAAAAjAw==',
  'Blue': 'AAAAAgAAAJcAAAAkAw==',
  'Red': 'AAAAAgAAAJcAAAAlAw==',
  'Green': 'AAAAAgAAAJcAAAAmAw==',
  'Yellow': 'AAAAAgAAAJcAAAAnAw==',
  'SubTitle': 'AAAAAgAAAJcAAAAoAw==',
  'CS': 'AAAAAgAAAJcAAAArAw==',
  'BS': 'AAAAAgAAAJcAAAAsAw==',
  'Digital': 'AAAAAgAAAJcAAAAyAw==',
  'Options': 'AAAAAgAAAJcAAAA2Aw==',
  'Media': 'AAAAAgAAAJcAAAA4Aw==',
  'Prev': 'AAAAAgAAAJcAAAA8Aw==',
  'Next': 'AAAAAgAAAJcAAAA9Aw==',
  'DpadCenter': 'AAAAAgAAAJcAAABKAw==',
  'CursorUp': 'AAAAAgAAAJcAAABPAw==',
  'CursorDown': 'AAAAAgAAAJcAAABQAw==',
  'CursorLeft': 'AAAAAgAAAJcAAABNAw==',
  'CursorRight': 'AAAAAgAAAJcAAABOAw==',
  'ShopRemoteControlForcedDynamic': 'AAAAAgAAAJcAAABqAw==',
  'FlashPlus': 'AAAAAgAAAJcAAAB4Aw==',
  'FlashMinus': 'AAAAAgAAAJcAAAB5Aw==',
  'AudioQualityMode': 'AAAAAgAAAJcAAAB7Aw==',
  'DemoMode': 'AAAAAgAAAJcAAAB8Aw==',
  'Analog': 'AAAAAgAAAHcAAAANAw==',
  'Mode3D': 'AAAAAgAAAHcAAABNAw==',
  'DigitalToggle': 'AAAAAgAAAHcAAABSAw==',
  'DemoSurround': 'AAAAAgAAAHcAAAB7Aw==',
  '*AD': 'AAAAAgAAABoAAAA7Aw==',
  'AudioMixUp': 'AAAAAgAAABoAAAA8Aw==',
  'AudioMixDown': 'AAAAAgAAABoAAAA9Aw==',
  'PhotoFrame': 'AAAAAgAAABoAAABVAw==',
  'Tv_Radio': 'AAAAAgAAABoAAABXAw==',
  'SyncMenu': 'AAAAAgAAABoAAABYAw==',
  'Hdmi1': 'AAAAAgAAABoAAABaAw==',
  'Hdmi2': 'AAAAAgAAABoAAABbAw==',
  'Hdmi3': 'AAAAAgAAABoAAABcAw==',
  'Hdmi4': 'AAAAAgAAABoAAABdAw==',
  'TopMenu': 'AAAAAgAAABoAAABgAw==',
  'PopUpMenu': 'AAAAAgAAABoAAABhAw==',
  'OneTouchTimeRec': 'AAAAAgAAABoAAABkAw==',
  'OneTouchView': 'AAAAAgAAABoAAABlAw==',
  'DUX': 'AAAAAgAAABoAAABzAw==',
  'FootballMode': 'AAAAAgAAABoAAAB2Aw==',
  'iManual': 'AAAAAgAAABoAAAB7Aw==',
  'Netflix': 'AAAAAgAAABoAAAB8Aw==',
  'Assists': 'AAAAAgAAAMQAAAA7Aw==',
  'ActionMenu': 'AAAAAgAAAMQAAABLAw==',
  'Help': 'AAAAAgAAAMQAAABNAw==',
  'TvSatellite': 'AAAAAgAAAMQAAABOAw==',
  'WirelessSubwoofer': 'AAAAAgAAAMQAAAB+Aw=='
};
<|endoftext|>


<commit_before>/*
 * Copyright 2016 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
 *
 * 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.
 */
'use strict';

var NetworkError = require('../NetworkError');
var WorkPool = require('../collections/WorkPool');
var chain = require('../util/chain');
var delay = require('../util/delay');
var clock = require('../util/clock');

// Map template properties to their corresponding tile properties.
var templateProperties = {
  x: 'x',
  y: 'y',
  z: 'z',
  f: 'face'
};

// Default face order for cube maps.
var defaultCubeMapFaceOrder = 'bdflru';

// Default maximum number of concurrent requests.
var defaultConcurrency = 4;

// Default milliseconds to wait before retrying failed requests.
var defaultRetryDelay = 10000;

/**
 * @class
 * @classdesc Source that loads images given a URL and a crop rectangle.
 * @implements Source
 * @param {Function} sourceFromTile Function that receives a tile and returns
 * an object `{ url, rect }`.
 * @param {Object} opts
 * @param {number} [opts.concurrency=4] Maximum number of tiles to load at the
 * same time.
 * @param {number} [opts.retryDelay=10000] Milliseconds to wait before
 * retrying failed requests.
*/
function ImageUrlSource(sourceFromTile, opts) {

  opts = opts ? opts : {};

  this._loadPool = new WorkPool({
    concurrency: opts.concurrency || defaultConcurrency
  });

  this._retryDelay = opts.retryDelay || defaultRetryDelay;
  this._retryMap = {};

  this._sourceFromTile = sourceFromTile;
}

ImageUrlSource.prototype.loadAsset = function(stage, tile, done) {

  var self = this;

  var retryDelay = this._retryDelay;
  var retryMap = this._retryMap;

  var tileSource = this._sourceFromTile(tile);
  var url = tileSource.url;
  var rect = tileSource.rect;

  var loadImage = stage.loadImage.bind(stage, url, rect);

  var loadFn = function(done) {
    return self._loadPool.push(loadImage, function(err, asset) {
      if (err) {
        if (err instanceof NetworkError) {
          // If a network error occurred, wait before retrying.
          retryMap[url] = clock();
        }
        done(err, tile);
      } else {
        // On a successful fetch, forget the previous timeout.
        delete retryMap[url];
        done(null, tile, asset);
      }
    });
  };

  // Check whether we are retrying a failed request.
  var delayAmount;
  var lastTime = retryMap[url];
  if (lastTime != null) {
    var now = clock();
    var elapsed = now - lastTime;
    if (elapsed < retryDelay) {
      // Wait before retrying.
      delayAmount = retryDelay - elapsed;
    } else {
      // Retry timeout expired; perform the request at once.
      delayAmount = 0;
      delete retryMap[url];
    }
  }

  var delayFn = delay.bind(null, delayAmount);

  return chain(delayFn, loadFn)(done);
};

/**
 * Create an ImageUrlSource from a string template
 * @param {String} url Template for the tile URLs. The following placeholders may be used:
   - {z} : tile level index (0 is the smallest level)
   - {f} : tile face (b, d, f, l, r, u)
   - {x} : tile x index
   - {y} : tile y index
 * @param {Object} opts
 * @param {String} opts.cubeMapPreviewUrl Use the cube map preview at this URL as the first level
 * @param {String} [opts.cubeMapPreviewFaceOrder='bdflru'] Ordering of the faces on the cube map preview
*/
ImageUrlSource.fromString = function(url, opts) {
  opts = opts || {};

  var faceOrder = opts && opts.cubeMapPreviewFaceOrder || defaultCubeMapFaceOrder;

  var urlFn = opts.cubeMapPreviewUrl ? withPreview : withoutPreview;

  return new ImageUrlSource(urlFn);

  function withoutPreview(tile) {
    var tileUrl = url;

    for (var property in templateProperties) {
      var regExp = propertyRegExp(property);
      var valueFromTile = tile[templateProperties[property]];
      tileUrl = tileUrl.replace(regExp, valueFromTile);
    }

    return { url: tileUrl };
  }

  function withPreview(tile) {
    if (tile.z === 0) {
      return cubeMapUrl(tile);
    }
    else {
      return withoutPreview(tile);
    }
  }

  function cubeMapUrl(tile) {
    var y = faceOrder.indexOf(tile.face) / 6;
    return {
      url: opts.cubeMapPreviewUrl,
      rect: { x: 0, y: y, width: 1, height: 1/6 }
    };
  }
};

ImageUrlSource.stringHasFace = function(url) {
  return ImageUrlSource.stringHasProperty(url, 'f');
};

ImageUrlSource.stringHasX = function(url) {
  return ImageUrlSource.stringHasProperty(url, 'x');
};

ImageUrlSource.stringHasY = function(url) {
  return ImageUrlSource.stringHasProperty(url, 'y');
};

ImageUrlSource.stringHasLevel = function(url) {
  return ImageUrlSource.stringHasProperty(url, 'z');
};

ImageUrlSource.stringHasProperty = function(url, property) {
  return !!url.match(propertyRegExp(property));
};

function propertyRegExp(property) {
  var regExpStr = '\\{(' + property + ')\\}';
  return new RegExp(regExpStr, 'g');
}

module.exports = ImageUrlSource;
<commit_msg>Use the empty string when replacing a nonexistent tile parameter in a URL.<commit_after>/*
 * Copyright 2016 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
 *
 * 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.
 */
'use strict';

var NetworkError = require('../NetworkError');
var WorkPool = require('../collections/WorkPool');
var chain = require('../util/chain');
var delay = require('../util/delay');
var clock = require('../util/clock');

// Map template properties to their corresponding tile properties.
var templateProperties = {
  x: 'x',
  y: 'y',
  z: 'z',
  f: 'face'
};

// Default face order for cube maps.
var defaultCubeMapFaceOrder = 'bdflru';

// Default maximum number of concurrent requests.
var defaultConcurrency = 4;

// Default milliseconds to wait before retrying failed requests.
var defaultRetryDelay = 10000;

/**
 * @class
 * @classdesc Source that loads images given a URL and a crop rectangle.
 * @implements Source
 * @param {Function} sourceFromTile Function that receives a tile and returns
 * an object `{ url, rect }`.
 * @param {Object} opts
 * @param {number} [opts.concurrency=4] Maximum number of tiles to load at the
 * same time.
 * @param {number} [opts.retryDelay=10000] Milliseconds to wait before
 * retrying failed requests.
*/
function ImageUrlSource(sourceFromTile, opts) {

  opts = opts ? opts : {};

  this._loadPool = new WorkPool({
    concurrency: opts.concurrency || defaultConcurrency
  });

  this._retryDelay = opts.retryDelay || defaultRetryDelay;
  this._retryMap = {};

  this._sourceFromTile = sourceFromTile;
}

ImageUrlSource.prototype.loadAsset = function(stage, tile, done) {

  var self = this;

  var retryDelay = this._retryDelay;
  var retryMap = this._retryMap;

  var tileSource = this._sourceFromTile(tile);
  var url = tileSource.url;
  var rect = tileSource.rect;

  var loadImage = stage.loadImage.bind(stage, url, rect);

  var loadFn = function(done) {
    return self._loadPool.push(loadImage, function(err, asset) {
      if (err) {
        if (err instanceof NetworkError) {
          // If a network error occurred, wait before retrying.
          retryMap[url] = clock();
        }
        done(err, tile);
      } else {
        // On a successful fetch, forget the previous timeout.
        delete retryMap[url];
        done(null, tile, asset);
      }
    });
  };

  // Check whether we are retrying a failed request.
  var delayAmount;
  var lastTime = retryMap[url];
  if (lastTime != null) {
    var now = clock();
    var elapsed = now - lastTime;
    if (elapsed < retryDelay) {
      // Wait before retrying.
      delayAmount = retryDelay - elapsed;
    } else {
      // Retry timeout expired; perform the request at once.
      delayAmount = 0;
      delete retryMap[url];
    }
  }

  var delayFn = delay.bind(null, delayAmount);

  return chain(delayFn, loadFn)(done);
};

/**
 * Create an ImageUrlSource from a string template
 * @param {String} url Template for the tile URLs. The following placeholders may be used:
   - {z} : tile level index (0 is the smallest level)
   - {f} : tile face (b, d, f, l, r, u)
   - {x} : tile x index
   - {y} : tile y index
 * @param {Object} opts
 * @param {String} opts.cubeMapPreviewUrl Use the cube map preview at this URL as the first level
 * @param {String} [opts.cubeMapPreviewFaceOrder='bdflru'] Ordering of the faces on the cube map preview
*/
ImageUrlSource.fromString = function(url, opts) {
  opts = opts || {};

  var faceOrder = opts && opts.cubeMapPreviewFaceOrder || defaultCubeMapFaceOrder;

  var urlFn = opts.cubeMapPreviewUrl ? withPreview : withoutPreview;

  return new ImageUrlSource(urlFn);

  function withoutPreview(tile) {
    var tileUrl = url;

    for (var property in templateProperties) {
      var regExp = propertyRegExp(property);
      var valueFromTile = tile[templateProperties[property]] || '';
      tileUrl = tileUrl.replace(regExp, valueFromTile);
    }

    return { url: tileUrl };
  }

  function withPreview(tile) {
    if (tile.z === 0) {
      return cubeMapUrl(tile);
    }
    else {
      return withoutPreview(tile);
    }
  }

  function cubeMapUrl(tile) {
    var y = faceOrder.indexOf(tile.face) / 6;
    return {
      url: opts.cubeMapPreviewUrl,
      rect: { x: 0, y: y, width: 1, height: 1/6 }
    };
  }
};

ImageUrlSource.stringHasFace = function(url) {
  return ImageUrlSource.stringHasProperty(url, 'f');
};

ImageUrlSource.stringHasX = function(url) {
  return ImageUrlSource.stringHasProperty(url, 'x');
};

ImageUrlSource.stringHasY = function(url) {
  return ImageUrlSource.stringHasProperty(url, 'y');
};

ImageUrlSource.stringHasLevel = function(url) {
  return ImageUrlSource.stringHasProperty(url, 'z');
};

ImageUrlSource.stringHasProperty = function(url, property) {
  return !!url.match(propertyRegExp(property));
};

function propertyRegExp(property) {
  var regExpStr = '\\{(' + property + ')\\}';
  return new RegExp(regExpStr, 'g');
}

module.exports = ImageUrlSource;
<|endoftext|>