<commit_before>import {warn, waitConfig, createPath, doFetch, normalizeDoctype} from './utils'

export async function defineIndex (doctype, fields) {
  const config = await waitConfig()
  doctype = normalizeDoctype(config, doctype)
  if (Object.prototype.toString.call(fields) !== '[object Array]' ||
      fields.length === 0) {
    throw new Error('defineIndex fields should be a non-empty array')
  }
  if (config.isV1) return await defineIndexV1(config, doctype, fields)
  else return await defineIndexV2(config, doctype, fields)
}

export async function query (indexRef, options) {
  const config = await waitConfig()

  if (!indexRef) {
    throw new Error('query should be passed the indexRef')
  }

  if (config.isV1) return await queryV1(config, indexRef, options)
  else return await queryV2(config, indexRef, options)
}

// Internals

const VALUEOPERATORS = ['$eq', '$gt', '$gte', '$lt', '$lte']
const LOGICOPERATORS = ['$or', '$and', '$not']

/* eslint-disable */
const MAP_TEMPLATE = (function (doc) {
  if (doc.docType.toLowerCase() === 'DOCTYPEPLACEHOLDER'){
    emit(FIELDSPLACEHOLDER, doc)
  }
}).toString().replace(/ /g, '').replace(/\n/g, '')
const COUCHDB_INFINITY = {"\uFFFF": "\uFFFF"}
const COUCHDB_LOWEST = null
/* eslint-enable */

// defineIndexV1 is equivalent to defineIndex but only works for V1.
// It transforms the index fields into a map reduce view.
async function defineIndexV1 (config, doctype, fields) {
  let indexName = 'by' + fields.map(capitalize).join('')
  let indexDefinition = { map: makeMapFunction(doctype, fields), reduce: '_count' }
  let path = `/request/${doctype}/${indexName}/`
  await doFetch(config, 'PUT', path, indexDefinition)
  return { doctype: doctype, type: 'mapreduce', name: indexName, fields: fields }
}

// defineIndexV1 is equivalent to defineIndex but only works for V1.
// It transforms the index fields into a map reduce view.
async function defineIndexV2 (config, doctype, fields) {
  let path = createPath(config, doctype, '_index')
  let indexDefinition = {'index': {fields}}
  let response = await doFetch(config, 'POST', path, indexDefinition)
  return { doctype: doctype, type: 'mango', name: response.id, fields: fields }
}

// queryV1 is equivalent to query but only works for V1.
// It transforms the query into a _views call using makeMapReduceQuery
async function queryV1 (config, indexRef, options) {
  if (indexRef.type !== 'mapreduce') {
    throw new Error('query indexRef should be the return value of defineIndexV1')
  }
  if (options.fields) {
    warn('query fields will be ignored on v1')
  }

  let path = `/request/${indexRef.doctype}/${indexRef.name}/`
  let opts = makeMapReduceQuery(indexRef, options)
  let response = await doFetch(config, 'POST', path, opts)
  return response.map(r => r.value)
}

// queryV2 is equivalent to query but only works for V2
async function queryV2 (config, indexRef, options) {
  if (indexRef.type !== 'mango') {
    throw new Error('indexRef should be the return value of defineIndexV2')
  }

  let opts = {
    use_index: indexRef.name,
    fields: options.fields,
    selector: options.selector,
    limit: options.limit,
    since: options.since,
    sort: indexRef.fields // sort is useless with mango
  }

  let path = createPath(config, indexRef.doctype, '_find')
  let response = await doFetch(config, 'POST', path, opts)
  return response.docs
}

// misc
function capitalize (name) {
  return name.charAt(0).toUpperCase() + name.slice(1)
}

function makeMapFunction (doctype, fields) {
  if (Object.prototype.toString.call(fields) !== '[object Array]' ||
      fields.length === 0) {
    throw new Error('makeMapFunction fields should be a non-empty array')
  }

  fields = '[' + fields.map(name => 'doc.' + name).join(',') + ']'

  return MAP_TEMPLATE.replace('DOCTYPEPLACEHOLDER', doctype.toLowerCase())
                     .replace('FIELDSPLACEHOLDER', fields)
}

// parseSelector takes a mango selector and returns it as an array of filter
// a filter is [path, operator, value] array
// a path is an array of field names
// This function is only exported so it can be unit tested.
// Example :
// parseSelector({"test":{"deep": {"$gt": 3}}})
// [[['test', 'deep'], '$gt', 3 ]]
export function parseSelector (selector, path = [], operator = '$eq') {
  if ((typeof selector) !== 'object') {
    return [[path, operator, selector]]
  }

  let keys = Object.keys(selector)
  if (keys.length === 0) {
    throw new Error('empty selector')
  } else {
    return keys.reduce(function (acc, k) {
      if (LOGICOPERATORS.indexOf(k) !== -1) {
        throw new Error('cozy-client-js does not support mango logic ops')
      } else if (VALUEOPERATORS.indexOf(k) !== -1) {
        return acc.concat(parseSelector(selector[k], path, k))
      } else {
        return acc.concat(parseSelector(selector[k], path.concat(k), '$eq'))
      }
    }, [])
  }
}

// normalizeSelector takes a mango selector and returns it as an object
// normalized.
// This function is only exported so it can be unit tested.
// Example :
// parseSelector({"test":{"deep": {"$gt": 3}}})
// {"test.deep": {"$gt": 3}}
export function normalizeSelector (selector) {
  var filters = parseSelector(selector)
  return filters.reduce(function (acc, filter) {
    let [path, op, value] = filter
    let field = path.join('.')
    acc[field] = acc[field] || {}
    acc[field][op] = value
    return acc
  }, {})
}

// applySelector takes the normalized selector for the current field
// and append the proper values to opts.startkey, opts.endkey
function applySelector (selector, opts) {
  let value = selector['$eq']
  let lower = COUCHDB_LOWEST
  let upper = COUCHDB_INFINITY
  let inclusiveEnd

  if (value) {
    opts.startkey.push(value)
    opts.endkey.push(value)
    return false
  }

  value = selector['$gt']
  if (value) {
    throw new Error('operator $gt (strict greater than) not supported')
  }

  value = selector['$gte']
  if (value) {
    lower = value
  }

  value = selector['$lte']
  if (value) {
    upper = value
    inclusiveEnd = true
  }

  value = selector['$lt']
  if (value) {
    upper = value
    inclusiveEnd = false
  }

  opts.startkey.push(lower)
  opts.endkey.push(upper)
  if (inclusiveEnd !== undefined) opts.inclusive_end = inclusiveEnd
  return true
}

// makeMapReduceQuery takes a mango query and generate _views call parameters
// to obtain same results depending on fields in the passed indexRef.
export function makeMapReduceQuery (indexRef, query) {
  let mrquery = {
    startkey: [],
    endkey: [],
    reduce: false
  }
  let firstFreeValueField = null
  let normalizedSelector = normalizeSelector(query.selector)

  indexRef.fields.forEach(function (field) {
    let selector = normalizedSelector[field]

    if (selector && firstFreeValueField != null) {
      throw new Error('Selector on field ' + field + ', but not on ' + firstFreeValueField + ' which is higher in index fields.')
    } else if (selector) {
      selector.used = true
      let isFreeValue = applySelector(selector, mrquery)
      if (isFreeValue) firstFreeValueField = field
    } else if (firstFreeValueField == null) {
      firstFreeValueField = field
      mrquery.endkey.push(COUCHDB_INFINITY)
    }
  })

  Object.keys(normalizedSelector).forEach(function (field) {
    if (!normalizedSelector[field].used) {
      throw new Error('Cant apply selector on ' + field + ', it is not in index')
    }
  })

  return mrquery
}
<commit_msg>remove useless check & use Array.isArray<commit_after>import {warn, waitConfig, createPath, doFetch, normalizeDoctype} from './utils'

export async function defineIndex (doctype, fields) {
  const config = await waitConfig()
  doctype = normalizeDoctype(config, doctype)
  if (!Array.isArray(fields) || fields.length === 0) {
    throw new Error('defineIndex fields should be a non-empty array')
  }
  if (config.isV1) return await defineIndexV1(config, doctype, fields)
  else return await defineIndexV2(config, doctype, fields)
}

