<commit_before>'use strict';

/**
 * A Transformer for retreiving cover-image-urls from
 * the DBC moreinfo service.
 *
 * The transformer can take a list of pids and returns
 * an object containing pids and coverurl-size-types and coverurls.
 *
 * request:
 *   { "pids": ["pid1", "pid2", .... , "pid_n" ] }
 *
 * response:
 *   {
 *     "pid1": [ "coverUrlFull": "URL", "coverUrl42": "URL", ... ],
 *     "pid2": [ ... ]
 *   }
 */

import genericTransformer from '../genericTransformer.js';
import moreInfoClient from '../services/MoreInfo/client';
import _ from 'lodash';
import {log} from '../utils.js';

/**
 * Splits a PID into: type, agencyid and localid.
 * This functionality is needed as long as moreinfo
 * can not accept pids as identifiers.
 */
function pidSplitter(pid) {
  let pid_array = pid.split(':');
  if (pid_array.length !== 2) {
    throw new Error('Illegal pid: ' + pid);
  }
  let id = {};
  id.localid = pid_array[1];
  let agency_type = pid_array[0].split('-');
  if (agency_type.length !== 2) {
    throw new Error('Illegal agency/type in pid: ' + pid);
  }
  id.agency = agency_type[0];
  id.type = agency_type[1];
  return id;
}

/**
 * Returns a String representing an identifier given a libraryCode and a localIdentifier.
 * Used for lookup in the state-object.
 */
function id2parameter(libCode, localId) {
  return libCode + ':' + localId;
}

/**
 * Retrieves a PID from a state-object, given a libraryCode and a localIdentifier.
 * The state-object must be contain key-values where the keys are constructed
 * from id2parameter with the libraryCode and localIdentifier. The values are the PIDs.
 *
 * This function is needed as long as more-info does not take PIDs as identifiers.
 */
function getPid(libCode, localId, state) {
  let stateId = id2parameter(libCode, localId);
  let pid = state[stateId];
  return pid;
}

/**
 * A const object for looking up image-sizes as returned from moreinfo.
 */
const IMAGE_SIZES = {
  detail_42: 'coverUrl42',
  detail_117: 'coverUrl117',
  detail_207: 'coverUrl207',
  detail_500: 'coverUrl500',
  thumbnail: 'coverUrlThumbnail',
  detail: 'coverUrlFull'
};

/**
 * Detects the value from requestStatus.statusEnum.
 * If the value is an error/errorcode of some kind, or if the property
 * can not be found in the response, an error will be thrown.
 */
function errorCodeInResponse(response) {
  if (!_.has(response, 'requestStatus.statusEnum')) {
    throw {
      name: 'MalformedResponse',
      message: 'Malformed Response. No requestStatus.statusEnum.',
      response: response,
      stack: new Error().stack
    };
  }
  if (response.requestStatus.statusEnum !== 'ok') {
    throw {
      name: 'ErrorResponse',
      message: 'Error returned in response.',
      response: response,
      stack: new Error().stack
    };
  }
}

/**
 * Finds the list of identifierInformation, or throws
 * an error if the property cannot be found or the length is 0.
 */
function getIdentifierInformationList(response) {
  if (!_.has(response, 'identifierInformation')) {
    throw {
      name: 'MalformedResponse',
      message: 'Malformed response. No identifierInformation.',
      response: response,
      stack: new Error().stack
    };
  }

  if (response.identifierInformation.length === 0) {
    throw {
      name: 'EmptyResponse',
      message: 'No identifiers were returned in the response.',
      response: response,
      stack: new Error().stack
    };
  }

  return response.identifierInformation;
}

/**
 * Tests whether the identifierInformation contains any cover-images.
 * Returns true if any cover-images are contained. False otherwise.
 */
function doIdentifierInformationContainsCoverImages(idInfo) {
  let res = true;
  if (!_.has(idInfo, 'identifierKnown')
    || !idInfo.identifierKnown === true
    || !_.has(idInfo, 'coverImage')
    || idInfo.coverImage.length === 0) {
    // no identifierKnown attribute. should this be the same as 'identifier not known'?
    if (_.has(idInfo, 'identifier.localIdentifier') && _.has(idInfo, 'identifier.libraryCode')) {
      let pid = id2parameter(idInfo.identifier.libraryCode, idInfo.identifier.localIdentifier);
      log.info('Could not find covers for identifier: ' + pid);
    } else { // eslint-disable-line brace-style
      log.info('Could not find covers for unknown identifier: ' + JSON.stringify(idInfo, null, 4));
    }
    res = false;
  }
  return res;
}

/**
 * Returns an object containing { "coverUrlxxx": "URL" }
 * where "coverUrlxxx is one of the values in IMAGE_SIZES
 * and "URL" is the coverImageUrl returned from moreinfo.
 */
function getImageSizeAndUrl(x) {
  let res = {};
  if (_.has(x, 'attributes.imageSize') && _.has(x, '$value')) {
    let is = IMAGE_SIZES[x.attributes.imageSize];
    res[is] = x.$value.replace('http:', '');
  }
  return res;
}

/**
 * Retrieves the cover-urls and image-size from the identifierInformation.
 * Returns the pid and a list of coverimage-urls and imageSizes according to IMAGE_SIZES.
 */
function getCoverUrlsFromIdentifierInformation(idInfo, state) {

  if (!doIdentifierInformationContainsCoverImages(idInfo)) {
    return {};
  }

  let pid = getPid(idInfo.identifier.libraryCode, idInfo.identifier.localIdentifier, state);
  let imageUrlsList = idInfo.coverImage.map(x => getImageSizeAndUrl(x));
  let imageUrls = imageUrlsList.reduce(_.extend, {});

  return {pid: pid, urls: imageUrls};
}

/**
 * Writes out the error to log, and returns an error envelope
 */
function handleError(e) {
  let errorEnvelope = {
    statusCode: 500,
    error: 'Internal server error'
  };

  try {
    log.error('ERROR: [' + e.name + '] : ' + e.message);
    log.error('Response: ' + JSON.stringify(e.response, null, 0));
    log.error(e.stack);
    log.error('******** END ERROR');
  } catch (err) { // eslint-disable-line brace-style
    // We dont care about an error here!
  }
  return errorEnvelope;
}

export default function () {

  function requestTransform(request, context) { // eslint-disable-line no-unused-vars
    let pids = request[0].pids;
    let params = {};
    let state = {};
    params.identifier = pids.map(pid => {
      let pid_obj = pidSplitter(pid);
      let identifier = {};
      identifier.localIdentifier = pid_obj.localid;
      identifier.libraryCode = pid_obj.agency;
      state[id2parameter(identifier.libraryCode, identifier.localIdentifier)] = pid;
      return identifier;
    });

    return {transformedRequest: params, state: state};
  }

  function responseTransform(response, context, state) { // eslint-disable-line no-unused-vars
    return new Promise((request, resolve) => {

      // The below should probably be converted to some kind of tests:
      //
      // response.identifierInformation = [];
      // delete response.identifierInformation;
      // delete response.requestStatus.statusEnum;
      // response.identifierInformation[0].identifierKnown = false;
      // delete response.identifierInformation[0].identifier.localIdentifier;
      // delete response.identifierInformation[0].coverImage;
      // response.identifierInformation[0].coverImage = [];
      // console.log('RESP: ' + JSON.stringify(response, null, 4));

      try {
        errorCodeInResponse(response);

        let identifierInformation = getIdentifierInformationList(response);

        let data = {};
        identifierInformation.forEach((idInfo) => {
          let {pid: pid, urls: Z} = getCoverUrlsFromIdentifierInformation(idInfo, state);
          data[pid] = Z;
        });
        let envelope = {
          statusCode: 200,
          data: data
        };
        return resolve(envelope);
      } catch (e) { // eslint-disable-line brace-style
        let errorEnvelope = handleError(e);
        resolve(errorEnvelope);
      }
    });
  }

  function moreInfoFunc(context) {
    let neoContext = context.libdata.config.provider.services.moreinfo;
    let client = moreInfoClient(neoContext);

    return function (request, local_context, state) { // eslint-disable-line no-unused-vars
      return {response: client.getMoreInfoResultNeo(request), state: state};
    };
  }

  return genericTransformer(requestTransform, responseTransform, moreInfoFunc);
}
<commit_msg>Moved transformers out from inner function scope<commit_after>'use strict';

/**
 * A Transformer for retreiving cover-image-urls from
 * the DBC moreinfo service.
 *
 * The transformer can take a list of pids and returns
 * an object containing pids and coverurl-size-types and coverurls.
 *
 * request:
 *   { "pids": ["pid1", "pid2", .... , "pid_n" ] }
 *
 * response:
 *   {
 *     "pid1": [ "coverUrlFull": "URL", "coverUrl42": "URL", ... ],
 *     "pid2": [ ... ]
 *   }
 */

import genericTransformer from '../genericTransformer.js';
import moreInfoClient from '../services/MoreInfo/client';
import _ from 'lodash';
import {log} from '../utils.js';

/**
 * Splits a PID into: type, agencyid and localid.
 * This functionality is needed as long as moreinfo
 * can not accept pids as identifiers.
 */
function pidSplitter(pid) {
  let pid_array = pid.split(':');
  if (pid_array.length !== 2) {
    throw new Error('Illegal pid: ' + pid);
  }
  let id = {};
  id.localid = pid_array[1];
  let agency_type = pid_array[0].split('-');
  if (agency_type.length !== 2) {
    throw new Error('Illegal agency/type in pid: ' + pid);
  }
  id.agency = agency_type[0];
  id.type = agency_type[1];
  return id;
}

/**
 * Returns a String representing an identifier given a libraryCode and a localIdentifier.
 * Used for lookup in the state-object.
 */
function id2parameter(libCode, localId) {
  return libCode + ':' + localId;
}

/**
 * Retrieves a PID from a state-object, given a libraryCode and a localIdentifier.
 * The state-object must be contain key-values where the keys are constructed
 * from id2parameter with the libraryCode and localIdentifier. The values are the PIDs.
 *
 * This function is needed as long as more-info does not take PIDs as identifiers.
 */
function getPid(libCode, localId, state) {
  let stateId = id2parameter(libCode, localId);
  let pid = state[stateId];
  return pid;
}

/**
 * A const object for looking up image-sizes as returned from moreinfo.
 */
const IMAGE_SIZES = {
  detail_42: 'coverUrl42',
  detail_117: 'coverUrl117',
  detail_207: 'coverUrl207',
  detail_500: 'coverUrl500',
  thumbnail: 'coverUrlThumbnail',
  detail: 'coverUrlFull'
};

/**
 * Detects the value from requestStatus.statusEnum.
 * If the value is an error/errorcode of some kind, or if the property
 * can not be found in the response, an error will be thrown.
 */
function errorCodeInResponse(response) {
  if (!_.has(response, 'requestStatus.statusEnum')) {
    throw {
      name: 'MalformedResponse',
      message: 'Malformed Response. No requestStatus.statusEnum.',
      response: response,
      stack: new Error().stack
    };
  }
  if (response.requestStatus.statusEnum !== 'ok') {
    throw {
      name: 'ErrorResponse',
      message: 'Error returned in response.',
      response: response,
      stack: new Error().stack
    };
  }
}

/**
 * Finds the list of identifierInformation, or throws
 * an error if the property cannot be found or the length is 0.
 */
function getIdentifierInformationList(response) {
  if (!_.has(response, 'identifierInformation')) {
    throw {
      name: 'MalformedResponse',
      message: 'Malformed response. No identifierInformation.',
      response: response,
      stack: new Error().stack
    };
  }

  if (response.identifierInformation.length === 0) {
    throw {
      name: 'EmptyResponse',
      message: 'No identifiers were returned in the response.',
      response: response,
      stack: new Error().stack
    };
  }

  return response.identifierInformation;
}

/**
 * Tests whether the identifierInformation contains any cover-images.
 * Returns true if any cover-images are contained. False otherwise.
 */