export async function query (indexRef, options) {
  const config = await waitConfig()

  if (!indexRef) {
    throw new Error('query should be passed the indexRef')
  }

  if (config.isV1) return await queryV1(config, indexRef, options)
  else return await queryV2(config, indexRef, options)
}

// Internals

const VALUEOPERATORS = ['$eq', '$gt', '$gte', '$lt', '$lte']
const LOGICOPERATORS = ['$or', '$and', '$not']

/* eslint-disable */
const MAP_TEMPLATE = (function (doc) {
  if (doc.docType.toLowerCase() === 'DOCTYPEPLACEHOLDER'){
    emit(FIELDSPLACEHOLDER, doc)
  }
}).toString().replace(/ /g, '').replace(/\n/g, '')
const COUCHDB_INFINITY = {"\uFFFF": "\uFFFF"}
const COUCHDB_LOWEST = null
/* eslint-enable */

// defineIndexV1 is equivalent to defineIndex but only works for V1.
// It transforms the index fields into a map reduce view.
async function defineIndexV1 (config, doctype, fields) {
  let indexName = 'by' + fields.map(capitalize).join('')
  let indexDefinition = { map: makeMapFunction(doctype, fields), reduce: '_count' }
  let path = `/request/${doctype}/${indexName}/`
  await doFetch(config, 'PUT', path, indexDefinition)
  return { doctype: doctype, type: 'mapreduce', name: indexName, fields: fields }
}

// defineIndexV1 is equivalent to defineIndex but only works for V1.
// It transforms the index fields into a map reduce view.
async function defineIndexV2 (config, doctype, fields) {
  let path = createPath(config, doctype, '_index')
  let indexDefinition = {'index': {fields}}
  let response = await doFetch(config, 'POST', path, indexDefinition)
  return { doctype: doctype, type: 'mango', name: response.id, fields: fields }
}

// queryV1 is equivalent to query but only works for V1.
// It transforms the query into a _views call using makeMapReduceQuery
async function queryV1 (config, indexRef, options) {
  if (indexRef.type !== 'mapreduce') {
    throw new Error('query indexRef should be the return value of defineIndexV1')
  }
  if (options.fields) {
    warn('query fields will be ignored on v1')
  }

  let path = `/request/${indexRef.doctype}/${indexRef.name}/`
  let opts = makeMapReduceQuery(indexRef, options)
  let response = await doFetch(config, 'POST', path, opts)
  return response.map(r => r.value)
}

// queryV2 is equivalent to query but only works for V2
async function queryV2 (config, indexRef, options) {
  if (indexRef.type !== 'mango') {
    throw new Error('indexRef should be the return value of defineIndexV2')
  }

  let opts = {
    use_index: indexRef.name,
    fields: options.fields,
    selector: options.selector,
    limit: options.limit,
    since: options.since,
    sort: indexRef.fields // sort is useless with mango
  }

  let path = createPath(config, indexRef.doctype, '_find')
  let response = await doFetch(config, 'POST', path, opts)
  return response.docs
}

// misc
function capitalize (name) {
  return name.charAt(0).toUpperCase() + name.slice(1)
}

function makeMapFunction (doctype, fields) {
  fields = '[' + fields.map(name => 'doc.' + name).join(',') + ']'

  return MAP_TEMPLATE.replace('DOCTYPEPLACEHOLDER', doctype.toLowerCase())
                     .replace('FIELDSPLACEHOLDER', fields)
}

// parseSelector takes a mango selector and returns it as an array of filter
// a filter is [path, operator, value] array
// a path is an array of field names
// This function is only exported so it can be unit tested.
// Example :
// parseSelector({"test":{"deep": {"$gt": 3}}})
// [[['test', 'deep'], '$gt', 3 ]]
export function parseSelector (selector, path = [], operator = '$eq') {
  if ((typeof selector) !== 'object') {
    return [[path, operator, selector]]
  }

  let keys = Object.keys(selector)
  if (keys.length === 0) {
    throw new Error('empty selector')
  } else {
    return keys.reduce(function (acc, k) {
      if (LOGICOPERATORS.indexOf(k) !== -1) {
        throw new Error('cozy-client-js does not support mango logic ops')
      } else if (VALUEOPERATORS.indexOf(k) !== -1) {
        return acc.concat(parseSelector(selector[k], path, k))
      } else {
        return acc.concat(parseSelector(selector[k], path.concat(k), '$eq'))
      }
    }, [])
  }
}

// normalizeSelector takes a mango selector and returns it as an object
// normalized.
// This function is only exported so it can be unit tested.
// Example :
// parseSelector({"test":{"deep": {"$gt": 3}}})
// {"test.deep": {"$gt": 3}}
export function normalizeSelector (selector) {
  var filters = parseSelector(selector)
  return filters.reduce(function (acc, filter) {
    let [path, op, value] = filter
    let field = path.join('.')
    acc[field] = acc[field] || {}
    acc[field][op] = value
    return acc
  }, {})
}

// applySelector takes the normalized selector for the current field
// and append the proper values to opts.startkey, opts.endkey
function applySelector (selector, opts) {
  let value = selector['$eq']
  let lower = COUCHDB_LOWEST
  let upper = COUCHDB_INFINITY
  let inclusiveEnd

  if (value) {
    opts.startkey.push(value)
    opts.endkey.push(value)
    return false
  }

  value = selector['$gt']
  if (value) {
    throw new Error('operator $gt (strict greater than) not supported')
  }

  value = selector['$gte']
  if (value) {
    lower = value
  }

  value = selector['$lte']
  if (value) {
    upper = value
    inclusiveEnd = true
  }

  value = selector['$lt']
  if (value) {
    upper = value
    inclusiveEnd = false
  }

  opts.startkey.push(lower)
  opts.endkey.push(upper)
  if (inclusiveEnd !== undefined) opts.inclusive_end = inclusiveEnd
  return true
}

// makeMapReduceQuery takes a mango query and generate _views call parameters
// to obtain same results depending on fields in the passed indexRef.
export function makeMapReduceQuery (indexRef, query) {
  let mrquery = {
    startkey: [],
    endkey: [],
    reduce: false
  }
  let firstFreeValueField = null
  let normalizedSelector = normalizeSelector(query.selector)

  indexRef.fields.forEach(function (field) {
    let selector = normalizedSelector[field]

    if (selector && firstFreeValueField != null) {
      throw new Error('Selector on field ' + field + ', but not on ' + firstFreeValueField + ' which is higher in index fields.')
    } else if (selector) {
      selector.used = true
      let isFreeValue = applySelector(selector, mrquery)
      if (isFreeValue) firstFreeValueField = field
    } else if (firstFreeValueField == null) {
      firstFreeValueField = field
      mrquery.endkey.push(COUCHDB_INFINITY)
    }
  })

  Object.keys(normalizedSelector).forEach(function (field) {
    if (!normalizedSelector[field].used) {
      throw new Error('Cant apply selector on ' + field + ', it is not in index')
    }
  })

  return mrquery
}
<|endoftext|>


<commit_before>var Ouch = require('ouch-stream')
module.exports = (db) => new Ouch(db).merge((message, doc) => {
  if (doc) {
    Object.assign(doc.files, message.doc.files)
    return doc
  } else {
    return message.doc
  }
})
<commit_msg>Added logging<commit_after>var debug = require('debug')('db_update')
var Ouch = require('ouch-stream')
module.exports = (db) => new Ouch(db).merge((message, doc) => {
  debug(`Message ${message.doc} Doc ${doc}`)
  if (doc) {
    Object.assign(doc.files, message.doc.files)
    return doc
  } else {
    return message.doc
  }
})
<|endoftext|>


<commit_before>(function() {
  // Global settings
  var el = "#admissions .d3-viz";
  var w = $('#admissions .d3-viz').width(),
      h = 300,
      xPadding = 50,
      yPadding = 30
      ;

  // Initialize SVG
  var svg = d3.select(el)
    .append("svg:svg")
    .attr("width", "100%")
    .attr("height", "100%")
    .attr("viewBox", "0 0 " + w + " " + h)
    .attr("preserveAspectRatio", "xMinYMin meet")
    ;

  // Datasets
  var datasets = $("#admissions table.table").tabulate();
  var applicants = datasets[0];
  var admissions = datasets[1];
  var enrollment = datasets[2];

  // Scales
  var minYear = d3.min(applicants, function(d) { return d.x; });
  var maxYear = d3.max(applicants, function(d) { return d.x; });
  var maxApplicants = d3.max(applicants, function(d) { return d.y; });
  var minAdmissions = d3.min(admissions, function(d) { return d.y; });
  var maxAdmissions = d3.max(admissions, function(d) { return d.y; });
  var rScale = d3.scale.linear().domain([0, maxAdmissions]).range([h - yPadding, yPadding]);
  var xScale = d3.scale.linear().domain([minYear, maxYear]).range([xPadding, w]);
  var yScale = d3.scale.linear().domain([0, maxApplicants]).range([h - yPadding, yPadding]);
  var barWidth = w / (applicants.length + 1) - 10;

  // Axes,
  var yearFormat = d3.format("4d");
  var xAxis = d3.svg.axis().scale(xScale).orient("bottom").tickSize(6, 1, 1).tickFormat(yearFormat);
  var yAxis = d3.svg.axis().scale(yScale).orient("left").ticks(5);

  svg.append("g")
    .attr("class", "x axis")
    .attr("transform", "translate(" + (barWidth / 2 + 20) + ", " + (h - yPadding) + ")")
    .call(xAxis);

  svg.append("g")
    .attr("class", "y axis")
    .attr("transform", "translate(" + xPadding + ", 0)")
    .call(yAxis);

  // Attrs
  function xN(d, i, n) {
    return xScale(d.x) + (n * 10);
  }

  function y(d) {
    return yScale(d.y) - yPadding;
  }

  function height(d) {
    return h - yScale(d.y);
  }

  function rY(d) {
    return rScale(d.y) - yPadding;
  }

  function rHeight(d) {
    return h - rScale(d.y);
  }

  var applicantsG = svg.append("g");

  // Render applicants
  applicantsG
    .selectAll("rect")
    .data(applicants)
    .enter()
    .append("rect")
    .attr("fill", "#99CCFF")
    .attr("width", barWidth)
    .attr("height", height)
    .attr("x", function(d, i) { return xN(d, i, 0); })
    .attr("y", y)
    ;

  // Render admissions
  svg.append("g")
    .selectAll("rect")
    .data(admissions)
    .enter()
    .append("rect")
    .attr("fill", "#3399CC")
    .attr("width", barWidth)
    .attr("height", height)
    .attr("x", function(d, i) { return xN(d, i, 1); })
    .attr("y", y)
    ;

  // Render enrollment
  svg.append("g")
    .selectAll("rect")
    .data(enrollment)
    .enter()
    .append("rect")
    .attr("fill", "#003366")
    .attr("width", barWidth)
    .attr("height", height)
    .attr("x", function(d, i) { return xN(d, i, 2); })
    .attr("y", y)
    ;

  // Toggle zoom
  $("#admissions a.zoom").toggle(function(e) {
    e.preventDefault();
    svg.selectAll("rect")
      .transition()
      .attr("y", rY)
      .attr("height", rHeight);
    svg.select("g.y.axis")
      .transition()
      .call(yAxis.scale(rScale));
    $(this).children("i").attr("class", "icon-minus");
  }, function(e) {
    e.preventDefault();
    svg.selectAll("rect")
      .transition()
      .attr("y", y)
      .attr("height", height);
    svg.select("g.y.axis")
      .transition()
      .call(yAxis.scale(yScale));
    $(this).children("i").attr("class", "icon-plus");
  });

  // Tooltip
  $('#admissions rect').tooltip({
    title: function(){
      return this.__data__.title;
    }
  });
})();
<commit_msg>Calculate bar width using year range<commit_after>(function() {
  // Global settings
  var el = "#admissions .d3-viz";
  var w = $('#admissions .d3-viz').width(),
      h = 300,
      xPadding = 50,
      yPadding = 30
      ;

  // Initialize SVG
  var svg = d3.select(el)
    .append("svg:svg")
    .attr("width", "100%")
    .attr("height", "100%")
    .attr("viewBox", "0 0 " + w + " " + h)
    .attr("preserveAspectRatio", "xMinYMin meet")
    ;

  // Datasets
  var datasets = $("#admissions table.table").tabulate();
  var applicants = datasets[0];
  var admissions = datasets[1];
  var enrollment = datasets[2];

  // Scales
  var minYear = d3.min(applicants, function(d) { return d.x; });
  var maxYear = d3.max(applicants, function(d) { return d.x; });
  var maxApplicants = d3.max(applicants, function(d) { return d.y; });
  var minAdmissions = d3.min(admissions, function(d) { return d.y; });
  var maxAdmissions = d3.max(admissions, function(d) { return d.y; });
  var rScale = d3.scale.linear().domain([0, maxAdmissions]).range([h - yPadding, yPadding]);
  var xScale = d3.scale.linear().domain([minYear, maxYear]).range([xPadding, w]);
  var yScale = d3.scale.linear().domain([0, maxApplicants]).range([h - yPadding, yPadding]);
  var barWidth = w / (maxYear - minYear) - 30;

  // Axes,
  var yearFormat = d3.format("4d");
  var xAxis = d3.svg.axis().scale(xScale).orient("bottom").tickSize(6, 1, 1).tickFormat(yearFormat);
  var yAxis = d3.svg.axis().scale(yScale).orient("left").ticks(5);

  svg.append("g")
    .attr("class", "x axis")
    .attr("transform", "translate(" + (barWidth / 2 + 20) + ", " + (h - yPadding) + ")")
    .call(xAxis);

  svg.append("g")
    .attr("class", "y axis")
    .attr("transform", "translate(" + xPadding + ", 0)")
    .call(yAxis);

  // Attrs
  function xN(d, i, n) {
    return xScale(d.x) + (n * 10);
  }

  function y(d) {
    return yScale(d.y) - yPadding;
  }

  function height(d) {
    return h - yScale(d.y);
  }

  function rY(d) {
    return rScale(d.y) - yPadding;
  }

  function rHeight(d) {
    return h - rScale(d.y);
  }

  var applicantsG = svg.append("g");

  // Render applicants
  applicantsG
    .selectAll("rect")
    .data(applicants)
    .enter()
    .append("rect")
    .attr("fill", "#99CCFF")
    .attr("width", barWidth)
    .attr("height", height)
    .attr("x", function(d, i) { return xN(d, i, 0); })
    .attr("y", y)
    ;

  // Render admissions
  svg.append("g")
    .selectAll("rect")
    .data(admissions)
    .enter()
    .append("rect")
    .attr("fill", "#3399CC")
    .attr("width", barWidth)
    .attr("height", height)
    .attr("x", function(d, i) { return xN(d, i, 1); })
    .attr("y", y)
    ;

  // Render enrollment
  svg.append("g")
    .selectAll("rect")
    .data(enrollment)
    .enter()
    .append("rect")
    .attr("fill", "#003366")
    .attr("width", barWidth)
    .attr("height", height)
    .attr("x", function(d, i) { return xN(d, i, 2); })
    .attr("y", y)
    ;

  // Toggle zoom
  $("#admissions a.zoom").toggle(function(e) {
    e.preventDefault();
    svg.selectAll("rect")
      .transition()
      .attr("y", rY)
      .attr("height", rHeight);
    svg.select("g.y.axis")
      .transition()
      .call(yAxis.scale(rScale));
    $(this).children("i").attr("class", "icon-minus");
  }, function(e) {
    e.preventDefault();
    svg.selectAll("rect")
      .transition()
      .attr("y", y)
      .attr("height", height);
    svg.select("g.y.axis")
      .transition()
      .call(yAxis.scale(yScale));
    $(this).children("i").attr("class", "icon-plus");
  });

  // Tooltip
  $('#admissions rect').tooltip({
    title: function(){
      return this.__data__.title;
    }
  });
})();
<|endoftext|>