function doIdentifierInformationContainsCoverImages(idInfo) {
  let res = true;
  if (!_.has(idInfo, 'identifierKnown')
    || !idInfo.identifierKnown === true
    || !_.has(idInfo, 'coverImage')
    || idInfo.coverImage.length === 0) {
    // no identifierKnown attribute. should this be the same as 'identifier not known'?
    if (_.has(idInfo, 'identifier.localIdentifier') && _.has(idInfo, 'identifier.libraryCode')) {
      let pid = id2parameter(idInfo.identifier.libraryCode, idInfo.identifier.localIdentifier);
      log.info('Could not find covers for identifier: ' + pid);
    } else { // eslint-disable-line brace-style
      log.info('Could not find covers for unknown identifier: ' + JSON.stringify(idInfo, null, 4));
    }
    res = false;
  }
  return res;
}

/**
 * Returns an object containing { "coverUrlxxx": "URL" }
 * where "coverUrlxxx is one of the values in IMAGE_SIZES
 * and "URL" is the coverImageUrl returned from moreinfo.
 */
function getImageSizeAndUrl(x) {
  let res = {};
  if (_.has(x, 'attributes.imageSize') && _.has(x, '$value')) {
    let is = IMAGE_SIZES[x.attributes.imageSize];
    res[is] = x.$value.replace('http:', '');
  }
  return res;
}

/**
 * Retrieves the cover-urls and image-size from the identifierInformation.
 * Returns the pid and a list of coverimage-urls and imageSizes according to IMAGE_SIZES.
 */
function getCoverUrlsFromIdentifierInformation(idInfo, state) {

  if (!doIdentifierInformationContainsCoverImages(idInfo)) {
    return {};
  }

  let pid = getPid(idInfo.identifier.libraryCode, idInfo.identifier.localIdentifier, state);
  let imageUrlsList = idInfo.coverImage.map(x => getImageSizeAndUrl(x));
  let imageUrls = imageUrlsList.reduce(_.extend, {});

  return {pid: pid, urls: imageUrls};
}

/**
 * Writes out the error to log, and returns an error envelope
 */
function handleError(e) {
  let errorEnvelope = {
    statusCode: 500,
    error: 'Internal server error'
  };

  try {
    log.error('ERROR: [' + e.name + '] : ' + e.message);
    log.error('Response: ' + JSON.stringify(e.response, null, 0));
    log.error(e.stack);
    log.error('******** END ERROR');
  } catch (err) { // eslint-disable-line brace-style
    // We dont care about an error here!
  }
  return errorEnvelope;
}

export function moreInfoRequest(request, context) { // eslint-disable-line no-unused-vars
  let pids = request[0].pids;
  let params = {};
  let state = {};
  params.identifier = pids.map(pid => {
    let pid_obj = pidSplitter(pid);
    let identifier = {};
    identifier.localIdentifier = pid_obj.localid;
    identifier.libraryCode = pid_obj.agency;
    state[id2parameter(identifier.libraryCode, identifier.localIdentifier)] = pid;
    return identifier;
  });

  return {transformedRequest: params, state: state};
}

export function moreInfoResponse(response, context, state) { // eslint-disable-line no-unused-vars
  return new Promise((request, resolve) => {

    // The below should probably be converted to some kind of tests:
    //
    // response.identifierInformation = [];
    // delete response.identifierInformation;
    // delete response.requestStatus.statusEnum;
    // response.identifierInformation[0].identifierKnown = false;
    // delete response.identifierInformation[0].identifier.localIdentifier;
    // delete response.identifierInformation[0].coverImage;
    // response.identifierInformation[0].coverImage = [];
    // console.log('RESP: ' + JSON.stringify(response, null, 4));

    try {
      errorCodeInResponse(response);

      let identifierInformation = getIdentifierInformationList(response);

      let data = {};
      identifierInformation.forEach((idInfo) => {
        let {pid: pid, urls: Z} = getCoverUrlsFromIdentifierInformation(idInfo, state);
        data[pid] = Z;
      });
      let envelope = {
        statusCode: 200,
        data: data
      };
      return resolve(envelope);
    } catch (e) { // eslint-disable-line brace-style
      let errorEnvelope = handleError(e);
      resolve(errorEnvelope);
    }
  });
}

export function moreInfoFunc(context) {
  let neoContext = context.libdata.config.provider.services.moreinfo;
  let client = moreInfoClient(neoContext);

  return function (request, local_context, state) { // eslint-disable-line no-unused-vars
    return {response: client.getMoreInfoResultNeo(request), state: state};
  };
}

export default function moreInfoTransformer() {

  return genericTransformer(moreInfoRequest,
                            moreInfoResponse,
                            moreInfoFunc);
}
<|endoftext|>


<commit_before>'use strict';

/* This is a simple cache intended to reduce the amount of load
 * Nightscout puts on MongoDB. The cache is based on identifying
 * elements based on the MongoDB _id field and implements simple
 * semantics for adding data to the cache in the runtime, intended
 * to be accessed by the persistence layer as data is inserted, updated
 * or deleted, as well as the periodic dataloader, which polls Mongo
 * for new inserts.
 *
 * Longer term, the cache is planned to allow skipping the Mongo polls
 * altogether.
 */

const _ = require('lodash');
const constants = require('../constants');

function cache (env, ctx) {

  const data = {
    treatments: []
    , devicestatus: []
    , entries: []
  };

  const retentionPeriods = {
    treatments: constants.ONE_HOUR * 60
    , devicestatus: env.extendedSettings.devicestatus && env.extendedSettings.devicestatus.days && env.extendedSettings.devicestatus.days == 2 ? constants.TWO_DAYS : constants.ONE_DAY
    , entries: constants.TWO_DAYS
  };

  function mergeCacheArrays (oldData, newData, retentionPeriod) {

    const ageLimit = Date.now() - retentionPeriod;

    var filteredOld =  filterForAge(oldData, ageLimit);
    var filteredNew = filterForAge(newData, ageLimit);

    const merged = ctx.ddata.idMergePreferNew(filteredOld, filteredNew);

    return _.sortBy(merged, function(item) {
      return -item.mills;
    });

    function filterForAge(data, ageLimit) {
      return _.filter(data, function hasId(object) {
        const hasId = !_.isEmpty(object._id);
        const isFresh = object.mills >= ageLimit;
        return isFresh && hasId;
      });
    }

  }

  data.isEmpty = (datatype) => {
    return data[datatype].length == 0;
  }

  data.getData = (datatype) => {
    return _.cloneDeep(data[datatype]);
  }

  data.insertData = (datatype, newData) => {
    data[datatype] = mergeCacheArrays(data[datatype], newData, retentionPeriods[datatype]);
    return data.getData(datatype);
  }

  function dataChanged (operation) {
    if (!data[operation.type]) return;

    if (operation.op == 'remove') {
      // if multiple items were deleted, flush entire cache
      if (!operation.changes) {
        data.treatments = [];
        data.devicestatus = [];
        data.entries = [];
      } else {
        removeFromArray(data[operation.type], operation.changes);
      }
    }

    if (operation.op == 'update') {
      data[operation.type] = mergeCacheArrays(data[operation.type], operation.changes, retentionPeriods[operation.type]);
    }
  }

  ctx.bus.on('data-update', dataChanged);

  function removeFromArray (array, id) {
    for (let i = 0; i < array.length; i++) {
      const o = array[i];
      if (o._id == id) {
        //console.log('Deleting object from cache', id);
        array.splice(i, 1);
        break;
      }
    }
  }

  return data;
}

module.exports = cache;
<commit_msg>Make empty cache detection a bit more aggressive to account for cache flush and data insert happening concurrently<commit_after>'use strict';

/* This is a simple cache intended to reduce the amount of load
 * Nightscout puts on MongoDB. The cache is based on identifying
 * elements based on the MongoDB _id field and implements simple
 * semantics for adding data to the cache in the runtime, intended
 * to be accessed by the persistence layer as data is inserted, updated
 * or deleted, as well as the periodic dataloader, which polls Mongo
 * for new inserts.
 *
 * Longer term, the cache is planned to allow skipping the Mongo polls
 * altogether.
 */

const _ = require('lodash');
const constants = require('../constants');

function cache (env, ctx) {

  const data = {
    treatments: []
    , devicestatus: []
    , entries: []
  };

  const retentionPeriods = {
    treatments: constants.ONE_HOUR * 60
    , devicestatus: env.extendedSettings.devicestatus && env.extendedSettings.devicestatus.days && env.extendedSettings.devicestatus.days == 2 ? constants.TWO_DAYS : constants.ONE_DAY
    , entries: constants.TWO_DAYS
  };

  function mergeCacheArrays (oldData, newData, retentionPeriod) {

    const ageLimit = Date.now() - retentionPeriod;

    var filteredOld =  filterForAge(oldData, ageLimit);
    var filteredNew = filterForAge(newData, ageLimit);

    const merged = ctx.ddata.idMergePreferNew(filteredOld, filteredNew);

    return _.sortBy(merged, function(item) {
      return -item.mills;
    });

    function filterForAge(data, ageLimit) {
      return _.filter(data, function hasId(object) {
        const hasId = !_.isEmpty(object._id);
        const isFresh = object.mills >= ageLimit;
        return isFresh && hasId;
      });
    }

  }

  data.isEmpty = (datatype) => {
    return data[datatype].length < 20;
  }

  data.getData = (datatype) => {
    return _.cloneDeep(data[datatype]);
  }

  data.insertData = (datatype, newData) => {
    data[datatype] = mergeCacheArrays(data[datatype], newData, retentionPeriods[datatype]);
    return data.getData(datatype);
  }

  function dataChanged (operation) {
    if (!data[operation.type]) return;

    if (operation.op == 'remove') {
      // if multiple items were deleted, flush entire cache
      if (!operation.changes) {
        data.treatments = [];
        data.devicestatus = [];
        data.entries = [];
      } else {
        removeFromArray(data[operation.type], operation.changes);
      }
    }

    if (operation.op == 'update') {
      data[operation.type] = mergeCacheArrays(data[operation.type], operation.changes, retentionPeriods[operation.type]);
    }
  }

  ctx.bus.on('data-update', dataChanged);

  function removeFromArray (array, id) {
    for (let i = 0; i < array.length; i++) {
      const o = array[i];
      if (o._id == id) {
        //console.log('Deleting object from cache', id);
        array.splice(i, 1);
        break;
      }
    }
  }

  return data;
}

module.exports = cache;
<|endoftext|>


<commit_before>var app = require('../app')
  , client = require('./client')
  , config = require('./config')
  , derby = require('derby')
  , error = require('./error')
  , events = require('./events')
  , express = require('express')
  , expressApp = module.exports = express()
  , flash = require('connect-flash')
  , gravatar = require('derby-user-gravatar')
  , liveDbMongo = require('livedb-mongo')
  , MongoStore = require('connect-mongo')(express)
  , LocalStrategy = require('passport-local').Strategy
  , Providers = require('derby-user-providers')
  , racerBrowserChannel = require('racer-browserchannel')
  , racerHooks = require('racer-hooks')
  , Redis = require('redis')
  , User = require('derby-user');

var redis = Redis.createClient(config.get('redis.port'), config.get('redis.hostname'));
redis.select(config.get('redis.index'));

var store = derby.createStore({
  db: {db: liveDbMongo(config.get('mongo.uri') + '?auto_reconnect', config.get('mongo.options')), redis: redis}
});

var user = User(expressApp, {secretKey: config.get('secretKey')})
  , providers = Providers(expressApp, config.get('user'));