<commit_before>const request = require("request")
const resolve = require("url").resolve

const config = {
  specs: [
    "./e2e-tests/**/*.tests.js"
  ],
  exclude: [],
  maxInstances: 10,
  sync: true,
  logLevel: "result",
  coloredLogs: true,
  screenshotPath: "./e2e-tests/error-screenshots",
  baseUrl: "http://the8-dev.azurewebsites.net",
  waitforTimeout: 10000,
  connectionRetryTimeout: 90000,
  connectionRetryCount: 3,
  framework: "jasmine",
  jasmineNodeOpts: {
    defaultTimeoutInterval: 10000
  },
  reporters: ["spec"],
  reporterOptions: {
    outputDir: "./e2e-tests/test-results"
  },
  services: ["phantomjs"],
  capabilities: [
    {
      maxInstances: 5,
      browserName: "chrome"
    }
  ],
  afterTest: test => console.log(test)
}

if (process.env.APPVEYOR) {
  console.log("Detected AppVeyor")
  const url = resolve(process.env.APPVEYOR_API_URL, "/api/tests")
  console.log(`Posting test results to ${url}`)

  config.afterTest = test => {
    const body = {
      "testName": test.fullName,
      "testFramework": "WebdriverIO",
      "fileName": test.file,
      "outcome": test.passed ? "Passed" : "Failed",
      "durationMilliseconds": test.duration
    }

    console.log("Sending test results to AppVeyor")
    console.log(body)

    request.post({
      url,
      json: true,
      body
    }, () => {})
  }
}

exports.config = config<commit_msg>Reduce logging<commit_after>const request = require("request")
const resolve = require("url").resolve

const config = {
  specs: [
    "./e2e-tests/**/*.tests.js"
  ],
  exclude: [],
  maxInstances: 10,
  sync: true,
  logLevel: "result",
  coloredLogs: true,
  screenshotPath: "./e2e-tests/error-screenshots",
  baseUrl: "http://the8-dev.azurewebsites.net",
  waitforTimeout: 10000,
  connectionRetryTimeout: 90000,
  connectionRetryCount: 3,
  framework: "jasmine",
  jasmineNodeOpts: {
    defaultTimeoutInterval: 10000
  },
  reporters: ["spec"],
  reporterOptions: {
    outputDir: "./e2e-tests/test-results"
  },
  services: ["phantomjs"],
  capabilities: [
    {
      maxInstances: 5,
      browserName: "chrome"
    }
  ],
  afterTest: test => console.log(test)
}

if (process.env.APPVEYOR) {
  const url = resolve(process.env.APPVEYOR_API_URL, "/api/tests")

  config.afterTest = test => {
    const body = {
      "testName": test.fullName,
      "testFramework": "WebdriverIO",
      "fileName": test.file,
      "outcome": test.passed ? "Passed" : "Failed",
      "durationMilliseconds": test.duration
    }

    request.post({
      url,
      body,
      json: true
    }, () => {})
  }
}

exports.config = config<|endoftext|>


<commit_before>Kwf.onContentReady(function()
{
    var fadeComponents = Ext.query('div.kwfFadeElements');
    Ext.each(fadeComponents, function(c) {
        var extWrapperEl = Ext.get(c);
        if (extWrapperEl.fadeElementsObject) return; // nur einmal initialisieren

        var fadeClass = Ext.query('.fadeClass', c)[0].value;
        var selector = Ext.query('.fadeSelector', c)[0].value;
        var config = Ext.query('.fadeConfig', c); // optional
        if (config && config[0]) {
            config = Ext.decode(config[0].value);
        } else {
            config = { };
        }
        var textSelector = Ext.query('.textSelector', c); // optional
        if (textSelector && textSelector[0]) {
            config.textSelector = textSelector[0].value;
        }

        config.selector = selector;
        config.selectorRoot = c;

        var cls = Kwf.Fade.Elements;
        if (fadeClass) {
            cls = eval(fadeClass);
            delete fadeClass;
        }

        extWrapperEl.fadeElementsObject = new cls(config);
        if (config.autoStart == undefined || config.autoStart) {
            extWrapperEl.fadeElementsObject.start();
        }
    });
});

Ext.namespace("Kwf.Fade");

Kwf.Fade.Elements = function(cfg) {
    this.selector = cfg.selector;

    this.animationType = 'fade';
    this.elementAccessDirect = false; // optional: displays direct acces links to each image
    this.elementAccessPlayPause = false; // optional: displayes play / pause button
    this.elementAccessLinks = false; // optional, deprecated: displays both of above
    this.elementAccessNextPrevious = false;
    this.selectorRoot = document;
    this.fadeDuration = 1.5;
    this.easingFadeOut = 'easeIn'; //TODO change names to fit every animation
    this.easingFadeIn = 'easeIn';
    this.fadeEvery = 7;
    this.startRandom = true;

    if (typeof cfg.animationType != 'undefined') this.animationType = cfg.animationType;
    if (typeof cfg.elementAccessPlayPause != 'undefined') this.elementAccessPlayPause = cfg.elementAccessPlayPause;
    if (typeof cfg.elementAccessDirect != 'undefined') this.elementAccessDirect = cfg.elementAccessDirect;
    if (typeof cfg.elementAccessLinks != 'undefined' && cfg.elementAccessLinks) {
        this.elementAccessPlayPause = cfg.elementAccessLinks;
        this.elementAccessDirect = cfg.elementAccessLinks;
    }
    if (typeof cfg.elementAccessNextPrevious != 'undefined') this.elementAccessNextPrevious = cfg.elementAccessNextPrevious;
    if (typeof cfg.selectorRoot != 'undefined') this.selectorRoot = cfg.selectorRoot;
    if (typeof cfg.fadeDuration != 'undefined') this.fadeDuration = cfg.fadeDuration;
    if (typeof cfg.easingFadeOut != 'undefined') this.easingFadeOut = cfg.easingFadeOut;
    if (typeof cfg.easingFadeIn != 'undefined') this.easingFadeIn = cfg.easingFadeIn;
    if (typeof cfg.fadeEvery != 'undefined') this.fadeEvery = cfg.fadeEvery;
    if (typeof cfg.startRandom != 'undefined') this.startRandom = cfg.startRandom;

    this._elementAccessLinkEls = [];

    this.fadeElements = Ext.query(this.selector, this.selectorRoot);

    if (this.startRandom) {
        this.active = Math.floor(Math.random() * this.fadeElements.length);
        if (this.active >= this.fadeElements.length) {
            this.active = this.fadeElements.length - 1;
        }

        this.next = this.active + 1;
        if (this.next >= this.fadeElements.length) {
            this.next = 0;
        }
    }

    var i = 0;
    Ext.each(this.fadeElements, function(e) {
        var ee = Ext.get(e);

        ee.addClass('kwfFadeElement');
        if (i != this.active) {
            ee.setStyle('display', 'none');
        } else {
            ee.setStyle('display', 'block');
        }
        i += 1;
    }, this);

    // wenn nur ein fade element existiert und im text element kein inhalt ist: ausblenden
    if (this.fadeElements.length == 1 && cfg.textSelector) {
        var dontShowEl = Ext.query(cfg.textSelector, this.selectorRoot);
        if (dontShowEl && dontShowEl[0]) {
            if (dontShowEl[0].innerHTML.replace(/\s/g, '') == '') {
                Ext.get(dontShowEl[0]).setDisplayed('none');
            }
        }
    }

    // create the element access link if needed
    if ((this.elementAccessDirect || this.elementAccessPlayPause || this.elementAccessNextPrevious) && i >= 1) {
        this._createElementAccessLinks(this.active);
    }
};

Kwf.Fade.Elements.prototype = {

    active: 0,
    next: 1,
    _firstFaded: false,
    _timeoutId: null,
    _playPause: 'play',
    _playPauseButton: null,

    start: function() {
        if (this.fadeElements.length <= 1) return;
        this._timeoutId = this.doFade.defer(this._getDeferTime(), this);
    },

    doFade: function() {
        if (this.fadeElements.length <= 1) return;

        var activeEl = Ext.get(this.fadeElements[this.active]);
        if (!activeEl.isVisible(true)) {
            this._timeoutId = this.doFade.defer(this._getDeferTime(), this);
            return;
        }
        var nextEl = Ext.get(this.fadeElements[this.next]);
        if(this.animationType == 'slide') { //TODO implement different animation-types
            // order of slideIn and slideOut is important because else there is one
            // pixel margin between the leaving and comming element
            nextEl.slideIn('r', { endOpacity: 1.0, easing: this.easingFadeIn, duration: this.fadeDuration, useDisplay: true,
                callback: function () {
                    Kwf.fireComponentEvent('componentSlideIn', nextEl.parent(), nextEl);
                }
            });
            activeEl.slideOut('l', { endOpacity: .0, easing: this.easingFadeOut, duration: this.fadeDuration, useDisplay: true,
                callback: function() {
                    Kwf.fireComponentEvent('componentSlideOut', activeEl.parent(), activeEl);
                }
            });
        } else {
            activeEl.fadeOut({ endOpacity: .0, easing: this.easingFadeOut, duration: this.fadeDuration, useDisplay: true,
                callback: function() {
                    Kwf.fireComponentEvent('componentFadeOut', activeEl.parent(), activeEl);
                }
            });
            nextEl.fadeIn({ endOpacity: 1.0, easing: this.easingFadeIn, duration: this.fadeDuration, useDisplay: true,
                callback: function () {
                    Kwf.fireComponentEvent('componentFadeIn', nextEl.parent(), nextEl);
                }
            });
        }

        if (this.elementAccessDirect) {
            if (this._elementAccessLinkEls[this.active].hasClass('elementAccessLinkActive')) {
                this._elementAccessLinkEls[this.active].removeClass('elementAccessLinkActive');
            }
            this._elementAccessLinkEls[this.next].addClass('elementAccessLinkActive');
        }

        this.active = this.next;
        this.next += 1;
        if (typeof this.fadeElements[this.next] == 'undefined') {
            this.next = 0;
        }

        this._timeoutId = this.doFade.defer(this._getDeferTime(), this);
    },

    pause: function() {
        if (this._timeoutId) window.clearTimeout(this._timeoutId);
        if (this._playPauseButton) {
            this._playPauseButton.removeClass('elementAccessPause');
            this._playPauseButton.addClass('elementAccessPlay');
        }
        this._playPause = 'pause';
    },

    play: function() {
        this.doFade();
        if (this._playPauseButton) {
            this._playPauseButton.removeClass('elementAccessPlay');
            this._playPauseButton.addClass('elementAccessPause');
        }
        this._playPause = 'play';
    },

    _getDeferTime: function() {
        if (!this._firstFaded) {
            this._firstFaded = true;
            return Math.ceil(this.fadeEvery * 1000) - Math.ceil(this.fadeDuration * 1000);
        } else {
            return Math.ceil(this.fadeEvery * 1000);
        }
    },

    _createElementAccessLinks: function(activeLinkIndex) {
        // accessLinks and play / pause button if there are at least 2 images
        if (this.fadeElements.length >= 2) {
            if (this.elementAccessDirect || this.elementAccessPlayPause) {
                var ul = Ext.get(this.selectorRoot).createChild({ tag: 'ul', cls: 'elementAccessLinks' });
            }

            if (this.elementAccessDirect) {
                var j = 0;
                Ext.each(this.fadeElements, function(e) {
                    var a = ul.createChild({ tag: 'li' })
                        .createChild({
                            tag: 'a',
                            cls: 'elementAccessLink'+(activeLinkIndex==j ? ' elementAccessLinkActive' : ''),
                            html: '',
                            href: '#'
                        });
                    a.on('click', function(ev, el, opt) {
                        ev.stopEvent();

                        if (this._timeoutId) {
                            window.clearTimeout(this._timeoutId);
                        }
                        this.next = opt.activateIdx;
                        this.doFade();
                        if (this.elementAccessPlayPause) this.pause();

                    }, this, { activateIdx: j });
                    this._elementAccessLinkEls.push(a);
                    j += 1;
                }, this);
            }

            if (this.elementAccessPlayPause) {
                this._playPauseButton = ul.createChild({ tag: 'li' })
                    .createChild({
                        tag: 'a',
                        cls: 'elementAccessPlayPauseButton elementAccessPause',
                        html: ' ',
                        href: '#'
                    });
                this._playPauseButton.on('click', function(ev, el, opt) {
                    ev.stopEvent();

                    if (this._playPause == 'play') {
                        this.pause();
                    } else if (this._playPause == 'pause') {
                        this.play();
                    }
                }, this);
            }

            if (this.elementAccessNextPrevious) {
                var prevButton = Ext.get(this.selectorRoot).createChild({
                    tag: 'a', cls: 'elementAccessPrevious', html: '', href: '#'
                });
                prevButton.on('click', function(ev, el, opt) {
                    ev.stopEvent();

                    if (this._timeoutId) window.clearTimeout(this._timeoutId);

                    var nextIdx = this.active - 1;
                    if (nextIdx < 0) nextIdx = this.fadeElements.length-1;

                    this.next = nextIdx;
                    this.doFade();
                    if (this.elementAccessPlayPause) this.pause();
                }, this);

                var nextButton = Ext.get(this.selectorRoot).createChild({
                    tag: 'a', cls: 'elementAccessNext', html: '', href: '#'
                });
                nextButton.on('click', function(ev, el, opt) {
                    ev.stopEvent();

                    if (this._timeoutId) window.clearTimeout(this._timeoutId);

                    var nextIdx = this.active + 1;
                    if (nextIdx >= this.fadeElements.length) nextIdx = 0;

                    this.next = nextIdx;
                    this.doFade();
                    if (this.elementAccessPlayPause) this.pause();
                }, this);
            }
        }
    }
};
<commit_msg>_createElementAccessLinks in EyeCandyFadeElements uses now an Ext.XTemplate now it's easier to edit the template of the accessLinks<commit_after>Kwf.onContentReady(function()
{
    var fadeComponents = Ext.query('div.kwfFadeElements');
    Ext.each(fadeComponents, function(c) {
        var extWrapperEl = Ext.get(c);
        if (extWrapperEl.fadeElementsObject) return; // nur einmal initialisieren

        var fadeClass = Ext.query('.fadeClass', c)[0].value;
        var selector = Ext.query('.fadeSelector', c)[0].value;
        var config = Ext.query('.fadeConfig', c); // optional
        if (config && config[0]) {
            config = Ext.decode(config[0].value);
        } else {
            config = { };
        }
        var textSelector = Ext.query('.textSelector', c); // optional
        if (textSelector && textSelector[0]) {
            config.textSelector = textSelector[0].value;
        }

        config.selector = selector;
        config.selectorRoot = c;

        var cls = Kwf.Fade.Elements;
        if (fadeClass) {
            cls = eval(fadeClass);
            delete fadeClass;
        }

        extWrapperEl.fadeElementsObject = new cls(config);
        if (config.autoStart == undefined || config.autoStart) {
            extWrapperEl.fadeElementsObject.start();
        }
    });
});

Ext.namespace("Kwf.Fade");