expressApp
  .use(express.favicon())
  .use(express.compress())
  .use(app.scripts(store))
  .use(express.static(__dirname + '/../../public'))
  .use(express.cookieParser())
  .use(express.session({
    secret: config.get('secretKey'),
    store: new MongoStore({
      safe: config.get('mongo.options.safe'),
      url: config.get('mongo.uri')
    })
  }))
  .use(racerBrowserChannel(store))
  .use(racerHooks(store))
  .use(store.modelMiddleware())
  .use(express.bodyParser())
  .use(express.methodOverride())
  .use(client())
  .use(user.init())
  .use(gravatar(store))
  .use(providers.init())
  .use(events(expressApp, store))
  .use(app.router())
  .use(expressApp.router)
  .use(user.routes())
  .use(providers.routes())
  .use(error(expressApp));<commit_msg>remove unused dependency<commit_after>var app = require('../app')
  , client = require('./client')
  , config = require('./config')
  , derby = require('derby')
  , error = require('./error')
  , events = require('./events')
  , express = require('express')
  , expressApp = module.exports = express()
  , gravatar = require('derby-user-gravatar')
  , liveDbMongo = require('livedb-mongo')
  , MongoStore = require('connect-mongo')(express)
  , LocalStrategy = require('passport-local').Strategy
  , Providers = require('derby-user-providers')
  , racerBrowserChannel = require('racer-browserchannel')
  , racerHooks = require('racer-hooks')
  , Redis = require('redis')
  , User = require('derby-user');

var redis = Redis.createClient(config.get('redis.port'), config.get('redis.hostname'));
redis.select(config.get('redis.index'));

var store = derby.createStore({
  db: {db: liveDbMongo(config.get('mongo.uri') + '?auto_reconnect', config.get('mongo.options')), redis: redis}
});

var user = User(expressApp, {secretKey: config.get('secretKey')})
  , providers = Providers(expressApp, config.get('user'));

expressApp
  .use(express.favicon())
  .use(express.compress())
  .use(app.scripts(store))
  .use(express.static(__dirname + '/../../public'))
  .use(express.cookieParser())
  .use(express.session({
    secret: config.get('secretKey'),
    store: new MongoStore({
      safe: config.get('mongo.options.safe'),
      url: config.get('mongo.uri')
    })
  }))
  .use(racerBrowserChannel(store))
  .use(racerHooks(store))
  .use(store.modelMiddleware())
  .use(express.bodyParser())
  .use(express.methodOverride())
  .use(client())
  .use(user.init())
  .use(gravatar(store))
  .use(providers.init())
  .use(events(expressApp, store))
  .use(app.router())
  .use(expressApp.router)
  .use(user.routes())
  .use(providers.routes())
  .use(error(expressApp));<|endoftext|>


<commit_before>Canvas2D.Sheet = Class.create( {
    style     : "static", // description of the style [static|dynamic]

    canvas    : null, // reference to the canvas this sheet needs to render on

    shapes    : [],  // list of shapes on the sheet
    shapesMap : {},  // name to shape mapping

    selectedShape : null, // the currently selected shape (by clicking on it)
    currentPos    : { x:0, y:0 }, // the position of last mousedown
    selectionPos  : { x:0, y:0 }, // the position of the current selection

    newLeft   : null, // temp var to hold left position of next added shape
    newTop    : null, // temp var to hold top position of next added shape

    eventHandlers : {}, // map of registered eventHandlers

    initialize: function(props) {
	props = props || {};

	this.name  = props.name  || "default";
	this.style = props.style || "static";

	this.canvas    = null;
	this.clear();

	this.eventHandlers = {};
	this.allowedStyles = new Array("static", "dynamic");
    },

    isDynamic: function() {
	return this.style == "dynamic";
    },

    on: function( event, handler ) {
	this.eventHandlers[event] = handler;
    },

    fireEvent: function( event, data ) {
	if( this.eventHandlers[event] ) {
	    this.eventHandlers[event](data);
	}
    },

    setCanvas: function( canvas ) {
	if (!(this.allowedStyles.indexOf(this.style) > -1)) {
    	canvas.log(this.style + " is an unknown style, reverting to static style");
    	canvas.log("Allowed styles are " + this.allowedStyles);
    	this.style = "static";
    }

	this.canvas = canvas;
	this.canvas.on( "mousedown", this.handleMouseDown.bind(this) );
	this.canvas.on( "mouseup",   this.handleMouseUp  .bind(this) );
	this.canvas.on( "mousedrag", this.handleMouseDrag.bind(this) );
    },

    freeze: function() { if( this.canvas ) { this.canvas.freeze(); } },
    thaw:   function() { if( this.canvas ) { this.canvas.thaw();   } },

    log: function(msg) {
	if( this.canvas ) {
	    this.canvas.log( "Canvas2D.Sheet: " + msg );
	}
    },

    makeDynamic: function() {
	this.style = "dynamic";
    },

    makeStatic: function() {
	this.style = "static";
    },

    at: function(left, top) {
	this.newTop = top;
	this.newLeft = left;
	return this;
    },

    put: function(shape) {
	this.add(shape.setPosition(this.newLeft, this.newTop));
	this.newLeft = null;
	this.newTop = null;
	return shape;
    },

    add: function(shape) {
	this.shapes.push( shape );
	this.shapesMap[shape.getName()] = shape;
	shape.setCanvas(this.canvas);
	this.canvas.render();

	this.log( "Added new shape" +
		  ( shape.props.left != null ? "@" + shape.props.left + ","
		    + shape.props.top : "" ) );
	return shape;
    },

    getShapeAt: function(x,y) {
	for( var s = this.shapes.length-1; s>=0; s-- ) {
	    if( this.shapes[s].hit(x,y) ) {
		return this.shapes[s];
	    }
	}
	return null;
    },

    getShapesIn: function(left, top, width, height) {
	alert( "Canvas2D.Sheet::getShapesIn: not implemented yet" );
    },

    handleMouseDown: function(pos) {
	if( !this.isDynamic() ) { return; }
	this.selectedShape = this.getShapeAt( pos.x, pos.y );
	if( this.selectedShape ) {
	    this.fireEvent( "shapeselected", this.selectedShape );
	}
	this.currentPos = pos;
    },

    handleMouseUp: function(pos) {
	if( !this.isDynamic() ) { return; }
	if( this.selectedShape ) {
	    var pos = this.selectedShape.getPosition();
	    this.log( "Shape moved to " + pos.left + ", " + pos.top );
	} else {
	    this.showSelection = false;
	}
    },

    handleMouseDrag: function(pos) {
	if( !this.isDynamic() ) { return; }
	if( this.selectedShape ) {
	    this.selectedShape.move( pos.dx, pos.dy );
	} else {
	    this.showSelection = true;
	    this.selectionPos  = pos;
	}
    },

    addSelectionOverlay: function() {
	if( this.showSelection ) {
	    var pos = this.selectionPos;
	    var dx = pos.x - this.currentPos.x;
	    var dy = pos.y - this.currentPos.y;

	    this.canvas.fillStyle = "rgba( 0, 0, 255, 0.10 )";
	    this.canvas.fillRect( pos.x <= this.currentPos.x ?
				  pos.x : this.currentPos.x,
				  pos.y <= this.currentPos.y ?
				  pos.y : this.currentPos.y,
				  Math.abs(dx), Math.abs(dy) );
	}
    },

    addSelectionMarkers: function() {
	if( this.selectedShape ) {
	    var box = this.selectedShape.getBox();
	    this.canvas.fillStyle = "rgba( 200, 200, 255, 1 )";
	    var corners = [[ box.left, box.top    ], [ box.right, box.top    ],
		           [ box.left, box.bottom ], [ box.right, box.bottom ]];
	    var canvas = this.canvas;
	    corners.each( function(corner) {
		canvas.beginPath();
		canvas.arc( corner[0],  corner[1], 5, 0, Math.PI*2, true );
		canvas.fill();
	    } );
	}
    },

    clear: function() {
	this.shapes    = [];
    	this.shapesMap = {};
    },

    render: function() {
	var delayed = [];
	this.shapes.each( function(shape) {
	    if( shape.delayRender() ) {
		delayed.push(shape);
	    } else {
		shape.render();
	    }
	} );

	delayed.each( function(shape) { shape.render(); } );

	this.addSelectionOverlay();
	this.addSelectionMarkers();
    },

    toString: function() {
	var s = "";
	s += "Sheet "  + this.name;
	s += " +" + this.style + " {\n";
	this.shapes.each(function(shape) {
	    s += shape.toString("  ") + "\n";
	} );
	s += "}";
	return s;
    }

} );

Canvas2D.Sheet.getNames = function() {
    return [ "sheet" ];
}

Canvas2D.Sheet.from = function(construct, canvas) {
    var style = "static";
    var styleModifier = construct.modifiers.get( "style" );
    if( styleModifier ) {
	style = styleModifier.value.value.toLowerCase();
    }

    construct.modifiers.each(function(pair) {
	if( pair.key.toLowerCase() == "static"
	    || pair.key.toLowerCase() == "dynamic" ) {
	    style = pair.key.toLowerCase();
	}
    });

    var sheet = new Canvas2D.Sheet({ name: construct.name, style: style } );
    canvas.add(sheet);
    return sheet;
};

Canvas2D.ADLVisitor.registerConstruct(Canvas2D.Sheet);
<commit_msg>some touch ups<commit_after>Canvas2D.Sheet = Class.create( {
    style         : "static",                // selected style
    allowedStyles : [ "static", "dynamic" ], // allowed styls

    canvas    : null, // reference to the canvas this sheet needs to render on

    shapes    : [],  // list of shapes on the sheet
    shapesMap : {},  // name to shape mapping

    selectedShape : null, // the currently selected shape (by clicking on it)
    currentPos    : { x:0, y:0 }, // the position of last mousedown
    selectionPos  : { x:0, y:0 }, // the position of the current selection

    newLeft   : null, // temp var to hold left position of next added shape
    newTop    : null, // temp var to hold top position of next added shape

    eventHandlers : {}, // map of registered eventHandlers

    initialize: function(props) {
	props = props || {};

	this.name  = props.name  || "default";
	this.style = props.style || "static";

	this.canvas    = null;
	this.clear();

	this.eventHandlers = {};
	this.allowedStyles = new Array("static", "dynamic");
    },

    isDynamic: function() {
	return this.style == "dynamic";
    },

    on: function( event, handler ) {
	this.eventHandlers[event] = handler;
    },

    fireEvent: function( event, data ) {
	if( this.eventHandlers[event] ) {
	    this.eventHandlers[event](data);
	}
    },

    setCanvas: function( canvas ) {
	this.canvas = canvas;

	if( this.allowedStyles.indexOf(this.style) < 0 ) {
    	    this.log(this.style + " is an unknown style, reverted to static.\n"+
    		     "Allowed styles are " + this.allowedStyles);
    	    this.style = "static";
	}

	this.canvas.on( "mousedown", this.handleMouseDown.bind(this) );
	this.canvas.on( "mouseup",   this.handleMouseUp  .bind(this) );
	this.canvas.on( "mousedrag", this.handleMouseDrag.bind(this) );
    },

    freeze: function() { if( this.canvas ) { this.canvas.freeze(); } },
    thaw:   function() { if( this.canvas ) { this.canvas.thaw();   } },

    log: function(msg) {
	if( this.canvas ) {
	    this.canvas.log( "Canvas2D.Sheet: " + msg );
	}
    },

    makeDynamic: function() {
	this.style = "dynamic";
    },

    makeStatic: function() {
	this.style = "static";
    },

    at: function(left, top) {
	this.newTop = top;
	this.newLeft = left;
	return this;
    },

    put: function(shape) {
	this.add(shape.setPosition(this.newLeft, this.newTop));
	this.newLeft = null;
	this.newTop = null;
	return shape;
    },

    add: function(shape) {
	this.shapes.push( shape );
	this.shapesMap[shape.getName()] = shape;
	shape.setCanvas(this.canvas);
	this.canvas.render();

	this.log( "Added new shape" +
		  ( shape.props.left != null ? "@" + shape.props.left + ","
		    + shape.props.top : "" ) );
	return shape;
    },

    getShapeAt: function(x,y) {
	for( var s = this.shapes.length-1; s>=0; s-- ) {
	    if( this.shapes[s].hit(x,y) ) {
		return this.shapes[s];
	    }
	}
	return null;
    },

    getShapesIn: function(left, top, width, height) {
	alert( "Canvas2D.Sheet::getShapesIn: not implemented yet" );
    },

    handleMouseDown: function(pos) {
	if( !this.isDynamic() ) { return; }
	this.selectedShape = this.getShapeAt( pos.x, pos.y );
	if( this.selectedShape ) {
	    this.fireEvent( "shapeselected", this.selectedShape );
	}
	this.currentPos = pos;
    },

    handleMouseUp: function(pos) {
	if( !this.isDynamic() ) { return; }
	if( this.selectedShape ) {
	    var pos = this.selectedShape.getPosition();
	    this.log( "Shape moved to " + pos.left + ", " + pos.top );
	} else {
	    this.showSelection = false;
	}
    },

    handleMouseDrag: function(pos) {
	if( !this.isDynamic() ) { return; }
	if( this.selectedShape ) {
	    this.selectedShape.move( pos.dx, pos.dy );
	} else {
	    this.showSelection = true;
	    this.selectionPos  = pos;
	}
    },

    addSelectionOverlay: function() {
	if( this.showSelection ) {
	    var pos = this.selectionPos;
	    var dx = pos.x - this.currentPos.x;
	    var dy = pos.y - this.currentPos.y;

	    this.canvas.fillStyle = "rgba( 0, 0, 255, 0.10 )";
	    this.canvas.fillRect( pos.x <= this.currentPos.x ?
				  pos.x : this.currentPos.x,
				  pos.y <= this.currentPos.y ?
				  pos.y : this.currentPos.y,
				  Math.abs(dx), Math.abs(dy) );
	}
    },

    addSelectionMarkers: function() {
	if( this.selectedShape ) {
	    var box = this.selectedShape.getBox();
	    this.canvas.fillStyle = "rgba( 200, 200, 255, 1 )";
	    var corners = [[ box.left, box.top    ], [ box.right, box.top    ],
		           [ box.left, box.bottom ], [ box.right, box.bottom ]];
	    var canvas = this.canvas;
	    corners.each( function(corner) {
		canvas.beginPath();
		canvas.arc( corner[0],  corner[1], 5, 0, Math.PI*2, true );
		canvas.fill();
	    } );
	}
    },

    clear: function() {
	this.shapes    = [];
    	this.shapesMap = {};
    },

    render: function() {
	var delayed = [];
	this.shapes.each( function(shape) {
	    if( shape.delayRender() ) {
		delayed.push(shape);
	    } else {
		shape.render();
	    }
	} );

	delayed.each( function(shape) { shape.render(); } );

	this.addSelectionOverlay();
	this.addSelectionMarkers();
    },

    toString: function() {
	var s = "";
	s += "Sheet "  + this.name;
	s += " +" + this.style + " {\n";
	this.shapes.each(function(shape) {
	    s += shape.toString("  ") + "\n";
	} );
	s += "}";
	return s;
    }

} );

Canvas2D.Sheet.getNames = function() {
    return [ "sheet" ];
}

Canvas2D.Sheet.from = function(construct, canvas) {
    var style = "static";
    var styleModifier = construct.modifiers.get( "style" );
    if( styleModifier ) {
	style = styleModifier.value.value.toLowerCase();
    }

    construct.modifiers.each(function(pair) {
	if( pair.key.toLowerCase() == "static"
	    || pair.key.toLowerCase() == "dynamic" ) {
	    style = pair.key.toLowerCase();
	}
    });

    var sheet = new Canvas2D.Sheet({ name: construct.name, style: style } );
    canvas.add(sheet);
    return sheet;
};

Canvas2D.ADLVisitor.registerConstruct(Canvas2D.Sheet);
<|endoftext|>


<commit_before>dojo.provide("dijit.form._DateTimeTextBox");

dojo.require("dojo.date");
dojo.require("dojo.date.locale");
dojo.require("dojo.date.stamp");
dojo.require("dijit.form.ValidationTextBox");

dojo.declare(
	"dijit.form._DateTimeTextBox",
	dijit.form.RangeBoundTextBox,
	{
		// summary:
		//		A validating, serializable, range-bound date or time text box.

		// constraints object: min, max
		regExpGen: dojo.date.locale.regexp,
		compare: dojo.date.compare,
		format: function(/*Date*/ value, /*Object*/ constraints){
			if(!value || value.toString() == this._invalid){ return ''; }
			return dojo.date.locale.format(value, constraints);
		},
		parse: function(/*String*/ value, /*Object*/ constraints){
			return dojo.date.locale.parse(value, constraints) || undefined; /* can't return null to getValue since that's special */
		},

		serialize: dojo.date.stamp.toISOString,

		value: new Date(""),	// NaN
		_invalid: (new Date("")).toString(),	// NaN

		// popupClass: String
		//              Name of the popup widget class used to select a date/time
		popupClass: "", // default is no popup = text only
		_selector: "",

		postMixInProperties: function(){
			//dijit.form.RangeBoundTextBox.prototype.postMixInProperties.apply(this, arguments);
			this.inherited("postMixInProperties",arguments);
			var constraints = this.constraints;
			constraints.selector = this._selector;
			if(typeof constraints.min == "string"){ constraints.min = dojo.date.stamp.fromISOString(constraints.min); }
 			if(typeof constraints.max == "string"){ constraints.max = dojo.date.stamp.fromISOString(constraints.max); }
		},

		_onFocus: function(/*Event*/ evt){
			// summary: open the TimePicker popup
			this._open();
		},

		setValue: function(/*Date*/ value, /*Boolean, optional*/ priorityChange){
			// summary:
			//	Sets the date on this textbox
			this.inherited('setValue', arguments);
			if(this._picker){
				// #3948: fix blank date on popup only
				if(!value || value.toString() == this._invalid){value=new Date();}
				this._picker.setValue(value);
			}
		},

		_open: function(){
			// summary:
			//	opens the TimePicker, and sets the onValueSelected value

			if(this.disabled || this.readOnly || !this.popupClass){return;}

			var self = this;

			if(!this._picker){
				var popupProto=dojo.getObject(this.popupClass, false);
				this._picker = new popupProto({
					onValueSelected: function(value){

						self.focus(); // focus the textbox before the popup closes to avoid reopening the popup
						setTimeout(dojo.hitch(self, "_close"), 1); // allow focus time to take

						// this will cause InlineEditBox and other handlers to do stuff so make sure it's last
						dijit.form._DateTimeTextBox.superclass.setValue.call(self, value, true);
					},
					lang: this.lang,
					constraints:this.constraints,
					isDisabledDate: function(/*Date*/ date){
						// summary:
						// 	disables dates outside of the min/max of the _DateTimeTextBox
						return self.constraints && (dojo.date.compare(self.constraints.min,date) > 0 || dojo.date.compare(self.constraints.max,date) < 0);
					}
				});
				this._picker.setValue(this.getValue() || new Date());
			}
			if(!this._opened){
				dijit.popup.open({
					parent: this,
					popup: this._picker,
					around: this.domNode,
					onCancel: dojo.hitch(this, this._close),
					onClose: function(){ self._opened=false; }
				});
				this._opened=true;
			}

			dojo.marginBox(this._picker.domNode,{ w:this.domNode.offsetWidth });
		},

		_close: function(){
			if(this._opened){
				dijit.popup.close(this._picker);
				this._opened=false;
			}
		},

		_onBlur: function(){
			// summary: called magically when focus has shifted away from this widget and it's dropdown
			this._close();
			this.inherited('_onBlur', arguments);
			// don't focus on <input>.  the user has explicitly focused on something else.
		},

		getDisplayedValue:function(){
			return this.textbox.value;
		},

		setDisplayedValue:function(/*String*/ value){
			this.textbox.value=value;
		}
	}
);
<commit_msg>Fixes #5304.  setDisplayedValue now calls setValue which will call onChange. !strict<commit_after>dojo.provide("dijit.form._DateTimeTextBox");

dojo.require("dojo.date");
dojo.require("dojo.date.locale");
dojo.require("dojo.date.stamp");
dojo.require("dijit.form.ValidationTextBox");

dojo.declare(
	"dijit.form._DateTimeTextBox",
	dijit.form.RangeBoundTextBox,
	{
		// summary:
		//		A validating, serializable, range-bound date or time text box.

		// constraints object: min, max
		regExpGen: dojo.date.locale.regexp,
		compare: dojo.date.compare,
		format: function(/*Date*/ value, /*Object*/ constraints){
			if(!value || value.toString() == this._invalid){ return ''; }
			return dojo.date.locale.format(value, constraints);
		},
		parse: function(/*String*/ value, /*Object*/ constraints){
			return dojo.date.locale.parse(value, constraints) || undefined; /* can't return null to getValue since that's special */
		},

		serialize: dojo.date.stamp.toISOString,

		value: new Date(""),	// NaN
		_invalid: (new Date("")).toString(),	// NaN

		// popupClass: String
		//              Name of the popup widget class used to select a date/time
		popupClass: "", // default is no popup = text only
		_selector: "",

		postMixInProperties: function(){
			//dijit.form.RangeBoundTextBox.prototype.postMixInProperties.apply(this, arguments);
			this.inherited("postMixInProperties",arguments);
			var constraints = this.constraints;
			constraints.selector = this._selector;
			if(typeof constraints.min == "string"){ constraints.min = dojo.date.stamp.fromISOString(constraints.min); }
 			if(typeof constraints.max == "string"){ constraints.max = dojo.date.stamp.fromISOString(constraints.max); }
		},

		_onFocus: function(/*Event*/ evt){
			// summary: open the TimePicker popup
			this._open();
		},

		setValue: function(/*Date*/ value, /*Boolean, optional*/ priorityChange, /*String, optional*/ formattedValue){
			// summary:
			//	Sets the date on this textbox
			this.inherited('setValue', arguments);
			if(this._picker){
				// #3948: fix blank date on popup only
				if(!value || value.toString() == this._invalid){value=new Date();}
				this._picker.setValue(value);
			}
		},

		_open: function(){
			// summary:
			//	opens the TimePicker, and sets the onValueSelected value

			if(this.disabled || this.readOnly || !this.popupClass){return;}

			var self = this;

			if(!this._picker){
				var popupProto=dojo.getObject(this.popupClass, false);
				this._picker = new popupProto({
					onValueSelected: function(value){

						self.focus(); // focus the textbox before the popup closes to avoid reopening the popup
						setTimeout(dojo.hitch(self, "_close"), 1); // allow focus time to take

						// this will cause InlineEditBox and other handlers to do stuff so make sure it's last
						dijit.form._DateTimeTextBox.superclass.setValue.call(self, value, true);
					},
					lang: this.lang,
					constraints:this.constraints,
					isDisabledDate: function(/*Date*/ date){
						// summary:
						// 	disables dates outside of the min/max of the _DateTimeTextBox
						return self.constraints && (dojo.date.compare(self.constraints.min,date) > 0 || dojo.date.compare(self.constraints.max,date) < 0);
					}
				});
				this._picker.setValue(this.getValue() || new Date());
			}
			if(!this._opened){
				dijit.popup.open({
					parent: this,
					popup: this._picker,
					around: this.domNode,
					onCancel: dojo.hitch(this, this._close),
					onClose: function(){ self._opened=false; }
				});
				this._opened=true;
			}

			dojo.marginBox(this._picker.domNode,{ w:this.domNode.offsetWidth });
		},

		_close: function(){
			if(this._opened){
				dijit.popup.close(this._picker);
				this._opened=false;
			}
		},

		_onBlur: function(){
			// summary: called magically when focus has shifted away from this widget and it's dropdown
			this._close();
			this.inherited('_onBlur', arguments);
			// don't focus on <input>.  the user has explicitly focused on something else.
		},

		getDisplayedValue:function(){
			return this.textbox.value;
		},

		setDisplayedValue:function(/*String*/ value){
			this.setValue(this.parse(value, this.constraints), true, value);
		}
	}
);
<|endoftext|>


<commit_before>"use strict";

// src/app.js

import { Post } from "./post";
import { ui } from "./ui";

Post.findAll()
  .then(ui.renderPosts)
  .catch( (error) => {
    console.error("Error: ", error)
  });

User.findRecent()
  .then(ui.renderUsers)
  .catch( (error) => {
    console.error("Error: ", error)
  });
<commit_msg>Add missed semicolon<commit_after>"use strict";