Kwf.Fade.Elements = function(cfg) {
    this.selector = cfg.selector;

    this.animationType = 'fade';
    this.elementAccessDirect = false; // optional: displays direct acces links to each image
    this.elementAccessPlayPause = false; // optional: displayes play / pause button
    this.elementAccessLinks = false; // optional, deprecated: displays both of above
    this.elementAccessNextPrevious = false;
    this.selectorRoot = document;
    this.fadeDuration = 1.5;
    this.easingFadeOut = 'easeIn'; //TODO change names to fit every animation
    this.easingFadeIn = 'easeIn';
    this.fadeEvery = 7;
    this.startRandom = true;

    if (typeof cfg.animationType != 'undefined') this.animationType = cfg.animationType;
    if (typeof cfg.elementAccessPlayPause != 'undefined') this.elementAccessPlayPause = cfg.elementAccessPlayPause;
    if (typeof cfg.elementAccessDirect != 'undefined') this.elementAccessDirect = cfg.elementAccessDirect;
    if (typeof cfg.elementAccessLinks != 'undefined' && cfg.elementAccessLinks) {
        this.elementAccessPlayPause = cfg.elementAccessLinks;
        this.elementAccessDirect = cfg.elementAccessLinks;
    }
    if (typeof cfg.elementAccessNextPrevious != 'undefined') this.elementAccessNextPrevious = cfg.elementAccessNextPrevious;
    if (typeof cfg.selectorRoot != 'undefined') this.selectorRoot = cfg.selectorRoot;
    if (typeof cfg.fadeDuration != 'undefined') this.fadeDuration = cfg.fadeDuration;
    if (typeof cfg.easingFadeOut != 'undefined') this.easingFadeOut = cfg.easingFadeOut;
    if (typeof cfg.easingFadeIn != 'undefined') this.easingFadeIn = cfg.easingFadeIn;
    if (typeof cfg.fadeEvery != 'undefined') this.fadeEvery = cfg.fadeEvery;
    if (typeof cfg.startRandom != 'undefined') this.startRandom = cfg.startRandom;

    this._elementAccessLinkEls = [];

    this.fadeElements = Ext.query(this.selector, this.selectorRoot);

    if (this.startRandom) {
        this.active = Math.floor(Math.random() * this.fadeElements.length);
        if (this.active >= this.fadeElements.length) {
            this.active = this.fadeElements.length - 1;
        }

        this.next = this.active + 1;
        if (this.next >= this.fadeElements.length) {
            this.next = 0;
        }
    }

    var i = 0;
    Ext.each(this.fadeElements, function(e) {
        var ee = Ext.get(e);

        ee.addClass('kwfFadeElement');
        if (i != this.active) {
            ee.setStyle('display', 'none');
        } else {
            ee.setStyle('display', 'block');
        }
        i += 1;
    }, this);

    // wenn nur ein fade element existiert und im text element kein inhalt ist: ausblenden
    if (this.fadeElements.length == 1 && cfg.textSelector) {
        var dontShowEl = Ext.query(cfg.textSelector, this.selectorRoot);
        if (dontShowEl && dontShowEl[0]) {
            if (dontShowEl[0].innerHTML.replace(/\s/g, '') == '') {
                Ext.get(dontShowEl[0]).setDisplayed('none');
            }
        }
    }

    // create the element access link if needed
    if ((this.elementAccessDirect || this.elementAccessPlayPause || this.elementAccessNextPrevious) && i >= 1) {
        this._createElementAccessLinks(this.active);
    }
};

Kwf.Fade.Elements.prototype = {

    active: 0,
    next: 1,
    _firstFaded: false,
    _timeoutId: null,
    _playPause: 'play',
    _playPauseButton: null,
    _template: null,

    start: function() {
        if (this.fadeElements.length <= 1) return;
        this._timeoutId = this.doFade.defer(this._getDeferTime(), this);
    },

    doFade: function() {
        if (this.fadeElements.length <= 1) return;

        var activeEl = Ext.get(this.fadeElements[this.active]);
        if (!activeEl.isVisible(true)) {
            this._timeoutId = this.doFade.defer(this._getDeferTime(), this);
            return;
        }
        var nextEl = Ext.get(this.fadeElements[this.next]);
        if(this.animationType == 'slide') { //TODO implement different animation-types
            // order of slideIn and slideOut is important because else there is one
            // pixel margin between the leaving and comming element
            nextEl.slideIn('r', { endOpacity: 1.0, easing: this.easingFadeIn, duration: this.fadeDuration, useDisplay: true,
                callback: function () {
                    Kwf.fireComponentEvent('componentSlideIn', nextEl.parent(), nextEl);
                }
            });
            activeEl.slideOut('l', { endOpacity: .0, easing: this.easingFadeOut, duration: this.fadeDuration, useDisplay: true,
                callback: function() {
                    Kwf.fireComponentEvent('componentSlideOut', activeEl.parent(), activeEl);
                }
            });
        } else {
            activeEl.fadeOut({ endOpacity: .0, easing: this.easingFadeOut, duration: this.fadeDuration, useDisplay: true,
                callback: function() {
                    Kwf.fireComponentEvent('componentFadeOut', activeEl.parent(), activeEl);
                }
            });
            nextEl.fadeIn({ endOpacity: 1.0, easing: this.easingFadeIn, duration: this.fadeDuration, useDisplay: true,
                callback: function () {
                    Kwf.fireComponentEvent('componentFadeIn', nextEl.parent(), nextEl);
                }
            });
        }

        if (this.elementAccessDirect) {
            if (Ext.get(this._elementAccessLinkEls[this.active]).hasClass('elementAccessLinkActive')) {
                Ext.get(this._elementAccessLinkEls[this.active]).removeClass('elementAccessLinkActive');
            }
            Ext.get(this._elementAccessLinkEls[this.next]).addClass('elementAccessLinkActive');
        }

        this.active = this.next;
        this.next += 1;
        if (typeof this.fadeElements[this.next] == 'undefined') {
            this.next = 0;
        }

        this._timeoutId = this.doFade.defer(this._getDeferTime(), this);
    },

    pause: function() {
        if (this._timeoutId) window.clearTimeout(this._timeoutId);
        if (this._playPauseButton) {
            this._playPauseButton.removeClass('elementAccessPause');
            this._playPauseButton.addClass('elementAccessPlay');
        }
        this._playPause = 'pause';
    },

    play: function() {
        this.doFade();
        if (this._playPauseButton) {
            this._playPauseButton.removeClass('elementAccessPlay');
            this._playPauseButton.addClass('elementAccessPause');
        }
        this._playPause = 'play';
    },

    _getDeferTime: function() {
        if (!this._firstFaded) {
            this._firstFaded = true;
            return Math.ceil(this.fadeEvery * 1000) - Math.ceil(this.fadeDuration * 1000);
        } else {
            return Math.ceil(this.fadeEvery * 1000);
        }
    },

    _createElementAccessLinks: function(activeLinkIndex) {
        // accessLinks and play / pause button if there are at least 2 images
        if (this.fadeElements.length >= 2) {
            var template = '';
            if (this.elementAccessDirect || this.elementAccessPlayPause) {
                template += '<ul class="elementAccessLinks">\n';
            }

            if (this.elementAccessDirect) {
                Ext.each(this.fadeElements, function(e) {
                    template += '<li>';
                        template += '<a class="elementAccessLink" href="#"></a>';
                    template += '</li>\n';
                }, this);
            }

            if (this.elementAccessPlayPause) {
                template += '<li>';
                    template += '<a class="elementAccessPlayPauseButton elementAccessPause" href="#"> </a>';
                template += '</li>\n';
            }

            if (this.elementAccessDirect || this.elementAccessPlayPause) {
                template += '</ul>\n';
            }

            if (this.elementAccessNextPrevious) {
                template += '<a class="elementAccessPrevious" href="#"></a>\n';

                template += '<a class="elementAccessNext" href="#"></a>\n';
            }

            this.template = new Ext.XTemplate(template);
            this.template.append(this.selectorRoot);

            var elementAccessLinks = Ext.get(this.selectorRoot).select('a.elementAccessLink', true);
            if (elementAccessLinks) {
                this._elementAccessLinkEls = Ext.get(this.selectorRoot).query('a.elementAccessLink');
                var j = 0;
                elementAccessLinks.each(function(link) {
                    if (activeLinkIndex==j) link.addClass('elementAccessLinkActive');
                    link.on('click', function(ev, el, opt) {
                        ev.stopEvent();

                        if (this._timeoutId) {
                            window.clearTimeout(this._timeoutId);
                        }
                        this.next = opt.activateIdx;
                        this.doFade();
                        if (this.elementAccessPlayPause) this.pause();

                    }, this, { activateIdx: j });
                    j += 1;
                }, this);
            }

            var playPauseButton = Ext.get(this.selectorRoot).child('a.elementAccessPlayPauseButton');
            if (playPauseButton) {
                playPauseButton.on('click', function(ev, el, opt) {
                    ev.stopEvent();

                    if (this._playPause == 'play') {
                        this.pause();
                    } else if (this._playPause == 'pause') {
                        this.play();
                    }
                }, this);
            }

            var prevButton = Ext.get(this.selectorRoot).child('a.elementAccessPrevious');
            if (prevButton) {
                prevButton.on('click', function(ev, el, opt) {
                    ev.stopEvent();

                    if (this._timeoutId) window.clearTimeout(this._timeoutId);

                    var nextIdx = this.active - 1;
                    if (nextIdx < 0) nextIdx = this.fadeElements.length-1;

                    this.next = nextIdx;
                    this.doFade();
                    if (this.elementAccessPlayPause) this.pause();
                }, this);
            }

            var nextButton = Ext.get(this.selectorRoot).child('a.elementAccessNext');
            if (nextButton) {
                nextButton.on('click', function(ev, el, opt) {
                    ev.stopEvent();

                    if (this._timeoutId) window.clearTimeout(this._timeoutId);

                    var nextIdx = this.active + 1;
                    if (nextIdx >= this.fadeElements.length) nextIdx = 0;

                    this.next = nextIdx;
                    this.doFade();
                    if (this.elementAccessPlayPause) this.pause();
                }, this);
            }
        }
    }
};
<|endoftext|>