// src/app.js

import { Post } from "./post";
import { ui } from "./ui";

Post.findAll()
  .then(ui.renderPosts)
  .catch( (error) => {
    console.error("Error: ", error);
  });

User.findRecent()
  .then(ui.renderUsers)
  .catch( (error) => {
    console.error("Error: ", error);
  });
<|endoftext|>


<commit_before>/* Dependencies: constants.js ol3_script.js, jquery  */

Array.prototype.max = function() {
return Math.max.apply(null, this);
};

Array.prototype.min = function() {
return Math.min.apply(null, this);
};

function HSVtoRGB(h, s, v) {
    var r, g, b, i, f, p, q, t;
    if (arguments.length === 1) {
	s = h.s, v = h.v, h = h.h;
    }
    i = Math.floor(h * 6);
    f = h * 6 - i;
    p = v * (1 - s);
    q = v * (1 - f * s);
    t = v * (1 - (1 - f) * s);
    switch (i % 6) {
	case 0: r = v, g = t, b = p; break;
	case 1: r = q, g = v, b = p; break;
	case 2: r = p, g = v, b = t; break;
	case 3: r = p, g = q, b = v; break;
	case 4: r = t, g = p, b = v; break;
	case 5: r = v, g = p, b = q; break;
    }
    return [Math.round(r * 255),
    	    Math.round(g * 255),
    	    Math.round(b * 255)];
 };

function createPopover(feature){
  return  "

Ort: 	" + feature.get('Ort') + "
"
	+ "
Bundesland: 	" + feature.get('Bundesland') + "
"
	+ "
Anzahl Messjahre: 	" + feature.get('cAll') + "
"
	+ "
Verhaeltnis: 	" + feature.get('rel') + "
"
	+ "
Lage: 	" + feature.get('Lage') + "
"
	+ "
Hoehe: 	" + feature.get('Hoehe') + "
"
	+ "
Von-Bis: 	" + feature.get('von').getFullYear() + "-" + feature.get('bis').getFullYear() + "
"
	+ "
";
};

var megentaStyle = new ol.style.Style({
	image: new ol.style.Circle({
                radius: 3,
                fill: new ol.style.Fill({
                    color: 'magenta'
                })
            })
});

var invisble = new ol.style.Style({
  name : "invisible",
  image: new ol.style.Circle({
	  radius: 0,
	  fill: new ol.style.Fill({
	      color: [0,0,0,0]
	  })
      })
});

var iconStyle = new ol.style.Style({
    image: new ol.style.Icon ({
	anchor: [0.5, 46],
	anchorXUnits: 'fraction',
	anchorYUnits: 'pixels',
	opacity: 0.65,
	// create commons license
	src: 'http://iconshow.me/download.phpl?file=path/media/images/Mixed/small-n-flat-icon/png2/48/-map-marker.png'
    })
});

function createPointStyle(color, radius, theStroke){
  return new ol.style.Style({
	name : "default",
	image: new ol.style.Circle({
	    radius: radius,
	    fill: new ol.style.Fill({
		color: color
	    }),
	    stroke: theStroke
	})
  });
};

function toColorScale(floating){
  return HSVtoRGB((1 - floating) / 3,1,1);
}

var iconFeature = new ol.Feature({
    geometry: new ol.geom.Point(ol.proj.transform([13, 51], 'EPSG:4326','EPSG:3857')),
    name: "adsf",
    population: 4000,
    rainfall: 500
});

var attribution = new ol.control.Attribution({
  collapsible: true,
  label: 'A',
  collapsed: true,
  tipLabel: 'Filter and copyright info',
  target : 'attribution'
});

var map;
$(function(){
  map = new ol.Map({
    target: 'map',
    renderer: 'canvas',
    layers: [
      new ol.layer.Tile({
	source: new ol.source.MapQuest({layer : "osm"})
      })
    ],
    view: new ol.View({
      center: new ol.proj.transform([10.41,51.30], "EPSG:4326", "EPSG:3857"),
      zoom: 7
    }),
    controls: ol.control.defaults({ attribution: false }).extend([attribution])
  });
});

var run = function(geojsonObject) {
    var pointVector, filterObj = [];

    pointVector = new ol.source.Vector({
        features: (new ol.format.GeoJSON()).readFeatures(geojsonObject,{
	    dataProjection : "EPSG:4326",
	    featureProjection : "EPSG:3857"
	}),
    });

    // init dynamic slider ranges
    var dynamicSliderRanges = {
      minFieldName : "MIN",
      maxFieldName : "MAX",
      getMinOf : function(fieldName){
	return this[fieldName + this.minFieldName];
      },
      getMaxOf : function(fieldName){
	return this[fieldName + this.maxFieldName];
      },
      adjustMinMaxFor : function(fieldName, value){
	var currentMin = this[fieldName + this.minFieldName];
	if(!currentMin | currentMin > value){
	  this[fieldName + this.minFieldName] = value;
	}
	var currentMax = this[fieldName + this.maxFieldName];
	if(!currentMax | currentMax  < value){
	  this[fieldName + this.maxFieldName] = value;
	}
      }
    };

    function createNewFilterObj(filters){
      var returnable = {};
      filters.forEach(function(e){
	returnable[e] = false;
      });
      returnable.isFilteredOut = function() {
	  var isFiltered = false;
	  filters.forEach(function(e){
 	    isFiltered = isFiltered || returnable[e];
	  });
	  return isFiltered;
      };
      return returnable;
    }

    var isCor = false;
    pointVector.forEachFeature(function(feature){
      // erstelle Filterobjekt für alle Filter, dies es geben soll!
      filterObj.push(createNewFilterObj(filters));
      feature.B.von = new Date(feature.B.von);
      feature.B.bis = new Date(feature.B.bis);

      // cor means neg value
      if(!isCor){
	isCor = feature.B.rel < 0;
      }
      // init ranges
      dynamicSliderRanges.adjustMinMaxFor(sliderHeight, parseFloat(feature.B.Hoehe));
      dynamicSliderRanges.adjustMinMaxFor(sliderCount, feature.B.cAll);
      dynamicSliderRanges.adjustMinMaxFor(sliderLowerDate, feature.B.von.getFullYear());
      dynamicSliderRanges.adjustMinMaxFor(sliderHigherDate, feature.B.bis.getFullYear());
    });

    if(isCor){
      createLegende($("#" + sliderOutcome), 25, -1.0, 1.0, 200);
    } else {
      createLegende($("#" + sliderOutcome), 50, 0, 1.0, 200);
    }

    var countDatasets = filterObj.length;

    $.each(sliders, function(i,sliderId){
      function adjustSlider(applyable){
	if(!applyable){
	   applyable = function(slider, min, max){
	      slider.value = min;
	   };
	}
	var curSlider = $("#" + sliderId)[0];
	var min = dynamicSliderRanges.getMinOf(sliderId);
	var max = dynamicSliderRanges.getMaxOf(sliderId);
	curSlider.min = min;
	curSlider.max = max;
	applyable(curSlider, min, max);
      }
      if(sliderId == sliderHigherDate){
	adjustSlider(function(slider, min, max){
	  slider.value = max;
	});
      } else {
	adjustSlider();
      }
    });

    // create stylefunction for the feature points

    function calcColorAndRad(feature, opacity){
       if(typeof opacity == 'undefined'){
	 opacity = 1;
       }
       var maxCountDatasets = dynamicSliderRanges.getMaxOf(sliderCount);
       var rad = feature.get("cAll") * 10 / maxCountDatasets + defaultRad; // the main radius of a station marker can switch from ~defaultRad and 10 + defaultRad
       var percentTrueCl = feature.get('rel');
       var color = toColorScale(percentTrueCl); // choose color from green to red
       color.push(opacity); // add the fourth member to the array representing the opacity
       return {"color" : color, "rad" : rad};
    }

    var defaultRad = Math.abs(2 + 200 / countDatasets); //calculate less size if there are many stations to be shown
    var strokedStyleFunction = function (feature, resolution) {
	var styleData = calcColorAndRad(feature);
	return [createPointStyle(styleData.color, styleData.rad, new ol.style.Stroke({
	  color: 'black',
	  width: 1
	}))];

    };

    var styleFunction = function (feature, resolution) {
	var styleData = calcColorAndRad(feature);
	return [createPointStyle(styleData.color, styleData.rad)];
    };

    /**
     * Elements that make up the popup.
     */
    var container = document.getElementById('popup');
    var content = document.getElementById('popup-content');
    var closer = document.getElementById('popup-closer');

    var popup = new ol.Overlay({
       element: container,
       autoPan : true,
       autoPanAnimation: {
         duration: 250
       },
       positioning: 'bottom-center',
       stopEvent: false
    });

    /**
     * Add a click handler to hide the popup.
     * @return {boolean} Don't follow the href.
     */
    closer.onclick = function() {
      popup.setPosition(undefined);
      closer.blur();
      return false;
    };

    map.addOverlay(popup);

    // display popup on click
    map.on('click', function(evt) {
      evt.preventDefault();
      var feature = map.forEachFeatureAtPixel(evt.pixel,
          function(feature, layer) {
	    return feature;
          });
      if (feature) {
	    var coordinate = evt.coordinate;
	    popup.setPosition(coordinate);
	    content.innerHTML = createPopover(feature);
      }
    });

    // highlight hovered feature
    var currentFeature;
    map.on('pointermove', function(evt) {
      evt.preventDefault
      var feature = map.forEachFeatureAtPixel(evt.pixel,
          function(feature, layer) {
	    return feature;
          });
      if (feature) {
	  if(currentFeature){
	      currentFeature.setStyle(styleFunction(feature));
	  }
	  currentFeature = feature;
	  feature.setStyle(strokedStyleFunction(feature));
      } else {
	if(currentFeature){
	    currentFeature.setStyle(styleFunction(currentFeature));
	    currentFeature = undefined;
	}
      }
    });

     $.each(filters, function(i, id){
     	var idCssSelector = "#" + id;
     	$(idCssSelector).bind('slidestop input', function(chngEvt){
		    function ltCompareFloats(comparable, parseable) {
			    return comparable < parseFloat(parseable);
		    };

		    function filteredFeatureOut(feature, compareFunc, comparable, value){
		      if(compareFunc(comparable, value)){
			feature.setStyle(invisble); // filtering out meens set the style to invisble
			return true;
		      } else {
			return false;
		      }
		    };
		    $.each(pointVector.getFeatures(),  function(i, e){
		        var filter = filterObj[i];
		        function checkFor(comparable, filterName, compareFunc){
			      var result = false;
			      if(chngEvt.target.id == filterName){
				 if(typeof chngEvt.target.value  == 'undefined'){
				   filter[chngEvt.target.id] = filteredFeatureOut(e, compareFunc, comparable, {
				     "from" : parseFloat($(chngEvt.target).attr("from")),
				     "to"   : parseFloat($(chngEvt.target).attr("to"))
				   });
				 } else {
				   filter[chngEvt.target.id] = filteredFeatureOut(e, compareFunc, comparable, chngEvt.target.value);
				 }
				 result = true;
			      }
			      return result;
	                };
		        checkFor(e.B.Hoehe, sliderHeight, ltCompareFloats) || checkFor(e.B.cAll, sliderCount, ltCompareFloats) || checkFor(e.B.von, sliderLowerDate, function(a,b){return a < new Date(parseInt(b), 0, 1);}) || checkFor(e.B.bis, sliderHigherDate, function(a,b){return a > new Date(parseInt(b), 0, 1);})
			|| checkFor(e.B.rel, sliderOutcome, function(a,b){
			  return !(e.B.rel > b.from && e.B.rel < b.to);
			});

		        if(!filter.isFilteredOut()) {
			    e.setStyle(styleFunction(e));
		        }

		    });
     	});
     });

     map.addLayer(new ol.layer.Vector({
      	name : "currentData",
      	source: pointVector,
      	style : styleFunction,
      }));
};

<commit_msg>code reformatting<commit_after>/* Dependencies: constants.js ol3_script.js, jquery  */

Array.prototype.max = function() {
return Math.max.apply(null, this);
};

Array.prototype.min = function() {
return Math.min.apply(null, this);
};

function HSVtoRGB(h, s, v) {
    var r, g, b, i, f, p, q, t;
    if (arguments.length === 1) {
	s = h.s, v = h.v, h = h.h;
    }
    i = Math.floor(h * 6);
    f = h * 6 - i;
    p = v * (1 - s);
    q = v * (1 - f * s);
    t = v * (1 - (1 - f) * s);
    switch (i % 6) {
	case 0: r = v, g = t, b = p; break;
	case 1: r = q, g = v, b = p; break;
	case 2: r = p, g = v, b = t; break;
	case 3: r = p, g = q, b = v; break;
	case 4: r = t, g = p, b = v; break;
	case 5: r = v, g = p, b = q; break;
    }
    return [Math.round(r * 255),
    	    Math.round(g * 255),
    	    Math.round(b * 255)];
 };

function createPopover(feature){
  return  "

Ort: 	" + feature.get('Ort') + "
"
	+ "
Bundesland: 	" + feature.get('Bundesland') + "
"
	+ "
Anzahl Messjahre: 	" + feature.get('cAll') + "
"
	+ "
Verhaeltnis: 	" + feature.get('rel') + "
"
	+ "
Lage: 	" + feature.get('Lage') + "
"
	+ "
Hoehe: 	" + feature.get('Hoehe') + "
"
	+ "
Von-Bis: 	" + feature.get('von').getFullYear() + "-" + feature.get('bis').getFullYear() + "
"
	+ "
";
};

var megentaStyle = new ol.style.Style({
	image: new ol.style.Circle({
                radius: 3,
                fill: new ol.style.Fill({
                    color: 'magenta'
                })
            })
});

var invisble = new ol.style.Style({
  name : "invisible",
  image: new ol.style.Circle({
	  radius: 0,
	  fill: new ol.style.Fill({
	      color: [0,0,0,0]
	  })
      })
});

var iconStyle = new ol.style.Style({
    image: new ol.style.Icon ({
	anchor: [0.5, 46],
	anchorXUnits: 'fraction',
	anchorYUnits: 'pixels',
	opacity: 0.65,
	// create commons license
	src: 'http://iconshow.me/download.phpl?file=path/media/images/Mixed/small-n-flat-icon/png2/48/-map-marker.png'
    })
});

function createPointStyle(color, radius, theStroke){
  return new ol.style.Style({
	name : "default",
	image: new ol.style.Circle({
	    radius: radius,
	    fill: new ol.style.Fill({
		color: color
	    }),
	    stroke: theStroke
	})
  });
};

function toColorScale(floating){
  return HSVtoRGB((1 - floating) / 3,1,1);
}

var iconFeature = new ol.Feature({
    geometry: new ol.geom.Point(ol.proj.transform([13, 51], 'EPSG:4326','EPSG:3857')),
    name: "adsf",
    population: 4000,
    rainfall: 500
});

var attribution = new ol.control.Attribution({
  collapsible: true,
  label: 'A',
  collapsed: true,
  tipLabel: 'Filter and copyright info',
  target : 'attribution'
});

var map;
$(function(){
  map = new ol.Map({
    target: 'map',
    renderer: 'canvas',
    layers: [
      new ol.layer.Tile({
	source: new ol.source.MapQuest({layer : "osm"})
      })
    ],
    view: new ol.View({
      center: new ol.proj.transform([10.41,51.30], "EPSG:4326", "EPSG:3857"),
      zoom: 7
    }),
    controls: ol.control.defaults({ attribution: false }).extend([attribution])
  });
});

var run = function(geojsonObject) {
    var pointVector, filterObj = [];

    pointVector = new ol.source.Vector({
        features: (new ol.format.GeoJSON()).readFeatures(geojsonObject,{
	    dataProjection : "EPSG:4326",
	    featureProjection : "EPSG:3857"
	}),
    });

    // init dynamic slider ranges
    var dynamicSliderRanges = {
      minFieldName : "MIN",
      maxFieldName : "MAX",
      getMinOf : function(fieldName){
	return this[fieldName + this.minFieldName];
      },
      getMaxOf : function(fieldName){
	return this[fieldName + this.maxFieldName];
      },
      adjustMinMaxFor : function(fieldName, value){
	var currentMin = this[fieldName + this.minFieldName];
	if(!currentMin | currentMin > value){
	  this[fieldName + this.minFieldName] = value;
	}
	var currentMax = this[fieldName + this.maxFieldName];
	if(!currentMax | currentMax  < value){
	  this[fieldName + this.maxFieldName] = value;
	}
      }
    };

    function createNewFilterObj(filters){
      var returnable = {};
      filters.forEach(function(e){
	returnable[e] = false;
      });
      returnable.isFilteredOut = function() {
	  var isFiltered = false;
	  filters.forEach(function(e){
 	    isFiltered = isFiltered || returnable[e];
	  });
	  return isFiltered;
      };
      return returnable;
    }

    var isCor = false;
    pointVector.forEachFeature(function(feature){
      // erstelle Filterobjekt für alle Filter, dies es geben soll!
      filterObj.push(createNewFilterObj(filters));
      feature.B.von = new Date(feature.B.von);
      feature.B.bis = new Date(feature.B.bis);

      // cor means neg value
      if(!isCor){
	isCor = feature.B.rel < 0;
      }
      // init ranges
      dynamicSliderRanges.adjustMinMaxFor(sliderHeight, parseFloat(feature.B.Hoehe));
      dynamicSliderRanges.adjustMinMaxFor(sliderCount, feature.B.cAll);
      dynamicSliderRanges.adjustMinMaxFor(sliderLowerDate, feature.B.von.getFullYear());
      dynamicSliderRanges.adjustMinMaxFor(sliderHigherDate, feature.B.bis.getFullYear());
    });

    if(isCor){
      createLegende($("#" + sliderOutcome), 25, -1.0, 1.0, 200);
    } else {
      createLegende($("#" + sliderOutcome), 50, 0, 1.0, 200);
    }

    var countDatasets = filterObj.length;

    $.each(sliders, function(i,sliderId){
      function adjustSlider(applyable){
	if(!applyable){
	   applyable = function(slider, min, max){
	      slider.value = min;
	   };
	}
	var curSlider = $("#" + sliderId)[0];
	var min = dynamicSliderRanges.getMinOf(sliderId);
	var max = dynamicSliderRanges.getMaxOf(sliderId);
	curSlider.min = min;
	curSlider.max = max;
	applyable(curSlider, min, max);
      }
      if(sliderId == sliderHigherDate){
	adjustSlider(function(slider, min, max){
	  slider.value = max;
	});
      } else {
	adjustSlider();
      }
    });

    // create stylefunction for the feature points

    function calcColorAndRad(feature, opacity){
       if(typeof opacity == 'undefined'){
	 opacity = 1;
       }
       var maxCountDatasets = dynamicSliderRanges.getMaxOf(sliderCount);
       var rad = feature.get("cAll") * 10 / maxCountDatasets + defaultRad; // the main radius of a station marker can switch from ~defaultRad and 10 + defaultRad
       var percentTrueCl = feature.get('rel');
       var color = toColorScale(percentTrueCl); // choose color from green to red
       color.push(opacity); // add the fourth member to the array representing the opacity
       return {"color" : color, "rad" : rad};
    }

    var defaultRad = Math.abs(2 + 200 / countDatasets); //calculate less size if there are many stations to be shown
    var strokedStyleFunction = function (feature, resolution) {
	var styleData = calcColorAndRad(feature);
	return [createPointStyle(styleData.color, styleData.rad, new ol.style.Stroke({
	  color: 'black',
	  width: 1
	}))];

    };

    var styleFunction = function (feature, resolution) {
	var styleData = calcColorAndRad(feature);
	return [createPointStyle(styleData.color, styleData.rad)];
    };

    /**
     * Elements that make up the popup.
     */
    var container = document.getElementById('popup');
    var content = document.getElementById('popup-content');
    var closer = document.getElementById('popup-closer');

    var popup = new ol.Overlay({
       element: container,
       autoPan : true,
       autoPanAnimation: {
         duration: 250
       },
       positioning: 'bottom-center',
       stopEvent: false
    });

    /**
     * Add a click handler to hide the popup.
     * @return {boolean} Don't follow the href.
     */
    closer.onclick = function() {
      popup.setPosition(undefined);
      closer.blur();
      return false;
    };

    map.addOverlay(popup);

    // display popup on click
    map.on('click', function(evt) {
      evt.preventDefault();
      var feature = map.forEachFeatureAtPixel(evt.pixel,
          function(feature, layer) {
	    return feature;
          });
      if (feature) {
	    var coordinate = evt.coordinate;
	    popup.setPosition(coordinate);
	    content.innerHTML = createPopover(feature);
      }
    });

    // highlight hovered feature
    var currentFeature;
    map.on('pointermove', function(evt) {
      evt.preventDefault
      var feature = map.forEachFeatureAtPixel(evt.pixel,
          function(feature, layer) {
	    return feature;
          });
      if (feature) {
	  if(currentFeature){
	      currentFeature.setStyle(styleFunction(feature));
	  }
	  currentFeature = feature;
	  feature.setStyle(strokedStyleFunction(feature));
      } else {
	if(currentFeature){
	    currentFeature.setStyle(styleFunction(currentFeature));
	    currentFeature = undefined;
	}
      }
    });

     $.each(filters, function(i, id){
     	var idCssSelector = "#" + id;
     	$(idCssSelector).bind('slidestop input', function(chngEvt){
		    function ltCompareFloats(comparable, parseable) {
			    return comparable < parseFloat(parseable);
		    };

		    function filteredFeatureOut(feature, compareFunc, comparable, value){
		      if(compareFunc(comparable, value)){
			feature.setStyle(invisble); // filtering out meens set the style to invisble
			return true;
		      } else {
			return false;
		      }
		    };
		    $.each(pointVector.getFeatures(),  function(i, e){
		        var filter = filterObj[i];
		        function checkFor(comparable, filterName, compareFunc){
			      var result = false;
			      if(chngEvt.target.id == filterName){
				 if(typeof chngEvt.target.value  == 'undefined'){
				   filter[chngEvt.target.id] = filteredFeatureOut(e, compareFunc, comparable, {
				     "from" : parseFloat($(chngEvt.target).attr("from")),
				     "to"   : parseFloat($(chngEvt.target).attr("to"))
				   });
				 } else {
				   filter[chngEvt.target.id] = filteredFeatureOut(e, compareFunc, comparable, chngEvt.target.value);
				 }
				 result = true;
			      }
			      return result;
	                };
		        checkFor(e.B.Hoehe, sliderHeight, ltCompareFloats) ||
		        checkFor(e.B.cAll, sliderCount, ltCompareFloats) ||
		        checkFor(e.B.von, sliderLowerDate, function(a,b){return a < new Date(parseInt(b), 0, 1);}) ||
		        checkFor(e.B.bis, sliderHigherDate, function(a,b){return a > new Date(parseInt(b), 0, 1);}) ||
		        checkFor(e.B.rel, sliderOutcome, function(a,b){
			  return !(e.B.rel > b.from && e.B.rel < b.to);
			});

		        if(!filter.isFilteredOut()) {
			    e.setStyle(styleFunction(e));
		        }

		    });
     	});
     });

     map.addLayer(new ol.layer.Vector({
      	name : "currentData",
      	source: pointVector,
      	style : styleFunction,
      }));
};

<|endoftext|>


<commit_before>
var EventEmitter = process.EventEmitter;

var items = new EventEmitter();

items.post = function(item)
{
    items.emit('item', item);
}

items.on('item', function(item)
{
    if (typeof item == 'function')
        item();
});

function ActorWrapper(obj)
{
    var agent = this;

    for (n in obj)
    {
        if (typeof obj[n] != 'function')
            continue;
        this[n] = function()
        {
            var args = arguments;

            items.post(function()
            {
                obj[n].apply(obj, args);
            });
        }
    }
}

exports.post = function(item)
{
    items.post(item);
}