<commit_before>define([
    'underscore',
    'knockout',
    'models/abstract',
    'arches'
], function(_, ko, AbstractModel, arches) {
    return AbstractModel.extend({
        url: arches.urls.project,

        initialize: function(options) {
            var self = this;
            self.identities = options.identities || [];
            self._project = ko.observable('{}');
            self.name = ko.observable('');
            self.description = ko.observable('');
            self.startdate = ko.observable(null);
            self.enddate = ko.observable(null);
            self.active = ko.observable(false);
            self.createdby = ko.observable(null);
            self.lasteditedby = ko.observable(null);
            self.users = ko.observableArray([]);
            self.groups = ko.observableArray([]);
            self.showDetails = ko.observable(false);
            self.cards = ko.observableArray([]);
            self.collectedResources = ko.observable(false);

            var getUserName = function(id) {
                var user = _.find(self.identities, function(i) {
                    return i.type === 'user' && i.id === id;
                });
                return user ? user.name : '';
            };

            self.setIdentityApproval = function() {
                var groups = ko.unwrap(this.groups)
                var users = ko.unwrap(this.users)
                _.each(this.identities, function(item) {
                    item.approved(false);
                    if ((item.type === 'group' && _.contains(groups, item.id)) ||
                        (item.type === 'user' && _.contains(users, item.id))) {
                        item.approved(true);
                    }
                })
            };

            _.each(this.identities, function(item) {
                if (item.type === 'group') {
                    _.each(item.users, function(user){
                        if (!user.expanded) {
                            user.expanded = ko.observable(false);
                        }
                    })
                }
            })

            self.createdbyName = ko.computed(function() {
                return getUserName(self.createdby());
            });

            self.lasteditedbyName = ko.computed(function() {
                return getUserName(self.lasteditedby());
            });

            self.selectedIdentity = ko.computed(function() {
                var selected = _.filter(self.identities, function(identity) {
                    return ko.unwrap(identity.selected);
                });
                return selected.length > 0 ? selected[0] : undefined;
            });

            self.approvedUserNames = ko.computed(function() {
                names = [];
                _.each(self.identities, function(identity) {
                    if (identity.type === 'user' && identity.approved()) {
                        names.push(identity.name);
                    }
                }, this);
                return names;
            })

            self.approvedGroupNames = ko.computed(function() {
                names = [];
                _.each(self.identities, function(identity) {
                    if (identity.type === 'group' && identity.approved()) {
                        names.push(identity.name);
                    }
                }, this);
                return names;
            })

            self.userFilter = ko.observable('');

            self.filteredUsers = ko.computed(function() {
                var filter = self.userFilter();
                var selected = _.filter(self.identities, function(identity) {
                    return ko.unwrap(identity.selected);
                });
                var list = []
                if (selected.length === 1) {
                    list = selected[0].users;
                    if (filter.length === 0) {
                        return list;
                    }
                }
                return _.filter(list, function(user) {
                    if (user.username.startsWith(filter)) {
                        return user
                    }
                });
            });

            self.hasIdentity = function() {
                var approved = false;
                var identity = self.selectedIdentity();
                if (identity) {
                    approved = identity.approved()
                }
                return approved
            };

            self.toggleIdentity = function() {
                var identity = self.selectedIdentity();
                if (identity) {
                    var currentIdentities = identity.type === 'user' ? self.users : self.groups;
                    if (self.hasIdentity()) {
                        currentIdentities.remove(identity.id);
                        if (identity.type === 'user') {
                            identity.approved(false);
                        } else {
                            identity.approved(false);
                            _.chain(self.identities).filter(function(id) {
                                return id.type === 'user'
                            }).each(function(user) {
                                if (_.intersection(user.group_ids, self.groups()).length === 0) { // user does not belong to any accepted groups
                                    user.approved(false);
                                    self.users.remove(user.id);
                                }
                            })
                        };
                    } else {
                        if (!_.contains(currentIdentities(), identity.id)) {
                            currentIdentities.push(identity.id);
                        }
                        identity.approved(true);
                        _.chain(self.identities).filter(function(id) {
                            return id.type === 'user'
                        }).each(function(user) {
                            if (_.intersection(user.group_ids, self.groups()).length > 0) {
                                user.approved(true);
                                if (!_.contains(self.users(), user.id)) {
                                    self.users.push(user.id);
                                }
                            }
                        })
                    };
                };
            };

            self.updateCards = function(val) {
                var approvedCards = _.chain(val.targetParent())
                .filter(function(card){
                    if (card.approved()) {
                        return card.cardid
                    }
                })
                .pluck('cardid').value()
                if (self.cards().length > approvedCards.length) {
                    self.cards(approvedCards)
                } else {
                    var diff = _.difference(self.cards(), approvedCards)
                    self.cards(_.union(diff, approvedCards))
                }
            }

            self.updateApproved = function(val){
                val.item.approved(true);
                self.updateCards(val)
            };;

            self.updateUnapproved = function(val){
                val.item.approved(false);
                self.updateCards(val)
            };

            self.toggleShowDetails = function() {
                self.setIdentityApproval();
                self.showDetails(!self.showDetails())
            }

            self.parse(options.source);
            self.setIdentityApproval();

            self.json = ko.computed(function() {
                var jsObj = ko.toJS({
                    name: self.name,
                    description: self.description,
                    startdate: self.startdate,
                    enddate: self.enddate,
                    active: self.active,
                    id: self.get('id'),
                    groups: self.groups,
                    users: self.users,
                    cards: self.cards
                });
                return JSON.stringify(_.extend(JSON.parse(self._project()), jsObj))
            });

            self.dirty = ko.computed(function() {
                return self.json() !== self._project() || !self.get('id');
            });
        },

        parse: function(source) {
            var self = this;
            self._project(JSON.stringify(source));
            self.name(source.name);
            self.description(source.description);
            self.startdate(source.startdate);
            self.enddate(source.enddate);
            self.active(source.active);
            self.createdby(source.createdby_id);
            self.lasteditedby(source.lasteditedby_id);
            self.groups(source.groups);
            self.users(source.users);
            self.cards(source.cards);
            self.set('id', source.id);
        },

        reset: function() {
            this.parse(JSON.parse(this._project()), self);
        },

        _getURL: function(method) {
            return this.url;
        },

        save: function(userCallback, scope) {
            var self = this;
            var method = "POST";
            var callback = function(request, status, model) {
                if (typeof userCallback === 'function') {
                    userCallback.call(this, request, status, model);
                }
                if (status === 'success') {
                    self.set('id', request.responseJSON.project.id);
                    self.createdby(request.responseJSON.project.createdby_id);
                    self.lasteditedby(request.responseJSON.project.lasteditedby_id);
                    self.groups(request.responseJSON.project.groups);
                    self.users(request.responseJSON.project.users);
                    self.cards(request.responseJSON.project.cards);
                    this._project(this.json());
                };
            };
            this._doRequest({
                type: method,
                url: this._getURL(method),
                data: JSON.stringify(this.toJSON())
            }, callback, scope, 'save');
        },

        toJSON: function() {
            return JSON.parse(this.json());
        },

        update: function() {
            this.setIdentityApproval();
        }
    });
});
<commit_msg>Fixes issue when adding cards from different resources (issue caused by previous method of handling card removal. (commit 879c3c7) re #2580<commit_after>define([
    'underscore',
    'knockout',
    'models/abstract',
    'arches'
], function(_, ko, AbstractModel, arches) {
    return AbstractModel.extend({
        url: arches.urls.project,

        initialize: function(options) {
            var self = this;
            self.identities = options.identities || [];
            self._project = ko.observable('{}');
            self.name = ko.observable('');
            self.description = ko.observable('');
            self.startdate = ko.observable(null);
            self.enddate = ko.observable(null);
            self.active = ko.observable(false);
            self.createdby = ko.observable(null);
            self.lasteditedby = ko.observable(null);
            self.users = ko.observableArray([]);
            self.groups = ko.observableArray([]);
            self.showDetails = ko.observable(false);
            self.cards = ko.observableArray([]);
            self.collectedResources = ko.observable(false);

            var getUserName = function(id) {
                var user = _.find(self.identities, function(i) {
                    return i.type === 'user' && i.id === id;
                });
                return user ? user.name : '';
            };

            self.setIdentityApproval = function() {
                var groups = ko.unwrap(this.groups)
                var users = ko.unwrap(this.users)
                _.each(this.identities, function(item) {
                    item.approved(false);
                    if ((item.type === 'group' && _.contains(groups, item.id)) ||
                        (item.type === 'user' && _.contains(users, item.id))) {
                        item.approved(true);
                    }
                })
            };

            _.each(this.identities, function(item) {
                if (item.type === 'group') {
                    _.each(item.users, function(user){
                        if (!user.expanded) {
                            user.expanded = ko.observable(false);
                        }
                    })
                }
            })

            self.createdbyName = ko.computed(function() {
                return getUserName(self.createdby());
            });

            self.lasteditedbyName = ko.computed(function() {
                return getUserName(self.lasteditedby());
            });

            self.selectedIdentity = ko.computed(function() {
                var selected = _.filter(self.identities, function(identity) {
                    return ko.unwrap(identity.selected);
                });
                return selected.length > 0 ? selected[0] : undefined;
            });

            self.approvedUserNames = ko.computed(function() {
                names = [];
                _.each(self.identities, function(identity) {
                    if (identity.type === 'user' && identity.approved()) {
                        names.push(identity.name);
                    }
                }, this);
                return names;
            })

            self.approvedGroupNames = ko.computed(function() {
                names = [];
                _.each(self.identities, function(identity) {
                    if (identity.type === 'group' && identity.approved()) {
                        names.push(identity.name);
                    }
                }, this);
                return names;
            })

            self.userFilter = ko.observable('');

            self.filteredUsers = ko.computed(function() {
                var filter = self.userFilter();
                var selected = _.filter(self.identities, function(identity) {
                    return ko.unwrap(identity.selected);
                });
                var list = []
                if (selected.length === 1) {
                    list = selected[0].users;
                    if (filter.length === 0) {
                        return list;
                    }
                }
                return _.filter(list, function(user) {
                    if (user.username.startsWith(filter)) {
                        return user
                    }
                });
            });

            self.hasIdentity = function() {
                var approved = false;
                var identity = self.selectedIdentity();
                if (identity) {
                    approved = identity.approved()
                }
                return approved
            };

            self.toggleIdentity = function() {
                var identity = self.selectedIdentity();
                if (identity) {
                    var currentIdentities = identity.type === 'user' ? self.users : self.groups;
                    if (self.hasIdentity()) {
                        currentIdentities.remove(identity.id);
                        if (identity.type === 'user') {
                            identity.approved(false);
                        } else {
                            identity.approved(false);
                            _.chain(self.identities).filter(function(id) {
                                return id.type === 'user'
                            }).each(function(user) {
                                if (_.intersection(user.group_ids, self.groups()).length === 0) { // user does not belong to any accepted groups
                                    user.approved(false);
                                    self.users.remove(user.id);
                                }
                            })
                        };
                    } else {
                        if (!_.contains(currentIdentities(), identity.id)) {
                            currentIdentities.push(identity.id);
                        }
                        identity.approved(true);
                        _.chain(self.identities).filter(function(id) {
                            return id.type === 'user'
                        }).each(function(user) {
                            if (_.intersection(user.group_ids, self.groups()).length > 0) {
                                user.approved(true);
                                if (!_.contains(self.users(), user.id)) {
                                    self.users.push(user.id);
                                }
                            }
                        })
                    };
                };
            };

            self.updateCards = function(val) {
                var approvedCards = _.chain(val.targetParent())
                .filter(function(card){
                    if (card.approved()) {
                        return card.cardid
                    }
                })
                .pluck('cardid').value()
                var diff = _.difference(self.cards(), approvedCards)
                self.cards(_.union(diff, approvedCards))
            }

            self.updateApproved = function(val){
                val.item.approved(true);
                self.updateCards(val)
            };

            self.updateUnapproved = function(val){
                val.item.approved(false);
                self.cards.remove(val.item.cardid)
            };

            self.toggleShowDetails = function() {
                self.setIdentityApproval();
                self.showDetails(!self.showDetails())
            }

            self.parse(options.source);
            self.setIdentityApproval();

            self.json = ko.computed(function() {
                var jsObj = ko.toJS({
                    name: self.name,
                    description: self.description,
                    startdate: self.startdate,
                    enddate: self.enddate,
                    active: self.active,
                    id: self.get('id'),
                    groups: self.groups,
                    users: self.users,
                    cards: self.cards
                });
                return JSON.stringify(_.extend(JSON.parse(self._project()), jsObj))
            });

            self.dirty = ko.computed(function() {
                return self.json() !== self._project() || !self.get('id');
            });
        },

        parse: function(source) {
            var self = this;
            self._project(JSON.stringify(source));
            self.name(source.name);
            self.description(source.description);
            self.startdate(source.startdate);
            self.enddate(source.enddate);
            self.active(source.active);
            self.createdby(source.createdby_id);
            self.lasteditedby(source.lasteditedby_id);
            self.groups(source.groups);
            self.users(source.users);
            self.cards(source.cards);
            self.set('id', source.id);
        },

        reset: function() {
            this.parse(JSON.parse(this._project()), self);
        },

        _getURL: function(method) {
            return this.url;
        },

        save: function(userCallback, scope) {
            var self = this;
            var method = "POST";
            var callback = function(request, status, model) {
                if (typeof userCallback === 'function') {
                    userCallback.call(this, request, status, model);
                }
                if (status === 'success') {
                    self.set('id', request.responseJSON.project.id);
                    self.createdby(request.responseJSON.project.createdby_id);
                    self.lasteditedby(request.responseJSON.project.lasteditedby_id);
                    self.groups(request.responseJSON.project.groups);
                    self.users(request.responseJSON.project.users);
                    self.cards(request.responseJSON.project.cards);
                    this._project(this.json());
                };
            };
            this._doRequest({
                type: method,
                url: this._getURL(method),
                data: JSON.stringify(this.toJSON())
            }, callback, scope, 'save');
        },

        toJSON: function() {
            return JSON.parse(this.json());
        },

        update: function() {
            this.setIdentityApproval();
        }
    });
});
<|endoftext|>