exports.asActor = function(obj)
{
    return new ActorWrapper(obj);
}

<commit_msg>makePost in internal implementation<commit_after>
var EventEmitter = process.EventEmitter;

var items = new EventEmitter();

items.post = function(item)
{
    items.emit('item', item);
}

items.on('item', function(item)
{
    if (typeof item == 'function')
        item();
});

function ActorWrapper(obj)
{
    var agent = this;

    for (n in obj)
    {
        if (typeof obj[n] != 'function')
            continue;

        this[n] = function()
        {
            items.post(makePost(obj, obj[n], arguments));
        }
    }
}

function makePost(target, fn, args)
{
    return function() { fn.apply(target, args); }
}

exports.post = function(item)
{
    items.post(item);
}

exports.asActor = function(obj)
{
    return new ActorWrapper(obj);
}

<|endoftext|>


<commit_before>require('fileapi');

var backbone = require('backbone');
var backboneBase = require('backbone-base');
var csv = require('csv');
var getUri = require('get-uri');
var Goodtables = require('goodtables');
var highlight = require('highlight-redux');
var jsonEditor = require('json-editor');
var jtsInfer = require('json-table-schema').infer;
var registry = require('./registry');
var request = require('superagent-bluebird-promise');
var UploadView = require('./upload');
var _ = require('underscore');
var $ = require('jquery');
var validator = require('validator');

// Convert name into title
function titleize(name) {
  return name
    .replace(/[\-\._]+/g, ' ')
    .replace(/([a-z]{1})([A-Z]{1})/g, '$1 $2')
    .replace(/\s+/g, ' ')
    .toLowerCase();
}

// Upload data file and populate .resource array with item
DataUploadView = backbone.BaseView.extend({
  events: {
    // Set up and append .resources row
    'change [data-id=input]': function(E) {
      FileAPI.readAsText(FileAPI.getFiles(E.currentTarget)[0], (function (EV) {
        if(EV.type === 'load') {
          csv.parse(EV.result, (function(E, D) {
            var
              schema,
              editor = this.options.form.getEditor('root.resources');

            if(E)
              throw E;

            schema = jtsInfer(D[0], _.rest(D));

            // Delete last row if empty
            if(_.chain(editor.getValue()).last().flatten().compact().isEmpty().value())
              editor.delete_last_row_button.click();

            editor.addRow({
              name: _.last(EV.target.name.split('/')).toLowerCase().replace(/\.[^.]+$|[^a-z^\-^\d^_^\.]+/g, ''),
              path: EV.target.name,
              schema: schema
            }, true);

            // Save data source in the form
            _.last(editor.rows).dataSource = {schema: schema, data: EV.result};
          }).bind(this));
        } else if( EV.type ==='progress' ) {
          this.setProgress(EV.loaded/EV.total * 100);
        } else {
          this.setError('File upload failed');
        }

        this.$(E.currentTarget).val('');
      }).bind(this));
    },

    'click [data-id=upload-data-file]': function() { this.$('[data-id=input]').trigger('click'); }
  },

  render: function() {
    this.$el
      .append(
        $(this.options.form.theme.getButton('Upload data file', '', 'Upload data file'))
          .attr('data-id', 'upload-data-file')
      )

      .append('<input data-id="input" type="file" accept="text/csv" style="display: none;">');

    return this;
  },

  setError: function() { return this; },
  setProgress: function() { return this; }
});

module.exports = {
  DescriptorEditView: backbone.BaseView.extend({
    activate: function(state) {
      backbone.BaseView.prototype.activate.call(this, state);
      this.layout.upload.activate(state);
      return this;
    },

    events: {
      // Populate main title and resource title fields
      'keyup [data-schemapath].container-name input': function(event) {
        var $input = $(event.currentTarget);
        var $title = $input.closest('[data-schematype=object]').find('.row .container-title input').eq(0);

        // Do not populate user changed field
        if(_.result($title[0], 'edited'))
          return true;

        $title.val(titleize($input.val()));
      },

      // Do not populate title field with name field data if title was edited
      // by user. Consider it is not edited once user empties it.
      'keyup [data-schemapath].container-title input': function(event) {
        var $input = $(event.currentTarget);

        event.currentTarget.edited = Boolean($input.val());

        // If user empties the title field then populate it with name field value
        if(!event.currentTarget.edited)
          $input.closest('[data-schematype=object]').find('.row .container-name input').trigger('keyup');
      },

      'click #validate-resources': function() {
        var goodTables = new Goodtables({method: 'post', report_type: 'grouped'});

        // Navigate to valifation results just once during series of API calls
        var navigateToResults = _.once(function(id) { window.ROUTER.navigate('/validation-results/' + id, {trigger: true}); });

        window.APP.layout.validationResultList.reset(new backbone.Collection());

        _.each(this.layout.form.getEditor('root.resources').rows, function(R) {
          // Conditional promises
          (function() {
            if(R.dataSource)
              return goodTables.run(R.dataSource.data, JSON.stringify(R.dataSource.schema));

            // If data source stored as URL in a row then first grab it and then return goodtables promise
            if(validator.isURL(R.value.url) && _.contains([R.mediatype, R.format], 'text/csv'))
              return request.get(R.value.url).then(function(RES) {
                // Need schema
                return (new Promise(function(RS, RJ) {
                  csv.parse(RES.text, function(E, D) {
                    if(E) {
                      RJ(E);
                      return false;
                    }

                    RS({data: RES.text, schema: jtsInfer(D[0], _.rest(D))});
                  });
                })).then(function(CSV) { R.dataSource = CSV; return goodTables.run(CSV.data, JSON.stringify(CSV.schema)); });
              });

            return new Promise(function(RS, RJ) { RS(false); });
          })()

            .then(function(M) {
              if(!M)
                return false;

              // Validation completed
              window.APP.layout.validationResultList.collection

                // Grouped report has complicated structure
                .add(M.getGroupedByRows().map(function(SR) { return _.extend(_.values(SR)[0], {
                  headers: M.getHeaders(),
                  resource_id: R.key
                }); }));

              // Navigate between resources in validation results
              window.APP.layout.validationResultList.layout.tabs.add(new backbone.Model({
                title: R.getValue().path,

                // .key is a unique property among all resources rows
                url: '/validation-results/' + R.key
              }));

              navigateToResults(R.key);
            });
        });
      }
    },

    initialize: function(options) {
      highlight.configure({useBR: true});

      // Customize theme
      JSONEditor.defaults.iconlibs.fontawesome4 = JSONEditor.defaults.iconlibs.fontawesome4.extend({
        mapping: {
          'collapse': 'minus',
          'expand'  : 'plus',
          'delete'  : 'times',
          'edit'    : 'pencil',
          'add'     : 'plus',
          'cancel'  : 'ban',
          'save'    : 'save',
          'moveup'  : 'arrow-up',
          'movedown': 'arrow-down'
        }
      });

      // Mark required fields
      JSONEditor.defaults.editors = _.mapObject(JSONEditor.defaults.editors, function(E, K) {
        return E.extend({
          updateHeaderText: function() {
            if(this.parent && _.indexOf(this.parent.schema.required, this.key) >= 0)
              $(this.label).html(this.getTitle() + ' <label class="required-field">*</label>');
            else
              JSONEditor.AbstractEditor.prototype.updateHeaderText.apply(this, arguments);
          }
        });
      });

      return backbone.BaseView.prototype.initialize.call(this, options);
    },

    render: function() {
      this.layout.upload = new UploadView({el: window.APP.$('#upload-data-package'), parent: this});
      this.layout.registryList = new registry.ListView({el: window.APP.$('#registry-list'), container: '[data-id=list-container]', parent: this});
      return this;
    },

    // Utility method to remove empty values from object recursive
    compactObject: function(data) {
      _.each(data, function(v, k) {
        if(_.isEmpty(v)) {
          delete data[k];
        } else if(_.isArray(v) || _.isObject(v)) {
          v = this.compactObject(v);

          if(_.isArray(v))
            v = _.compact(v);

          if(_.isEmpty(v))
            delete data[k];
          else data[k] = v;
        }
      }, this);

      return data;
    },

    getFilledValues: function() { return this.compactObject(this.layout.form.getValue()); },
    getValue: function () { return this.layout.form.getValue(); },
    hasChanges: function() { return Boolean(this.changed); },

    reset: function(schema) {
      var formData;

      // Clean up previous state
      if(this.layout.form) {
        formData = this.getFilledValues();
        this.layout.form.destroy();
        this.layout.uploadData.undelegateEvents().remove();
      }

      // Proper representation of all form buttons. Avoid changing each newly
      // rendered button by rewriting the .getButton()
      JSONEditor.defaults.themes.bootstrap3.prototype.getButton = function(text, icon, title) {
        var el = document.createElement('button');

        el.type = 'button';
        el.className += 'btn btn-info btn-sm';
        this.setButtonText(el,text,icon,title);
        return el;
      };

      this.layout.form = new JSONEditor(this.$('[data-id=form-container]').get(0), {
        schema: schema,
        show_errors: 'change',
        theme: 'bootstrap3',
        disable_edit_json: true,
        disable_properties: true,
        iconlib: 'fontawesome4'
      });

      // Remove Top-level collapse button
      this.layout.form.root.toggle_button.remove();

      // Bind local event to form nodes after form is renedered
      this.delegateEvents();

      this.layout.uploadData = (new DataUploadView({
        el: this.layout.form.theme.getHeaderButtonHolder(),
        form: this.layout.form,
        parent: this
      })).render();

      this.layout.form.on('ready', (function() {
        // There is no any good way to bind events to custom button or even add cutsom button
        $(this.layout.form.getEditor('root.resources').container)
          .children('h3').append(this.layout.uploadData.el);

        // Detecting changes
        this.changed = false;

        // After `ready` event fired, editor fire `change` event regarding to the initial changes
        this.layout.form.on('change', _.after(2, (function() {
          var resources = this.layout.form.getEditor('root.resources');
          var resourcesLength = _.result(resources.rows, 'length');

          this.changed = true;
          window.APP.layout.download.reset(this.layout.form.getValue(), schema).activate();
          this.showResult();

          // Expand resources section if there are any resources, collapse if row is empty
          if(resourcesLength && resources.collapsed || !resourcesLength && !resources.collapsed)
            $(resources.toggle_button).trigger('click');

          // Do not allow changing schema field type — disable type selectbox
          this.$('[data-schemapath]:not([data-schematype]) select.form-control').prop('hidden', true);

          // Populate empty title fields with name field value. Rely on DOM events defined
          // in DescriptorEditView.events
          _.each($('[data-schemapath].container-title input', this.layout.form.getEditor('root').container), function(I) {
            I.edited = Boolean($(I).val());
          });

          $('[data-schemapath].container-name input', this.layout.form.getEditor('root').container).trigger('keyup');
        }).bind(this)));

        $('#json-code').prop('hidden', true);

        // Collapse editor and add empty item if it has no value
        _.each(this.$('[data-schemapath^="root."]:has(.json-editor-btn-collapse)'), function(E) {
          var editor = this.layout.form.getEditor($(E).data('schemapath'));
          var isEmpty = _.isEmpty(editor.getValue());

          // Empty array data should have one empty item
          if(_.contains(['resources', 'sources', 'licences'], E.dataset.schemapath.replace('root.', '')))
            $(editor.add_row_button).trigger('click');

          if(isEmpty && !editor.collapsed)
            $(editor.toggle_button).trigger('click');
        }, this);

        // Looks like previous loop is somehow async
        setTimeout((function() {
          $('#json-code').prop('hidden', false);
        }).bind(this), 300);

        // If on the previous form was entered values try to apply it to new form
        if(formData)
          this.layout.form.setValue(_.extend({}, this.layout.form.getValue(formData), formData));

        // Remove collapse button on add new item in collection
        _.each(this.$('[data-schemapath][data-schemapath!=root]:has(.json-editor-btn-add)'), function(E) {
          var
            editor = this.layout.form.getEditor($(E).data('schemapath'));

          $(_.pluck(editor.rows, 'toggle_button')).remove();

          $(editor.add_row_button).click((function() {
            $(_.last(this.rows).toggle_button).remove();
          }).bind(editor));
        }, this);
      }).bind(this));
    },

    showResult: function() {
      $('#json-code').html(highlight.fixMarkup(
        highlight.highlight('json', JSON.stringify(this.layout.form.getValue(), undefined, 2)).value
      ));

      return this;
    }
  })
};
<commit_msg>Remove empty properties from result & show required messages (#97)<commit_after>require('fileapi');

var backbone = require('backbone');
var backboneBase = require('backbone-base');
var csv = require('csv');
var getUri = require('get-uri');
var Goodtables = require('goodtables');
var highlight = require('highlight-redux');
var jsonEditor = require('json-editor');
var jtsInfer = require('json-table-schema').infer;
var registry = require('./registry');
var request = require('superagent-bluebird-promise');
var UploadView = require('./upload');
var _ = require('underscore');
var $ = require('jquery');
var validator = require('validator');

// Convert name into title
function titleize(name) {
  return name
    .replace(/[\-\._]+/g, ' ')
    .replace(/([a-z]{1})([A-Z]{1})/g, '$1 $2')
    .replace(/\s+/g, ' ')
    .toLowerCase();
}

// Upload data file and populate .resource array with item
DataUploadView = backbone.BaseView.extend({
  events: {
    // Set up and append .resources row
    'change [data-id=input]': function(E) {
      FileAPI.readAsText(FileAPI.getFiles(E.currentTarget)[0], (function (EV) {
        if(EV.type === 'load') {
          csv.parse(EV.result, (function(E, D) {
            var
              schema,
              editor = this.options.form.getEditor('root.resources');

            if(E)
              throw E;

            schema = jtsInfer(D[0], _.rest(D));

            // Delete last row if empty
            if(_.chain(editor.getValue()).last().flatten().compact().isEmpty().value())
              editor.delete_last_row_button.click();

            editor.addRow({
              name: _.last(EV.target.name.split('/')).toLowerCase().replace(/\.[^.]+$|[^a-z^\-^\d^_^\.]+/g, ''),
              path: EV.target.name,
              schema: schema
            }, true);

            // Save data source in the form
            _.last(editor.rows).dataSource = {schema: schema, data: EV.result};
          }).bind(this));
        } else if( EV.type ==='progress' ) {
          this.setProgress(EV.loaded/EV.total * 100);
        } else {
          this.setError('File upload failed');
        }

        this.$(E.currentTarget).val('');
      }).bind(this));
    },

    'click [data-id=upload-data-file]': function() { this.$('[data-id=input]').trigger('click'); }
  },

  render: function() {
    this.$el
      .append(
        $(this.options.form.theme.getButton('Upload data file', '', 'Upload data file'))
          .attr('data-id', 'upload-data-file')
      )

      .append('<input data-id="input" type="file" accept="text/csv" style="display: none;">');

    return this;
  },

  setError: function() { return this; },
  setProgress: function() { return this; }
});

module.exports = {
  DescriptorEditView: backbone.BaseView.extend({
    activate: function(state) {
      backbone.BaseView.prototype.activate.call(this, state);
      this.layout.upload.activate(state);
      return this;
    },

    events: {
      // Populate main title and resource title fields
      'keyup [data-schemapath].container-name input': function(event) {
        var $input = $(event.currentTarget);
        var $title = $input.closest('[data-schematype=object]').find('.row .container-title input').eq(0);

        // Do not populate user changed field
        if(_.result($title[0], 'edited'))
          return true;

        $title.val(titleize($input.val()));
      },

      // Do not populate title field with name field data if title was edited
      // by user. Consider it is not edited once user empties it.
      'keyup [data-schemapath].container-title input': function(event) {
        var $input = $(event.currentTarget);

        event.currentTarget.edited = Boolean($input.val());

        // If user empties the title field then populate it with name field value
        if(!event.currentTarget.edited)
          $input.closest('[data-schematype=object]').find('.row .container-name input').trigger('keyup');
      },

      'click #validate-resources': function() {
        var goodTables = new Goodtables({method: 'post', report_type: 'grouped'});

        // Navigate to valifation results just once during series of API calls
        var navigateToResults = _.once(function(id) { window.ROUTER.navigate('/validation-results/' + id, {trigger: true}); });

        window.APP.layout.validationResultList.reset(new backbone.Collection());

        _.each(this.layout.form.getEditor('root.resources').rows, function(R) {
          // Conditional promises
          (function() {
            if(R.dataSource)
              return goodTables.run(R.dataSource.data, JSON.stringify(R.dataSource.schema));

            // If data source stored as URL in a row then first grab it and then return goodtables promise
            if(validator.isURL(R.value.url) && _.contains([R.mediatype, R.format], 'text/csv'))
              return request.get(R.value.url).then(function(RES) {
                // Need schema
                return (new Promise(function(RS, RJ) {
                  csv.parse(RES.text, function(E, D) {
                    if(E) {
                      RJ(E);
                      return false;
                    }

                    RS({data: RES.text, schema: jtsInfer(D[0], _.rest(D))});
                  });
                })).then(function(CSV) { R.dataSource = CSV; return goodTables.run(CSV.data, JSON.stringify(CSV.schema)); });
              });

            return new Promise(function(RS, RJ) { RS(false); });
          })()

            .then(function(M) {
              if(!M)
                return false;

              // Validation completed
              window.APP.layout.validationResultList.collection

                // Grouped report has complicated structure
                .add(M.getGroupedByRows().map(function(SR) { return _.extend(_.values(SR)[0], {
                  headers: M.getHeaders(),
                  resource_id: R.key
                }); }));

              // Navigate between resources in validation results
              window.APP.layout.validationResultList.layout.tabs.add(new backbone.Model({
                title: R.getValue().path,

                // .key is a unique property among all resources rows
                url: '/validation-results/' + R.key
              }));

              navigateToResults(R.key);
            });
        });
      }
    },

    initialize: function(options) {
      highlight.configure({useBR: true});

      // Customize theme
      JSONEditor.defaults.iconlibs.fontawesome4 = JSONEditor.defaults.iconlibs.fontawesome4.extend({
        mapping: {
          'collapse': 'minus',
          'expand'  : 'plus',
          'delete'  : 'times',
          'edit'    : 'pencil',
          'add'     : 'plus',
          'cancel'  : 'ban',
          'save'    : 'save',
          'moveup'  : 'arrow-up',
          'movedown': 'arrow-down'
        }
      });

      // Mark required fields
      JSONEditor.defaults.editors = _.mapObject(JSONEditor.defaults.editors, function(E, K) {
        return E.extend({
          updateHeaderText: function() {
            if(this.parent && _.indexOf(this.parent.schema.required, this.key) >= 0)
              $(this.label).html(this.getTitle() + ' <label class="required-field">*</label>');
            else
              JSONEditor.AbstractEditor.prototype.updateHeaderText.apply(this, arguments);
          }
        });
      });

      return backbone.BaseView.prototype.initialize.call(this, options);
    },

    render: function() {
      this.layout.upload = new UploadView({el: window.APP.$('#upload-data-package'), parent: this});
      this.layout.registryList = new registry.ListView({el: window.APP.$('#registry-list'), container: '[data-id=list-container]', parent: this});
      return this;
    },

    // Utility method to remove empty values from object recursive
    compactObject: function(data) {
      _.each(data, function(v, k) {
        if(_.isEmpty(v)) {
          delete data[k];
        } else if(_.isArray(v) || _.isObject(v)) {
          v = this.compactObject(v);

          if(_.isArray(v))
            v = _.compact(v);

          if(_.isEmpty(v))
            delete data[k];
          else data[k] = v;
        }
      }, this);

      return data;
    },

    getFilledValues: function() { return this.compactObject(this.layout.form.getValue()); },
    getValue: function () { return this.layout.form.getValue(); },
    hasChanges: function() { return Boolean(this.changed); },

    reset: function(schema) {
      var formData;

      // Clean up previous state
      if(this.layout.form) {
        formData = this.getFilledValues();
        this.layout.form.destroy();
        this.layout.uploadData.undelegateEvents().remove();
      }

      // Proper representation of all form buttons. Avoid changing each newly
      // rendered button by rewriting the .getButton()
      JSONEditor.defaults.themes.bootstrap3.prototype.getButton = function(text, icon, title) {
        var el = document.createElement('button');

        el.type = 'button';
        el.className += 'btn btn-info btn-sm';
        this.setButtonText(el,text,icon,title);
        return el;
      };

      this.layout.form = new JSONEditor(this.$('[data-id=form-container]').get(0), {
        schema: schema,
        show_errors: 'change',
        remove_empty_properties: true,
        theme: 'bootstrap3',
        disable_edit_json: true,
        disable_properties: true,
        iconlib: 'fontawesome4'
      });

      // Remove Top-level collapse button
      this.layout.form.root.toggle_button.remove();

      // Bind local event to form nodes after form is renedered
      this.delegateEvents();

      this.layout.uploadData = (new DataUploadView({
        el: this.layout.form.theme.getHeaderButtonHolder(),
        form: this.layout.form,
        parent: this
      })).render();

      this.layout.form.on('ready', (function() {
        // There is no any good way to bind events to custom button or even add cutsom button
        $(this.layout.form.getEditor('root.resources').container)
          .children('h3').append(this.layout.uploadData.el);

        // Detecting changes
        this.changed = false;

        // After `ready` event fired, editor fire `change` event regarding to the initial changes
        this.layout.form.on('change', _.after(2, (function() {
          var resources = this.layout.form.getEditor('root.resources');
          var resourcesLength = _.result(resources.rows, 'length');

          this.changed = true;
          window.APP.layout.download.reset(this.layout.form.getValue(), schema).activate();
          this.showResult();

          // Expand resources section if there are any resources, collapse if row is empty
          if(resourcesLength && resources.collapsed || !resourcesLength && !resources.collapsed)
            $(resources.toggle_button).trigger('click');

          // Do not allow changing schema field type — disable type selectbox
          this.$('[data-schemapath]:not([data-schematype]) select.form-control').prop('hidden', true);

          // Populate empty title fields with name field value. Rely on DOM events defined
          // in DescriptorEditView.events
          _.each($('[data-schemapath].container-title input', this.layout.form.getEditor('root').container), function(I) {
            I.edited = Boolean($(I).val());
          });

          $('[data-schemapath].container-name input', this.layout.form.getEditor('root').container).trigger('keyup');
        }).bind(this)));

        $('#json-code').prop('hidden', true);

        // Collapse editor and add empty item if it has no value
        _.each(this.$('[data-schemapath^="root."]:has(.json-editor-btn-collapse)'), function(E) {
          var editor = this.layout.form.getEditor($(E).data('schemapath'));
          var isEmpty = _.isEmpty(editor.getValue());

          // Empty array data should have one empty item
          if(_.contains(['resources', 'sources', 'licences'], E.dataset.schemapath.replace('root.', '')))
            $(editor.add_row_button).trigger('click');

          if(isEmpty && !editor.collapsed)
            $(editor.toggle_button).trigger('click');
        }, this);

        // Looks like previous loop is somehow async
        setTimeout((function() {
          $('#json-code').prop('hidden', false);
        }).bind(this), 300);

        // If on the previous form was entered values try to apply it to new form
        if(formData)
          this.layout.form.setValue(_.extend({}, this.layout.form.getValue(formData), formData));

        // Remove collapse button on add new item in collection
        _.each(this.$('[data-schemapath][data-schemapath!=root]:has(.json-editor-btn-add)'), function(E) {
          var
            editor = this.layout.form.getEditor($(E).data('schemapath'));

          $(_.pluck(editor.rows, 'toggle_button')).remove();

          $(editor.add_row_button).click((function() {
            $(_.last(this.rows).toggle_button).remove();
          }).bind(editor));
        }, this);
      }).bind(this));
    },

    showResult: function() {
      $('#json-code').html(highlight.fixMarkup(
        highlight.highlight('json', JSON.stringify(this.layout.form.getValue(), undefined, 2)).value
      ));

      return this;
    }
  })
};
<|endoftext|>