<commit_before>var request = require('request')
  , assert = require('assert')
  , vows = require('vows')
  , JSONExp = require('../jsonexp')
  , captureRE = /\[([A-Z_]+)\]/g

function vowsCompiler(options) {
    this.options = options
    this.root = options.root
    this.vars = {}
}

vowsCompiler.prototype = {}

vowsCompiler.prototype.call = function (params) {
    var self = this
    return function() {
        var cb = this.callback
          , options =
            { uri: self.root + self.substitute(params.path)
            , method: params.method
            , headers: self.convertRequestHeaders(params.headers, true)
            }

        if (params.body) options.body = self.substitute(params.body, true)
        else options.headers['content-length'] = 0

        request(options, function (error, response, body) {
            cb(error, response)
        })
    }
}

vowsCompiler.prototype.substitute = function(string, json) {
    for (var key in this.vars) {
        if (!this.vars.hasOwnProperty(key)) continue
        var re = new RegExp('\\[' + key + '\\]', 'g')
        if (json)
            string = string.replace(re, JSON.stringify(this.vars[key]))
        else
            string = string.replace(re, this.vars[key])
    }
    return string
}

vowsCompiler.prototype.convertRequestHeaders = function(arr, sub) {
    var headers = {}, match

    for (var i = 0; i < arr.length; i++) {
        if (!(match = /([^:]+):[ \t]+(.*)/.exec(arr[i]))) continue

        if (sub)
            headers[match[1].toLowerCase()] = this.substitute(match[2])
        else
            headers[match[1].toLowerCase()] = match[2]
    }

    return headers
}

vowsCompiler.prototype.headerCapture = function(string) {
    var captures = []
      , regex = string.replace(captureRE, function(m, name) { captures.push(name); return '(.*)' })

    if (captures.length > 0) return { regex: new RegExp(regex, 'g'), captures: captures }
}

vowsCompiler.prototype.matchers = function (batch, params) {
    var self = this

    batch[params.label] = function(error, result) {
        assert.equal(result.statusCode, params.code, "Got status code: " + result.statusCode)
    }

    var headers = self.convertRequestHeaders(params.headers)
    for (var key in headers) {
        batch[key + ": " + headers[key]] = function(key) {
            return function(error, result) {
                var pattern, header = self.substitute(headers[key])
                if (pattern = self.headerCapture(header)) {
                    assert.ok(result.headers[key], "Header should exist, but doesn't")
                    var match = pattern.regex.exec(result.headers[key])
                    assert.ok(match, "Header did not match pattern")
                    for (var i = 0; i < pattern.captures.length; i++) {
                        assert.ok(match[i], pattern.captures.length, "Header did not match all patterns")
                        self.vars[pattern.captures[i]] = match[i + 1]
                    }
                } else {
                    assert.equal(result.headers[key], headers[key])
                }
            }
        }(key)
    }

    if (params.body) {
        batch[params.body.replace(/\s+/g, " ")] = function(error, result) {
            try {
                var exp = new JSONExp(params.body)
            } catch (e) {
                // invalid JSONExp
                console.log("Failed to compile params: ", e)
                assert.equal(result.body, params.body)
                return
            }

            assert.notEqual(result.body, '', "No body returned")

            assert.doesNotThrow(function(){
                    body = JSON.parse(result.body)
                }, SyntaxError, "Response was not JSON: " + result.body)

            try {
                var match = exp.assert(body, { namespace: self.vars, merge: true })
            } catch (e) {
                e.message += ", got: \n        " + result.body
                throw(e);
            }
        }
    } else {
        batch["Empty response body"] = function(error, result) {
            assert.equal(result.body, '', "Body should be empty")
        }
    }
}

vowsCompiler.prototype.makeBatch = function (test) {
    var batch = {}, topic = {}

    batch[test.req.label] = topic = { topic: this.call(test.req) }
    if (test.resp)
        this.matchers(topic, test.resp);
    else
        topic['done'] = function(err, result) { assert.ok(result) }

    return batch;
}

vowsCompiler.prototype.compile = function (results) {
    var suite = vows.describe(results.title)

    for (var i = 0; i < results.tests.length; i++) {
        suite.addBatch(this.makeBatch(results.tests[i]))
    }

    return suite
}

module.exports = vowsCompiler

<commit_msg>Warn about malformed JSON<commit_after>var request = require('request')
  , assert = require('assert')
  , vows = require('vows')
  , JSONExp = require('../jsonexp')
  , captureRE = /\[([A-Z_]+)\]/g

function vowsCompiler(options) {
    this.options = options
    this.root = options.root
    this.vars = {}
}

vowsCompiler.prototype = {}

vowsCompiler.prototype.call = function (params) {
    var self = this
    return function() {
        var cb = this.callback
          , options =
            { uri: self.root + self.substitute(params.path)
            , method: params.method
            , headers: self.convertRequestHeaders(params.headers, true)
            }

        if (params.body) options.body = self.substitute(params.body, true)
        else options.headers['content-length'] = 0

        request(options, function (error, response, body) {
            cb(error, response)
        })
    }
}

vowsCompiler.prototype.substitute = function(string, json) {
    for (var key in this.vars) {
        if (!this.vars.hasOwnProperty(key)) continue
        var re = new RegExp('\\[' + key + '\\]', 'g')
        if (json)
            string = string.replace(re, JSON.stringify(this.vars[key]))
        else
            string = string.replace(re, this.vars[key])

    }
    if (json) {
        var match;
        if (match = captureRE.exec(string)) {
            console.log("WARNING: Possible unrecognized capture: " + match[0])
            console.log("      -> " + string)
        }

        try {
            JSON.parse(string)
        } catch (e) {
            console.log("WARNING: Malformed JSON: " + string)
            console.log(e)
        }

    }
    return string
}

vowsCompiler.prototype.convertRequestHeaders = function(arr, sub) {
    var headers = {}, match

    for (var i = 0; i < arr.length; i++) {
        if (!(match = /([^:]+):[ \t]+(.*)/.exec(arr[i]))) continue

        if (sub)
            headers[match[1].toLowerCase()] = this.substitute(match[2])
        else
            headers[match[1].toLowerCase()] = match[2]
    }

    return headers
}

vowsCompiler.prototype.headerCapture = function(string) {
    var captures = []
      , regex = string.replace(captureRE, function(m, name) { captures.push(name); return '(.*)' })

    if (captures.length > 0) return { regex: new RegExp(regex, 'g'), captures: captures }
}

vowsCompiler.prototype.matchers = function (batch, params) {
    var self = this

    batch[params.label] = function(error, result) {
        assert.equal(result.statusCode, params.code, "Got status code: " + result.statusCode)
    }

    var headers = self.convertRequestHeaders(params.headers)
    for (var key in headers) {
        batch[key + ": " + headers[key]] = function(key) {
            return function(error, result) {
                var pattern, header = self.substitute(headers[key])
                if (pattern = self.headerCapture(header)) {
                    assert.ok(result.headers[key], "Header should exist, but doesn't")
                    var match = pattern.regex.exec(result.headers[key])
                    assert.ok(match, "Header did not match pattern")
                    for (var i = 0; i < pattern.captures.length; i++) {
                        assert.ok(match[i], pattern.captures.length, "Header did not match all patterns")
                        self.vars[pattern.captures[i]] = match[i + 1]
                    }
                } else {
                    assert.equal(result.headers[key], headers[key])
                }
            }
        }(key)
    }

    if (params.body) {
        batch[params.body.replace(/\s+/g, " ")] = function(error, result) {
            try {
                var exp = new JSONExp(params.body)
            } catch (e) {
                // invalid JSONExp
                console.log("Failed to compile params: ", e)
                assert.equal(result.body, params.body)
                return
            }

            assert.notEqual(result.body, '', "No body returned")

            assert.doesNotThrow(function(){
                    body = JSON.parse(result.body)
                }, SyntaxError, "Response was not JSON: " + result.body)

            try {
                var match = exp.assert(body, { namespace: self.vars, merge: true })
            } catch (e) {
                e.message += ", got: \n        " + result.body
                throw(e);
            }
        }
    } else {
        batch["Empty response body"] = function(error, result) {
            assert.equal(result.body, '', "Body should be empty")
        }
    }
}

vowsCompiler.prototype.makeBatch = function (test) {
    var batch = {}, topic = {}

    batch[test.req.label] = topic = { topic: this.call(test.req) }
    if (test.resp)
        this.matchers(topic, test.resp);
    else
        topic['done'] = function(err, result) { assert.ok(result) }

    return batch;
}

vowsCompiler.prototype.compile = function (results) {
    var suite = vows.describe(results.title)

    for (var i = 0; i < results.tests.length; i++) {
        suite.addBatch(this.makeBatch(results.tests[i]))
    }

    return suite
}

module.exports = vowsCompiler

<|endoftext|>


<commit_before>const _ = require('lodash');
const debug = require('debug')('purge-cache');
const APIBuilder = require('taskcluster-lib-api');
const taskcluster = require('taskcluster-client');
const Entity = require('azure-entities');

// Common schema prefix
const SCHEMA_PREFIX_CONST = 'http://schemas.taskcluster.net/purge-cache/v1/';

// Common patterns URL parameters
const GENERIC_ID_PATTERN = /^[a-zA-Z0-9-_]{1,22}$/;

/** API end-point for version v1/ */
const builder = new APIBuilder({
  title:        'Purge Cache API',
  context: [
    'cfg',              // A typed-env-config instance
    'CachePurge',      // A data.CachePurge instance
    'cachePurgeCache', // An Promise for cacheing cachepurge responses
  ],
  params: {
    provisionerId:    GENERIC_ID_PATTERN,
    workerType:       GENERIC_ID_PATTERN,
  },
  description: [
    'The purge-cache service, typically available at',
    '`purge-cache.taskcluster.net`, is responsible for publishing a pulse',
    'message for workers, so they can purge cache upon request.',
    '',
    'This document describes the API end-point for publishing the pulse',
    'message. This is mainly intended to be used by tools.',
  ].join('\n'),
  serviceName: 'purge-cache',
  apiVersion: 'v1',
});

// Export API
module.exports = builder;

/** Define tasks */
builder.declare({
  method:     'post',
  route:      '/purge-cache/:provisionerId/:workerType',
  name:       'purgeCache',
  scopes:     'purge-cache:<provisionerId>/<workerType>:<cacheName>',
  input:      'purge-cache-request.yml',
  title:      'Purge Worker Cache',
  stability:  APIBuilder.stability.stable,
  description: [
    'Publish a purge-cache message to purge caches named `cacheName` with',
    '`provisionerId` and `workerType` in the routing-key. Workers should',
    'be listening for this message and purge caches when they see it.',
  ].join('\n'),
}, async function(req, res) {
  let {provisionerId, workerType} = req.params;
  let {cacheName} = req.body;

  debug(`Processing request for ${provisionerId}/${workerType}/${cacheName}.`);

  await req.authorize({provisionerId, workerType, cacheName});

  try {
    await this.CachePurge.create({
      workerType,
      provisionerId,
      cacheName,
      before: new Date(),
      expires: taskcluster.fromNow('1 day'),
    });
  } catch (err) {
    if (err.code !== 'EntityAlreadyExists') {
      throw err;
    }
    let cb = await this.CachePurge.load({
      workerType,
      provisionerId,
      cacheName,
    });

    await cb.modify(cachePurge => {
      cachePurge.before = new Date();
      cachePurge.expires = taskcluster.fromNow('1 day');
    });
  }

  // Return 204
  res.status(204).send();
});

builder.declare({
  method:   'get',
  route:    '/purge-cache/list',
  query: {
    continuationToken: Entity.continuationTokenPattern,
    limit: /^[0-9]+$/,
  },
  name:     'allPurgeRequests',
  output:   'all-purge-cache-request-list.yml',
  title:    'All Open Purge Requests',
  stability:  APIBuilder.stability.stable,
  description: [
    'This is useful mostly for administors to view',
    'the set of open purge requests. It should not',
    'be used by workers. They should use the purgeRequests',
    'endpoint that is specific to their workerType and',
    'provisionerId.',
  ].join('\n'),
}, async function(req, res) {
  let continuation = req.query.continuationToken || null;
  let limit = parseInt(req.query.limit || 1000, 10);
  let openRequests = await this.CachePurge.scan({}, {continuation, limit});
  return res.reply({
    continuationToken: openRequests.continuation || '',
    requests: _.map(openRequests.entries, entry => {
      return {
        provisionerId: entry.provisionerId,
        workerType: entry.workerType,
        cacheName: entry.cacheName,
        before: entry.before.toJSON(),
      };
    }),
  });
});

builder.declare({
  method:   'get',
  route:    '/purge-cache/:provisionerId/:workerType',
  query: {
    since: dt => Date.parse(dt) ? null : 'Invalid Date',
  },
  name:     'purgeRequests',
  output:   'purge-cache-request-list.yml',
  title:    'Open Purge Requests for a provisionerId/workerType pair',
  stability:  APIBuilder.stability.stable,
  description: [
    'List of caches that need to be purged if they are from before',
    'a certain time. This is safe to be used in automation from',
    'workers.',
  ].join('\n'),
}, async function(req, res) {

  let {provisionerId, workerType} = req.params;
  let cacheKey = `${provisionerId}/${workerType}`;
  let since = new Date(req.query.since || 0);

  this.cachePurgeCache[cacheKey] = Promise.resolve(this.cachePurgeCache[cacheKey]).then(async cacheCache => {
    if (cacheCache && Date.now() - cacheCache.touched < this.cfg.app.cacheTime * 1000) {
      return cacheCache;
    }
    return Promise.resolve({reqs: await this.CachePurge.query({provisionerId, workerType}), touched: Date.now()});
  });

  let {reqs: openRequests} = await this.cachePurgeCache[cacheKey];
  return res.reply({
    requests: _.reduce(openRequests.entries, (l, entry) => {
      if (entry.before >= since) {
        l.push({
          provisionerId: entry.provisionerId,
          workerType: entry.workerType,
          cacheName: entry.cacheName,
          before: entry.before.toJSON(),
        });
      }
      return l;
    }, []),
  });
});
<commit_msg>Bug 1509186 - remove references to taskcluster.net from API description<commit_after>const _ = require('lodash');
const debug = require('debug')('purge-cache');
const APIBuilder = require('taskcluster-lib-api');
const taskcluster = require('taskcluster-client');
const Entity = require('azure-entities');

// Common patterns URL parameters
const GENERIC_ID_PATTERN = /^[a-zA-Z0-9-_]{1,22}$/;

/** API end-point for version v1/ */
const builder = new APIBuilder({
  title:        'Purge Cache API',
  context: [
    'cfg',              // A typed-env-config instance
    'CachePurge',      // A data.CachePurge instance
    'cachePurgeCache', // An Promise for cacheing cachepurge responses
  ],
  params: {
    provisionerId:    GENERIC_ID_PATTERN,
    workerType:       GENERIC_ID_PATTERN,
  },
  description: [
    'The purge-cache service is responsible for publishing a pulse',
    'message for workers, so they can purge cache upon request.',
    '',
    'This document describes the API end-point for publishing the pulse',
    'message. This is mainly intended to be used by tools.',
  ].join('\n'),
  serviceName: 'purge-cache',
  apiVersion: 'v1',
});

// Export API
module.exports = builder;

/** Define tasks */
builder.declare({
  method:     'post',
  route:      '/purge-cache/:provisionerId/:workerType',
  name:       'purgeCache',
  scopes:     'purge-cache:<provisionerId>/<workerType>:<cacheName>',
  input:      'purge-cache-request.yml',
  title:      'Purge Worker Cache',
  stability:  APIBuilder.stability.stable,
  description: [
    'Publish a purge-cache message to purge caches named `cacheName` with',
    '`provisionerId` and `workerType` in the routing-key. Workers should',
    'be listening for this message and purge caches when they see it.',
  ].join('\n'),
}, async function(req, res) {
  let {provisionerId, workerType} = req.params;
  let {cacheName} = req.body;

  debug(`Processing request for ${provisionerId}/${workerType}/${cacheName}.`);

  await req.authorize({provisionerId, workerType, cacheName});

  try {
    await this.CachePurge.create({
      workerType,
      provisionerId,
      cacheName,
      before: new Date(),
      expires: taskcluster.fromNow('1 day'),
    });
  } catch (err) {
    if (err.code !== 'EntityAlreadyExists') {
      throw err;
    }
    let cb = await this.CachePurge.load({
      workerType,
      provisionerId,
      cacheName,
    });

    await cb.modify(cachePurge => {
      cachePurge.before = new Date();
      cachePurge.expires = taskcluster.fromNow('1 day');
    });
  }

  // Return 204
  res.status(204).send();
});

builder.declare({
  method:   'get',
  route:    '/purge-cache/list',
  query: {
    continuationToken: Entity.continuationTokenPattern,
    limit: /^[0-9]+$/,
  },
  name:     'allPurgeRequests',
  output:   'all-purge-cache-request-list.yml',
  title:    'All Open Purge Requests',
  stability:  APIBuilder.stability.stable,
  description: [
    'This is useful mostly for administors to view',
    'the set of open purge requests. It should not',
    'be used by workers. They should use the purgeRequests',
    'endpoint that is specific to their workerType and',
    'provisionerId.',
  ].join('\n'),
}, async function(req, res) {
  let continuation = req.query.continuationToken || null;
  let limit = parseInt(req.query.limit || 1000, 10);
  let openRequests = await this.CachePurge.scan({}, {continuation, limit});
  return res.reply({
    continuationToken: openRequests.continuation || '',
    requests: _.map(openRequests.entries, entry => {
      return {
        provisionerId: entry.provisionerId,
        workerType: entry.workerType,
        cacheName: entry.cacheName,
        before: entry.before.toJSON(),
      };
    }),
  });
});

builder.declare({
  method:   'get',
  route:    '/purge-cache/:provisionerId/:workerType',
  query: {
    since: dt => Date.parse(dt) ? null : 'Invalid Date',
  },
  name:     'purgeRequests',
  output:   'purge-cache-request-list.yml',
  title:    'Open Purge Requests for a provisionerId/workerType pair',
  stability:  APIBuilder.stability.stable,
  description: [
    'List of caches that need to be purged if they are from before',
    'a certain time. This is safe to be used in automation from',
    'workers.',
  ].join('\n'),
}, async function(req, res) {

  let {provisionerId, workerType} = req.params;
  let cacheKey = `${provisionerId}/${workerType}`;
  let since = new Date(req.query.since || 0);

  this.cachePurgeCache[cacheKey] = Promise.resolve(this.cachePurgeCache[cacheKey]).then(async cacheCache => {
    if (cacheCache && Date.now() - cacheCache.touched < this.cfg.app.cacheTime * 1000) {
      return cacheCache;
    }
    return Promise.resolve({reqs: await this.CachePurge.query({provisionerId, workerType}), touched: Date.now()});
  });

  let {reqs: openRequests} = await this.cachePurgeCache[cacheKey];
  return res.reply({
    requests: _.reduce(openRequests.entries, (l, entry) => {
      if (entry.before >= since) {
        l.push({
          provisionerId: entry.provisionerId,
          workerType: entry.workerType,
          cacheName: entry.cacheName,
          before: entry.before.toJSON(),
        });
      }
      return l;
    }, []),
  });
});
<|endoftext|>


<commit_before>import React from 'react';
import Component from '../component';

export default class Tab extends Component {
  constructor() {
    super();

    this.handleHover = this.handleHover.bind(this);
    this.handleBlur = this.handleBlur.bind(this);
    this.handleClick = this.handleClick.bind(this);

    this.state = {
      hovered: false
    };
  }

  shouldComponentUpdate() {
    return true;
  }

  handleHover() {
    this.setState({
      hovered: true
    });
  }

  handleBlur() {
    this.setState({
      hovered: false
    });
  }

  handleClick(event) {
    const isLeftClick = event.nativeEvent.which === 1;
    const isMiddleClick = event.nativeEvent.which === 2;

    if (isLeftClick && !this.props.isActive) {
      this.props.onSelect();
    } else if (isMiddleClick) {
      this.props.onClose();
    }
  }

  template(css) {
    const {isActive, isFirst, isLast, borderColor, hasActivity} = this.props;
    const {hovered} = this.state;

    return (<li
      onMouseEnter={this.handleHover}
      onMouseLeave={this.handleBlur}
      onClick={this.props.onClick}
      style={{borderColor}}
      className={css(
        'tab',
        isFirst && 'first',
        isActive && 'active',
        isFirst && isActive && 'firstActive',
        hasActivity && 'hasActivity'
      )}
      >
      { this.props.customChildrenBefore }
      <span
        className={css(
          'text',
          isLast && 'textLast',
          isActive && 'textActive'
        )}
        onClick={this.handleClick}
        >
        <span
          title={this.props.text}
          className={css('textInner')}
          >
          { this.props.text }
        </span>
      </span>
      <i
        className={css(
          'icon',
          hovered && 'iconHovered'
        )}
        onClick={this.props.onClose}
        >
        <svg className={css('shape')}>
          <use xlinkHref="./dist/assets/icons.svg#close-tab"/>
        </svg>
      </i>
      { this.props.customChildren }
    </li>);
  }

  styles() {
    return {
      tab: {
        color: '#ccc',
        borderColor: '#ccc',
        borderBottomWidth: 1,
        borderBottomStyle: 'solid',
        borderLeftWidth: 1,
        borderLeftStyle: 'solid',
        listStyleType: 'none',
        flexGrow: 1,
        position: 'relative',
        ':hover': {
          color: '#ccc'
        }
      },

      first: {
        borderLeftWidth: 0,
        paddingLeft: 1
      },

      firstActive: {
        borderLeftWidth: 1,
        paddingLeft: 0
      },

      active: {
        color: '#fff',
        borderBottomWidth: 0,
        ':hover': {
          color: '#fff'
        }
      },

      hasActivity: {
        color: '#50E3C2',
        ':hover': {
          color: '#50E3C2'
        }
      },

      text: {
        transition: 'color .2s ease',
        height: '34px',
        display: 'block',
        width: '100%',
        position: 'relative',
        overflow: 'hidden'
      },

      textInner: {
        position: 'absolute',
        left: 0,
        right: 0,
        top: 0,
        bottom: 0,
        textAlign: 'center',
        textOverflow: 'ellipsis',
        whiteSpace: 'nowrap',
        padding: '0 25px',
        overflow: 'hidden'
      },

      icon: {
        transition: `opacity .2s ease, color .2s ease,
          transform .25s ease, background-color .1s ease`,
        pointerEvents: 'none',
        position: 'absolute',
        right: '7px',
        top: '10px',
        display: 'inline-block',
        width: '14px',
        height: '14px',
        borderRadius: '100%',
        color: '#e9e9e9',
        opacity: 0,
        transform: 'scale(.95)',

        ':hover': {
          backgroundColor: 'rgba(255,255,255, .13)',
          color: '#fff'
        },

        ':active': {
          backgroundColor: 'rgba(255,255,255, .1)',
          color: '#909090'
        }
      },

      iconHovered: {
        opacity: 1,
        transform: 'none',
        pointerEvents: 'all'
      },

      shape: {
        position: 'absolute',
        left: '4px',
        top: '4px',
        width: '6px',
        height: '6px',
        verticalAlign: 'middle',
        fill: 'currentColor',
        shapeRendering: 'crispEdges'
      }
    };
  }

}
<commit_msg>fix tab text alignment issue (#1543)<commit_after>import React from 'react';
import Component from '../component';

export default class Tab extends Component {
  constructor() {
    super();

    this.handleHover = this.handleHover.bind(this);
    this.handleBlur = this.handleBlur.bind(this);
    this.handleClick = this.handleClick.bind(this);

    this.state = {
      hovered: false
    };
  }

  shouldComponentUpdate() {
    return true;
  }

  handleHover() {
    this.setState({
      hovered: true
    });
  }

  handleBlur() {
    this.setState({
      hovered: false
    });
  }

  handleClick(event) {
    const isLeftClick = event.nativeEvent.which === 1;
    const isMiddleClick = event.nativeEvent.which === 2;

    if (isLeftClick && !this.props.isActive) {
      this.props.onSelect();
    } else if (isMiddleClick) {
      this.props.onClose();
    }
  }

  template(css) {
    const {isActive, isFirst, isLast, borderColor, hasActivity} = this.props;
    const {hovered} = this.state;

    return (<li
      onMouseEnter={this.handleHover}
      onMouseLeave={this.handleBlur}
      onClick={this.props.onClick}
      style={{borderColor}}
      className={css(
        'tab',
        isFirst && 'first',
        isActive && 'active',
        isFirst && isActive && 'firstActive',
        hasActivity && 'hasActivity'
      )}
      >
      { this.props.customChildrenBefore }
      <span
        className={css(
          'text',
          isLast && 'textLast',
          isActive && 'textActive'
        )}
        onClick={this.handleClick}
        >
        <span
          title={this.props.text}
          className={css('textInner')}
          >
          { this.props.text }
        </span>
      </span>
      <i
        className={css(
          'icon',
          hovered && 'iconHovered'
        )}
        onClick={this.props.onClose}
        >
        <svg className={css('shape')}>
          <use xlinkHref="./dist/assets/icons.svg#close-tab"/>
        </svg>
      </i>
      { this.props.customChildren }
    </li>);
  }

  styles() {
    return {
      tab: {
        color: '#ccc',
        borderColor: '#ccc',
        borderBottomWidth: 1,
        borderBottomStyle: 'solid',
        borderLeftWidth: 1,
        borderLeftStyle: 'solid',
        listStyleType: 'none',
        flexGrow: 1,
        position: 'relative',
        ':hover': {
          color: '#ccc'
        }
      },

      first: {
        borderLeftWidth: 0,
        paddingLeft: 1
      },

      firstActive: {
        borderLeftWidth: 1,
        paddingLeft: 0
      },

      active: {
        color: '#fff',
        borderBottomWidth: 0,
        ':hover': {
          color: '#fff'
        }
      },

      hasActivity: {
        color: '#50E3C2',
        ':hover': {
          color: '#50E3C2'
        }
      },

      text: {
        transition: 'color .2s ease',
        height: '34px',
        display: 'block',
        width: '100%',
        position: 'relative',
        overflow: 'hidden'
      },

      textInner: {
        position: 'absolute',
        left: '24px',
        right: '24px',
        top: 0,
        bottom: 0,
        textAlign: 'center',
        textOverflow: 'ellipsis',
        whiteSpace: 'nowrap',
        overflow: 'hidden'
      },

      icon: {
        transition: `opacity .2s ease, color .2s ease,
          transform .25s ease, background-color .1s ease`,
        pointerEvents: 'none',
        position: 'absolute',
        right: '7px',
        top: '10px',
        display: 'inline-block',
        width: '14px',
        height: '14px',
        borderRadius: '100%',
        color: '#e9e9e9',
        opacity: 0,
        transform: 'scale(.95)',

        ':hover': {
          backgroundColor: 'rgba(255,255,255, .13)',
          color: '#fff'
        },

        ':active': {
          backgroundColor: 'rgba(255,255,255, .1)',
          color: '#909090'
        }
      },

      iconHovered: {
        opacity: 1,
        transform: 'none',
        pointerEvents: 'all'
      },

      shape: {
        position: 'absolute',
        left: '4px',
        top: '4px',
        width: '6px',
        height: '6px',
        verticalAlign: 'middle',
        fill: 'currentColor',
        shapeRendering: 'crispEdges'
      }
    };
  }

}
<|endoftext|>


<commit_before>'use strict';

var express = require('express'),
    favicon = require('static-favicon'),
    morgan = require('morgan'),
    compression = require('compression'),
    bodyParser = require('body-parser'),
    methodOverride = require('method-override'),
    cookieParser = require('cookie-parser'),
    session = require('express-session'),
    errorHandler = require('errorhandler'),
    path = require('path'),
    config = require('./config');

/**
 * Express configuration
 */
module.exports = function(app) {
  var env = app.get('env');

  if ('development' === env) {
    app.use(require('connect-livereload')());

    // Disable caching of scripts for easier testing
    /*app.use(function noCache(req, res, next) {
      if (req.url.indexOf('/scripts/') === 0) {
        res.header('Cache-Control', 'no-cache, no-store, must-revalidate');
        res.header('Pragma', 'no-cache');
        res.header('Expires', 0);
      }
      next();
    });*/

    app.use(express.static(path.join(config.root, '.tmp')));
    app.use(express.static(path.join(config.root, 'app')));
    app.set('views', config.root + '/app/views');
  }

  if ('production' === env || 'test' === env) {
    app.use(compression());
    app.use(favicon(path.join(config.root, 'public', 'favicon.ico')));
    app.use(express.static(path.join(config.root, 'public')));
    app.set('views', config.root + '/views');
  }

  app.set('view engine', 'jade');
  app.use(morgan('dev'));
  app.use(bodyParser());
  app.use(methodOverride());
  app.use(cookieParser());

  // Error handler - has to be last
  if ('development' === app.get('env')) {
    app.use(errorHandler());
  }
};
<commit_msg>Enabling case sensitive routing.<commit_after>'use strict';

var express = require('express'),
    favicon = require('static-favicon'),
    morgan = require('morgan'),
    compression = require('compression'),
    bodyParser = require('body-parser'),
    methodOverride = require('method-override'),
    cookieParser = require('cookie-parser'),
    session = require('express-session'),
    errorHandler = require('errorhandler'),
    path = require('path'),
    config = require('./config');

/**
 * Express configuration
 */
module.exports = function(app) {
  var env = app.get('env');

  app.set('case sensitive routing', true);

  if ('development' === env) {
    app.use(require('connect-livereload')());

    // Disable caching of scripts for easier testing
    /*app.use(function noCache(req, res, next) {
      if (req.url.indexOf('/scripts/') === 0) {
        res.header('Cache-Control', 'no-cache, no-store, must-revalidate');
        res.header('Pragma', 'no-cache');
        res.header('Expires', 0);
      }
      next();
    });*/

    app.use(express.static(path.join(config.root, '.tmp')));
    app.use(express.static(path.join(config.root, 'app')));
    app.set('views', config.root + '/app/views');
  }

  if ('production' === env || 'test' === env) {
    app.use(compression());
    app.use(favicon(path.join(config.root, 'public', 'favicon.ico')));
    app.use(express.static(path.join(config.root, 'public')));
    app.set('views', config.root + '/views');
  }

  app.set('view engine', 'jade');
  app.use(morgan('dev'));
  app.use(bodyParser());
  app.use(methodOverride());
  app.use(cookieParser());

  // Error handler - has to be last
  if ('development' === app.get('env')) {
    app.use(errorHandler());
  }
};
<|endoftext|>


<commit_before>var _ = require('lodash');

var api = require('./api');
var utils = require('../utils');
var resources = require('../dummy/resources');

var Contact = api.Contact;
var Group = api.Group;
var DummyResource = resources.DummyResource;
var DummyResourceError = resources.DummyResourceError;

var DummyContactsResource = DummyResource.extend(function(self, name, opts) {
    /**class:DummyContactsResource(name)

    Handles api requests to the contacts resource from :class:`DummyApi`.

    :param string name:
        The name of the resource. Should match the name given in api requests.
    */
    DummyResource.call(self, name);

    /**attribute:DummyContactsResource.store
    A list of the resource's currently stored contacts.
    */
    self.store = [];

    /**attribute:DummyContactsResource.search_results
    An object mapping expected search queries to an array of the matching keys.
    */
    self.search_results = {};

    self.create = function(data) {
        data = _.defaults(data || {}, {
            created_at: utils.now(),
            user_account: utils.uuid(),
            key: utils.uuid(),
            msisdn: 'unknown',
            name: null,
            surname: null,
            email_address: null,
            dob: null,
            twitter_handle: null,
            facebook_id: null,
            bbm_pin: null,
            gtalk_id: null
        });

        return new Contact(data);
    };

    self.add = function(contact) {
        /**:DummyContactsResource.add(contact)

        Adds an already created contact to the resource's store.

        :param Contact contact:
            The contact to add.
        */
        /**:DummyContactsResource.add(attrs)
        Adds an contact to the resource via a data object.

        :param object attrs:
            The attributes to initialise a contact with.
        */
        if (!(contact instanceof Contact)) {
            contact = self.create(contact);
        }

        self.store.push(contact);
        return contact;
    };

    self._bad_delivery_class = function(cmd) {
        return new DummyResourceError([
            "Unsupported delivery class",
            "(got: " + cmd.delivery_class + " with address " + cmd.addr + ")"
        ].join(' '));
    };

    self.handlers.get = function(cmd) {
        var type = infer_addr_type(cmd.delivery_class);

        if (!type) {
            throw self._bad_delivery_class(cmd);
        }

        var attrs = {};
        attrs[type] = format_addr(cmd.addr, type);

        var contact = _.find(self.store, attrs);
        if (!contact) {
            throw new DummyResourceError("Contact not found");
        }

        return {
            success: true,
            contact: contact.do.serialize()
        };
    };

    self.handlers.get_by_key = function(cmd) {
        var contact = _.find(self.store, {key: cmd.key});

        if (!contact) {
            throw new DummyResourceError("Contact not found");
        }

        return {
            success: true,
            contact: contact.do.serialize()
        };
    };

    self.handlers.get_or_create = function(cmd) {
        var type = infer_addr_type(cmd.delivery_class);

        if (!type) {
            throw self._bad_delivery_class(cmd);
        }

        var attrs = {};
        attrs[type] = format_addr(cmd.addr, type);

        var contact = _.find(self.store, attrs);
        var created = false;

        if (!contact) {
            created = true;
            contact = self.create(attrs);
            self.add(contact);
        }

        return {
            success: true,
            created: created,
            contact: contact.do.serialize()
        };
    };

    self.handlers.new = function(cmd) {
        var contact = self.create(cmd.contact);
        self.add(contact);

        return {
            success: true,
            contact: contact.do.serialize()
        };
    };

    self.handlers.save = function(cmd) {
        var contact = _.find(self.store, {key: cmd.contact.key});

        if (!contact) {
            throw new DummyResourceError("Contact not found");
        }

        contact.do.reset(cmd.contact);

        return {
            success: true,
            contact: contact.do.serialize()
        };
    };

    self.handlers.update = function(cmd) {
        var contact = _.find(self.store, {key: cmd.key});

        if (!contact) {
            throw new DummyResourceError("Contact not found");
        }

        _.assign(contact, cmd.fields);

        return {
            success: true,
            contact: contact.do.serialize()
        };
    };

    self.handlers.update_extras = function(cmd) {
        var contact = _.find(self.store, {key: cmd.key});

        if (!contact) {
            throw new DummyResourceError("Contact not found");
        }

        _.assign(contact.extra, cmd.fields);

        return {
            success: true,
            contact: contact.do.serialize()
        };
    };

    self.handlers.update_subscriptions = function(cmd) {
        var contact = _.find(self.store, {key: cmd.key});

        if (!contact) {
            throw new DummyResourceError("Contact not found");
        }

        _.assign(contact.subscriptions, cmd.fields);

        return {
            success: true,
            contact: contact.do.serialize()
        };
    };

    self.handlers.search = function(cmd) {
        return {
            success: true,
            keys: self.search_results[cmd.query] || []
        };
    };
});

var DummyGroupsResource = DummyResource.extend(function(self, name, contacts) {
    /**class:DummyGroupsResource(name)

    Handles api requests to the groups resource from :class:`DummyApi`.

    :param string name:
        The name of the resource. Should match the name given in api requests.
    :param DummyContactsResource contacts:
        The contacts resource associated to this groups resource
    */
    DummyResource.call(self, name);

    self.contacts = contacts;

    /**attribute:DummyGroupsResource.store
    A list of the resource's currently stored groups.
    */
    self.store = [];

    /**attribute:DummyGroupsResource.search_results
    An object mapping expected search queries to an array of the matching keys.
    */
    self.search_results = {};

    self.create = function(data) {
        data = _.defaults(data || {}, {
            key: utils.uuid(),
            user_account: utils.uuid(),
            created_at: utils.now()
        });
        return new Group(data);
    };

    self.add = function(group) {
        /**:DummyGroupsResource.add(group)

        Adds an already created group to the resource's store.

        :param Group group:
            The group to add.
        */
        /**:DummyGroupsResource.add(attrs)
        Adds an group to the resource via a data object.

        :param object attrs:
            The attributes to initialise a group with.
        */
        if (!(group instanceof Group)) {
            group = self.create(group);
        }

        self.store.push(group);
        return group;
    };

    self.handlers.get = function(cmd) {
        var group = _.find(self.store, {key: cmd.key});

        if (!group) {
            throw new DummyResourceError("Group not found");
        }

        return {
            success: true,
            group: group.do.serialize()
        };
    };

    self.handlers.get_by_name = function(cmd) {
        var groups = _.where(self.store, {name: cmd.name});
        var group = groups[0];

        if (groups.length > 1) {
            throw new DummyResourceError("Multiple groups found");
        }

        if (!group) {
            throw new DummyResourceError("Group not found");
        }

        return {
            success: true,
            group: group.do.serialize()
        };
    };

    self.handlers.get_or_create_by_name = function(cmd) {
        var groups = _.where(self.store, {name: cmd.name});
        var group = groups[0];

        if (groups.length > 1) {
            throw new DummyResourceError("Multiple groups found");
        }

        var created = false;
        if (!group) {
            created = true;
            group = self.create({name: cmd.name});
            self.add(group);
        }

        return {
            success: true,
            created: created,
            group: group.do.serialize()
        };
    };

    self.handlers.update = function(cmd) {
        var groups = _.where(self.store, {key: cmd.key});
        var group = groups[0];

        if (groups.length > 1) {
            throw new DummyResourceError("Multiple groups found");
        }

        if (!group) {
            throw new DummyResourceError("Group not found");
        }

        _.assign(group, _.pick(cmd, 'name', 'query'));

        return {
            success: true,
            group: group.do.serialize()
        };
    };

    self.handlers.search = function(cmd) {
        var groups = _(self.search_results[cmd.query] || [])
            .map(function(key) {
                return _.find(self.store, {key: key});
            })
            .compact()
            .map(function(group) {
                return group.do.serialize();
            })
            .value();

        return {
            success: true,
            groups: groups
        };
    };

    self.handlers.list = function(cmd) {
        return {
            success: true,
            groups: _.invoke(self.store, 'serialize')
        };
    };

    self.handlers.count_members = function(cmd) {
        var group = _.find(self.store, {key: cmd.key});

        if (!group) {
            throw new DummyResourceError("Group not found");
        }

        var count = !_.isString(group.query)
            ? _.where(self.contacts.store, {groups: [group.key]}).length
            : (self.contacts.search_results[group.query] || []).length;

        return {
            success: true,
            group: group,
            count: count
        };
    };
});

function format_addr(addr, type) {
    var formatter = format_addr[type] || _.identity;
    return formatter(addr);
}

format_addr.msisdn = function(addr) {
    return '+' + addr.replace('+', '');
};

format_addr.gtalk_id = function(addr) {
    return addr.split('/')[0];
};

function infer_addr_type(delivery_class) {
    return {
        sms: 'msisdn',
        ussd: 'msisdn',
        gtalk: 'gtalk_id',
        twitter: 'twitter_handle'
    }[delivery_class];
}

this.DummyContactsResource = DummyContactsResource;
this.DummyGroupsResource = DummyGroupsResource;
this.infer_addr_type = infer_addr_type;
this.format_addr = format_addr;
<commit_msg>Document DummyContactsResource.create() and DummyGroupsResource.create()<commit_after>var _ = require('lodash');

var api = require('./api');
var utils = require('../utils');
var resources = require('../dummy/resources');

var Contact = api.Contact;
var Group = api.Group;
var DummyResource = resources.DummyResource;
var DummyResourceError = resources.DummyResourceError;

var DummyContactsResource = DummyResource.extend(function(self, name, opts) {
    /**class:DummyContactsResource(name)

    Handles api requests to the contacts resource from :class:`DummyApi`.

    :param string name:
        The name of the resource. Should match the name given in api requests.
    */
    DummyResource.call(self, name);

    /**attribute:DummyContactsResource.store
    A list of the resource's currently stored contacts.
    */
    self.store = [];

    /**attribute:DummyContactsResource.search_results
    An object mapping expected search queries to an array of the matching keys.
    */
    self.search_results = {};

    self.create = function(attrs) {
        /**DummyContactsResource.create(attrs)

        Helper for creating a :class:`Contact` with the given attributes (along
        with sensible defaults where possible)

        :param object attrs:
            The attributes to create a :class:`Contact` with.
        */
        attrs = _.defaults(attrs || {}, {
            created_at: utils.now(),
            user_account: utils.uuid(),
            key: utils.uuid(),
            msisdn: 'unknown',
            name: null,
            surname: null,
            email_address: null,
            dob: null,
            twitter_handle: null,
            facebook_id: null,
            bbm_pin: null,
            gtalk_id: null
        });

        return new Contact(attrs);
    };

    self.add = function(contact) {
        /**:DummyContactsResource.add(contact)

        Adds an already created contact to the resource's store.

        :param Contact contact:
            The contact to add.
        */
        /**:DummyContactsResource.add(attrs)
        Adds an contact to the resource via a data object.

        :param object attrs:
            The attributes to initialise a contact with.
        */
        if (!(contact instanceof Contact)) {
            contact = self.create(contact);
        }

        self.store.push(contact);
        return contact;
    };

    self._bad_delivery_class = function(cmd) {
        return new DummyResourceError([
            "Unsupported delivery class",
            "(got: " + cmd.delivery_class + " with address " + cmd.addr + ")"
        ].join(' '));
    };

    self.handlers.get = function(cmd) {
        var type = infer_addr_type(cmd.delivery_class);

        if (!type) {
            throw self._bad_delivery_class(cmd);
        }

        var attrs = {};
        attrs[type] = format_addr(cmd.addr, type);

        var contact = _.find(self.store, attrs);
        if (!contact) {
            throw new DummyResourceError("Contact not found");
        }

        return {
            success: true,
            contact: contact.do.serialize()
        };
    };

    self.handlers.get_by_key = function(cmd) {
        var contact = _.find(self.store, {key: cmd.key});

        if (!contact) {
            throw new DummyResourceError("Contact not found");
        }

        return {
            success: true,
            contact: contact.do.serialize()
        };
    };

    self.handlers.get_or_create = function(cmd) {
        var type = infer_addr_type(cmd.delivery_class);

        if (!type) {
            throw self._bad_delivery_class(cmd);
        }

        var attrs = {};
        attrs[type] = format_addr(cmd.addr, type);

        var contact = _.find(self.store, attrs);
        var created = false;

        if (!contact) {
            created = true;
            contact = self.create(attrs);
            self.add(contact);
        }

        return {
            success: true,
            created: created,
            contact: contact.do.serialize()
        };
    };

    self.handlers.new = function(cmd) {
        var contact = self.create(cmd.contact);
        self.add(contact);

        return {
            success: true,
            contact: contact.do.serialize()
        };
    };

    self.handlers.save = function(cmd) {
        var contact = _.find(self.store, {key: cmd.contact.key});

        if (!contact) {
            throw new DummyResourceError("Contact not found");
        }

        contact.do.reset(cmd.contact);

        return {
            success: true,
            contact: contact.do.serialize()
        };
    };

    self.handlers.update = function(cmd) {
        var contact = _.find(self.store, {key: cmd.key});

        if (!contact) {
            throw new DummyResourceError("Contact not found");
        }

        _.assign(contact, cmd.fields);

        return {
            success: true,
            contact: contact.do.serialize()
        };
    };

    self.handlers.update_extras = function(cmd) {
        var contact = _.find(self.store, {key: cmd.key});

        if (!contact) {
            throw new DummyResourceError("Contact not found");
        }

        _.assign(contact.extra, cmd.fields);

        return {
            success: true,
            contact: contact.do.serialize()
        };
    };

    self.handlers.update_subscriptions = function(cmd) {
        var contact = _.find(self.store, {key: cmd.key});

        if (!contact) {
            throw new DummyResourceError("Contact not found");
        }

        _.assign(contact.subscriptions, cmd.fields);

        return {
            success: true,
            contact: contact.do.serialize()
        };
    };

    self.handlers.search = function(cmd) {
        return {
            success: true,
            keys: self.search_results[cmd.query] || []
        };
    };
});

var DummyGroupsResource = DummyResource.extend(function(self, name, contacts) {
    /**class:DummyGroupsResource(name)

    Handles api requests to the groups resource from :class:`DummyApi`.

    :param string name:
        The name of the resource. Should match the name given in api requests.
    :param DummyContactsResource contacts:
        The contacts resource associated to this groups resource.
    */
    DummyResource.call(self, name);

    self.contacts = contacts;

    /**attribute:DummyGroupsResource.store
    A list of the resource's currently stored groups.
    */
    self.store = [];

    /**attribute:DummyGroupsResource.search_results
    An object mapping expected search queries to an array of the matching keys.
    */
    self.search_results = {};

    self.create = function(data) {
        /**DummyGroupResource.create(attrs)

        Helper for creating a :class:`Group` with the given attributes (along
        with sensible defaults where possible)

        :param object attrs:
            The attributes to create a :class:`Group with.
        */
        data = _.defaults(data || {}, {
            key: utils.uuid(),
            user_account: utils.uuid(),
            created_at: utils.now()
        });
        return new Group(data);
    };

    self.add = function(group) {
        /**:DummyGroupsResource.add(group)

        Adds an already created group to the resource's store.

        :param Group group:
            The group to add.
        */
        /**:DummyGroupsResource.add(attrs)
        Adds an group to the resource via a data object.

        :param object attrs:
            The attributes to initialise a group with.
        */
        if (!(group instanceof Group)) {
            group = self.create(group);
        }

        self.store.push(group);
        return group;
    };

    self.handlers.get = function(cmd) {
        var group = _.find(self.store, {key: cmd.key});

        if (!group) {
            throw new DummyResourceError("Group not found");
        }

        return {
            success: true,
            group: group.do.serialize()
        };
    };

    self.handlers.get_by_name = function(cmd) {
        var groups = _.where(self.store, {name: cmd.name});
        var group = groups[0];

        if (groups.length > 1) {
            throw new DummyResourceError("Multiple groups found");
        }

        if (!group) {
            throw new DummyResourceError("Group not found");
        }

        return {
            success: true,
            group: group.do.serialize()
        };
    };

    self.handlers.get_or_create_by_name = function(cmd) {
        var groups = _.where(self.store, {name: cmd.name});
        var group = groups[0];

        if (groups.length > 1) {
            throw new DummyResourceError("Multiple groups found");
        }

        var created = false;
        if (!group) {
            created = true;
            group = self.create({name: cmd.name});
            self.add(group);
        }

        return {
            success: true,
            created: created,
            group: group.do.serialize()
        };
    };

    self.handlers.update = function(cmd) {
        var groups = _.where(self.store, {key: cmd.key});
        var group = groups[0];

        if (groups.length > 1) {
            throw new DummyResourceError("Multiple groups found");
        }

        if (!group) {
            throw new DummyResourceError("Group not found");
        }

        _.assign(group, _.pick(cmd, 'name', 'query'));

        return {
            success: true,
            group: group.do.serialize()
        };
    };

    self.handlers.search = function(cmd) {
        var groups = _(self.search_results[cmd.query] || [])
            .map(function(key) {
                return _.find(self.store, {key: key});
            })
            .compact()
            .map(function(group) {
                return group.do.serialize();
            })
            .value();

        return {
            success: true,
            groups: groups
        };
    };

    self.handlers.list = function(cmd) {
        return {
            success: true,
            groups: _.invoke(self.store, 'serialize')
        };
    };

    self.handlers.count_members = function(cmd) {
        var group = _.find(self.store, {key: cmd.key});

        if (!group) {
            throw new DummyResourceError("Group not found");
        }

        var count = !_.isString(group.query)
            ? _.where(self.contacts.store, {groups: [group.key]}).length
            : (self.contacts.search_results[group.query] || []).length;

        return {
            success: true,
            group: group,
            count: count
        };
    };
});

function format_addr(addr, type) {
    var formatter = format_addr[type] || _.identity;
    return formatter(addr);
}

format_addr.msisdn = function(addr) {
    return '+' + addr.replace('+', '');
};

format_addr.gtalk_id = function(addr) {
    return addr.split('/')[0];
};

function infer_addr_type(delivery_class) {
    return {
        sms: 'msisdn',
        ussd: 'msisdn',
        gtalk: 'gtalk_id',
        twitter: 'twitter_handle'
    }[delivery_class];
}

this.DummyContactsResource = DummyContactsResource;
this.DummyGroupsResource = DummyGroupsResource;
this.infer_addr_type = infer_addr_type;
this.format_addr = format_addr;
<|endoftext|>


<commit_before>'use strict';

const ReporterTemplate = require('./reporterTemplate');
const _ = require('../common/helper');
let root = require('window-or-global');

let Hooks;
if (root.cmdArgs && root.cmdArgs.hooks) {
  if (_.isExistedFile(root.cmdArgs.hooks)) {
    Hooks = require(root.cmdArgs.hooks).Hooks;
  } else {
    process.exit();
  }
} else {
  Hooks = require('../../public/hooks').Hooks;
}
const hooks = new Hooks();

root.updatePassedTestsCount = function () {
  if (root.mockData.stats.tests > root.mockData.stats.passes) {
    root.mockData.stats.passes++;
  }

  if (root.mockData.stats.pending > 0) {
    root.mockData.stats.pending--;
  }
};

/** Crawling Node: each of the tree node represents a unique user page  */
function NSAppCrawlingTreeNode() {
  this.path = '';
  this.parent = null;
  this.type = 'normal'; //  'tab'/ 'normal'
  this.depth = 0;
  this.actions = []; // Units in {value : NSAppCrawlingTreeNodeAction}
  this.digest = null;
  this.reportSuite = null;
}

function NSAppCrawlingTreeNodeAction() {
  this.isTriggered = false;
  this.location = null;
  this.input = null;
  this.interval = 0;
  this.source = {};
}

NSAppCrawlingTreeNodeAction.prototype.title = function() {
  if (this.location) {
    let components = this.location.split(/[/]+/);
    if (components.length <= 4) {
      return components.join('-');
    } else {
      return components.slice(components.length - 4, components.length).join('-');
    }
  } else {
    return null;
  }
};

NSAppCrawlingTreeNodeAction.prototype.checkInterval = function () {
  this.previousTime = this.currentTime;
  this.currentTime = new Date();

  if (!this.previousTime) {
    this.previousTime = this.currentTime;
  }

  this.interval = this.currentTime.getTime() - this.previousTime.getTime();
};

NSAppCrawlingTreeNode.prototype.isFinishedBrowsing = function() {
  let isFinished = true;
  for (let key in this.actions) {
    if (this.actions[key].isTriggered === false) {
      isFinished = false;
      break;
    }
  }
  return isFinished;
};

NSAppCrawlingTreeNode.prototype.produceNodeActions = function(rawElements) {
  let actions = [];
  for (let index in rawElements) {
    let rawElement = rawElements[index];
    let action = new NSAppCrawlingTreeNodeAction();
    let shallowCopy = Object.assign({}, rawElement);
    delete shallowCopy.children;
    action.source = shallowCopy;
    action.location = shallowCopy.xpath;
    action.input = shallowCopy.input;
    actions.push(action);
  }

  return actions;
};

NSAppCrawlingTreeNode.prototype.checkDigest = function(platform, source, crawler) {
  // 0. check source validity
  try {
    this.raw = crawler.config.platform === 'pc-web' ? JSON.stringify(source.value) : source.value;
    this.innerTree = crawler.config.platform === 'pc-web' ? source.value : JSON.parse(source.value);
  } catch (e) {
    this.terminate(e.message);
  }

  // 1. check digest
  if (!this.digest) {
    if (hooks.checkDigest(platform, source, this)) {
      return new Promise((resolve) => {
        resolve(this.digest);
      });
    } else {
      if (platform === 'ios') {
        return root.wdclient.send('/wd/hub/session/' + root.wdclient.sessionId + '/title', 'get', null, null)
          .then(title => {
            this.digest = title.value;
          });
      } else if (platform === 'pc-web') {
        return root.wdclient.send('/wd/hub/session/' + root.wdclient.sessionId + '/url', 'get', null, null)
          .then(title => {
            this.digest = title.value;
            this.url = this.digest;
          });
      } else {
        // 0. get digest structure
        if (crawler.config.packages && this.innerTree.package) {
          let regex = new RegExp(crawler.config.packages, 'i');
          if (!regex.test(this.innerTree.package)) {
            return new Promise((resolve, reject) => {
              reject(`crawling activity out of default scope ${crawler.config.packages}`);
            });
          }
        }

        // 1. check android digest
        return new Promise((resolve, reject) => {
          this.digest = '' + (source.value.match(/node/g) || []).length +
            (source.value.match(/Android/g) || []).length +
            (source.value.match(/TextView/g) || []).length +
            (source.value.match(/EditText/g) || []).length +
            (source.value.match(/Layout/g) || []).length +
            (source.value.match(/Button/g) || []).length;
          resolve();
        });
      }
    }
  } else {
    return new Promise((resolve) => {
      resolve(this.digest);
    });
  }
};

NSAppCrawlingTreeNode.prototype.abbreviatedDigest = function() {
  if (this.digest) {
    let components = this.digest.split(/[.\-_/\\]+/);
    if (components.length <= 2 || this.digest.length < 20) {
      return components.join('-');
    } else {
      return components.slice(components.length - 4, components.length).join('-');
    }
  } else {
    return null;
  }
};

NSAppCrawlingTreeNode.prototype.updateReportData = function(crawler) {
  // empty case
  if (!this.reportSuite) {
    this.reportSuite = ReporterTemplate.suite();
    this.reportSuite.title = 'View: ' + this.abbreviatedDigest();
    this.reportSuite.uuid = `${_.uuid()}`;

    let shell = ReporterTemplate.suite();
    shell.uuid = _.uuid();
    shell.suites.push(this.reportSuite);
    shell.title = 'View: ' + this.abbreviatedDigest();
    this.reportSuite.context = this.fileName || null;
    root.mockData.suites.suites.push(shell);
  }

  // update total actions
  if (!this.reportSuite.tests.length) {
    this.reportSuite.totalPasses = 0;
    this.reportSuite.totalTests = 0;

    for (let i = 0; i < this.actions.length; i++) {
      let test = ReporterTemplate.test();
      test.title = this.actions[i].title();
      test.fullTitle = `View: ${this.abbreviatedDigest()}//${this.actions[i].title()}`;
      this.actions[i].bindedTest = test;
      this.reportSuite.tests.push(test);
      this.reportSuite.totalPasses++;
      this.reportSuite.totalTests++;
      root.mockData.stats.pending++;
      root.mockData.stats.tests++;
    }

    this.reportSuite.passes = this.reportSuite.tests;
  }

  if (crawler.currentAction &&
    crawler.currentAction.bindedTest &&
    crawler.currentAction.bindedTest.state !== 'passed') {
    crawler.currentAction.checkInterval();
    crawler.currentAction.bindedTest.context = `${this.fileName}` || null;
    crawler.currentAction.bindedTest.pending = false;
    crawler.currentAction.bindedTest.pass = true;
    crawler.currentAction.bindedTest.state = 'passed';
    crawler.currentAction.bindedTest.duration = crawler.currentAction.interval;
    crawler.currentAction.bindedTest.code = JSON.stringify(crawler.currentAction, null, 2);
  }

  root.mockData.stats.suites = crawler.crawlingBuffer.length;
};

exports.NSAppCrawlingTreeNodeAction = NSAppCrawlingTreeNodeAction;
exports.NSAppCrawlingTreeNode = NSAppCrawlingTreeNode;
<commit_msg>删除多余空行<commit_after>'use strict';

const ReporterTemplate = require('./reporterTemplate');
const _ = require('../common/helper');
let root = require('window-or-global');

let Hooks;
if (root.cmdArgs && root.cmdArgs.hooks) {
  if (_.isExistedFile(root.cmdArgs.hooks)) {
    Hooks = require(root.cmdArgs.hooks).Hooks;
  } else {
    process.exit();
  }
} else {
  Hooks = require('../../public/hooks').Hooks;
}
const hooks = new Hooks();

root.updatePassedTestsCount = function () {
  if (root.mockData.stats.tests > root.mockData.stats.passes) {
    root.mockData.stats.passes++;
  }

  if (root.mockData.stats.pending > 0) {
    root.mockData.stats.pending--;
  }
};

/** Crawling Node: each of the tree node represents a unique user page  */
function NSAppCrawlingTreeNode() {
  this.path = '';
  this.parent = null;
  this.type = 'normal'; //  'tab'/ 'normal'
  this.depth = 0;
  this.actions = []; // Units in {value : NSAppCrawlingTreeNodeAction}
  this.digest = null;
  this.reportSuite = null;
}

function NSAppCrawlingTreeNodeAction() {
  this.isTriggered = false;
  this.location = null;
  this.input = null;
  this.interval = 0;
  this.source = {};
}

NSAppCrawlingTreeNodeAction.prototype.title = function() {
  if (this.location) {
    let components = this.location.split(/[/]+/);
    if (components.length <= 4) {
      return components.join('-');
    } else {
      return components.slice(components.length - 4, components.length).join('-');
    }
  } else {
    return null;
  }
};

NSAppCrawlingTreeNodeAction.prototype.checkInterval = function () {
  this.previousTime = this.currentTime;
  this.currentTime = new Date();

  if (!this.previousTime) {
    this.previousTime = this.currentTime;
  }

  this.interval = this.currentTime.getTime() - this.previousTime.getTime();
};

NSAppCrawlingTreeNode.prototype.isFinishedBrowsing = function() {
  let isFinished = true;
  for (let key in this.actions) {
    if (this.actions[key].isTriggered === false) {
      isFinished = false;
      break;
    }
  }
  return isFinished;
};

NSAppCrawlingTreeNode.prototype.produceNodeActions = function(rawElements) {
  let actions = [];
  for (let index in rawElements) {
    let rawElement = rawElements[index];
    let action = new NSAppCrawlingTreeNodeAction();
    let shallowCopy = Object.assign({}, rawElement);
    delete shallowCopy.children;
    action.source = shallowCopy;
    action.location = shallowCopy.xpath;
    action.input = shallowCopy.input;
    actions.push(action);
  }

  return actions;
};

NSAppCrawlingTreeNode.prototype.checkDigest = function(platform, source, crawler) {
  // 0. check source validity
  try {
    this.raw = crawler.config.platform === 'pc-web' ? JSON.stringify(source.value) : source.value;
    this.innerTree = crawler.config.platform === 'pc-web' ? source.value : JSON.parse(source.value);
  } catch (e) {
    this.terminate(e.message);
  }

  // 1. check digest
  if (!this.digest) {
    if (hooks.checkDigest(platform, source, this)) {
      return new Promise((resolve) => {
        resolve(this.digest);
      });
    } else {
      if (platform === 'ios') {
        return root.wdclient.send('/wd/hub/session/' + root.wdclient.sessionId + '/title', 'get', null, null)
          .then(title => {
            this.digest = title.value;
          });
      } else if (platform === 'pc-web') {
        return root.wdclient.send('/wd/hub/session/' + root.wdclient.sessionId + '/url', 'get', null, null)
          .then(title => {
            this.digest = title.value;
            this.url = this.digest;
          });
      } else {
        // 0. get digest structure
        if (crawler.config.packages && this.innerTree.package) {
          let regex = new RegExp(crawler.config.packages, 'i');
          if (!regex.test(this.innerTree.package)) {
            return new Promise((resolve, reject) => {
              reject(`crawling activity out of default scope ${crawler.config.packages}`);
            });
          }
        }

        // 1. check android digest
        return new Promise((resolve, reject) => {
          this.digest = '' + (source.value.match(/node/g) || []).length +
            (source.value.match(/Android/g) || []).length +
            (source.value.match(/TextView/g) || []).length +
            (source.value.match(/EditText/g) || []).length +
            (source.value.match(/Layout/g) || []).length +
            (source.value.match(/Button/g) || []).length;
          resolve();
        });
      }
    }
  } else {
    return new Promise((resolve) => {
      resolve(this.digest);
    });
  }
};

NSAppCrawlingTreeNode.prototype.abbreviatedDigest = function() {
  if (this.digest) {
    let components = this.digest.split(/[.\-_/\\]+/);
    if (components.length <= 2 || this.digest.length < 20) {
      return components.join('-');
    } else {
      return components.slice(components.length - 4, components.length).join('-');
    }
  } else {
    return null;
  }
};

NSAppCrawlingTreeNode.prototype.updateReportData = function(crawler) {
  // empty case
  if (!this.reportSuite) {
    this.reportSuite = ReporterTemplate.suite();
    this.reportSuite.title = 'View: ' + this.abbreviatedDigest();
    this.reportSuite.uuid = `${_.uuid()}`;

    let shell = ReporterTemplate.suite();
    shell.uuid = _.uuid();
    shell.suites.push(this.reportSuite);
    shell.title = 'View: ' + this.abbreviatedDigest();
    this.reportSuite.context = this.fileName || null;
    root.mockData.suites.suites.push(shell);
  }

  // update total actions
  if (!this.reportSuite.tests.length) {
    this.reportSuite.totalPasses = 0;
    this.reportSuite.totalTests = 0;

    for (let i = 0; i < this.actions.length; i++) {
      let test = ReporterTemplate.test();
      test.title = this.actions[i].title();
      test.fullTitle = `View: ${this.abbreviatedDigest()}//${this.actions[i].title()}`;
      this.actions[i].bindedTest = test;
      this.reportSuite.tests.push(test);
      this.reportSuite.totalPasses++;
      this.reportSuite.totalTests++;
      root.mockData.stats.pending++;
      root.mockData.stats.tests++;
    }

    this.reportSuite.passes = this.reportSuite.tests;
  }

  if (crawler.currentAction &&
    crawler.currentAction.bindedTest &&
    crawler.currentAction.bindedTest.state !== 'passed') {
    crawler.currentAction.checkInterval();
    crawler.currentAction.bindedTest.context = `${this.fileName}` || null;
    crawler.currentAction.bindedTest.pending = false;
    crawler.currentAction.bindedTest.pass = true;
    crawler.currentAction.bindedTest.state = 'passed';
    crawler.currentAction.bindedTest.duration = crawler.currentAction.interval;
    crawler.currentAction.bindedTest.code = JSON.stringify(crawler.currentAction, null, 2);
  }

  root.mockData.stats.suites = crawler.crawlingBuffer.length;
};

exports.NSAppCrawlingTreeNodeAction = NSAppCrawlingTreeNodeAction;
exports.NSAppCrawlingTreeNode = NSAppCrawlingTreeNode;
<|endoftext|>


<commit_before>'use strict';

const Hooks = require('../../public/hooks').Hooks;
const ReporterTemplate = require('./reporterTemplate');

const hooks = new Hooks();
const _ = require('../common/helper');

let root = require('window-or-global');

root.updatePassedTestsCount = function () {
  if (root.mockData.stats.tests > root.mockData.stats.passes) {
    root.mockData.stats.passes++;
    root.mockData.stats.pending--;
  }
};

/** Crawling Node: each of the tree node represents a unique user page  */
function NSAppCrawlingTreeNode() {
  this.path = '';
  this.parent = null;
  this.type = 'normal'; //  'tab'/ 'normal'
  this.depth = 0;
  this.actions = []; // Units in {value : NSAppCrawlingTreeNodeAction}
  this.digest = null;
  this.reportSuite = null;
}

function NSAppCrawlingTreeNodeAction() {
  this.isTriggered = false;
  this.location = null;
  this.input = null;
  this.interval = 0;
  this.source = {};
}

NSAppCrawlingTreeNodeAction.prototype.title = function() {
  if (this.location) {
    let components = this.location.split(/[/]+/);
    if (components.length <= 4) {
      return components.join('-');
    } else {
      return components.slice(components.length - 4, components.length).join('-');
    }
  } else {
    return null;
  }
};

NSAppCrawlingTreeNodeAction.prototype.checkInterval = function () {
  this.previousTime = this.currentTime;
  this.currentTime = new Date();

  if (!this.previousTime) {
    this.previousTime = this.currentTime;
  }

  this.interval = this.currentTime.getTime() - this.previousTime.getTime();
};

NSAppCrawlingTreeNode.prototype.isFinishedBrowsing = function() {
  let isFinished = true;
  for (let key in this.actions) {
    if (this.actions[key].isTriggered === false) {
      isFinished = false;
      break;
    }
  }
  return isFinished;
};

NSAppCrawlingTreeNode.prototype.produceNodeActions = function(rawElements) {
  let actions = [];
  for (let index in rawElements) {
    let rawElement = rawElements[index];
    let action = new NSAppCrawlingTreeNodeAction();
    let shallowCopy = Object.assign({}, rawElement);
    delete shallowCopy.children;
    action.source = shallowCopy;
    action.location = shallowCopy.xpath;
    action.input = shallowCopy.input;
    actions.push(action);
  }

  return actions;
};

NSAppCrawlingTreeNode.prototype.checkDigest = function(platform, source) {
  if (!this.digest) {
    if (hooks.checkDigest(platform, source, this)) {
      return new Promise((resolve) => {
        resolve(this.digest);
      });
    } else {
      if (platform === 'ios') {
        return root.wdclient.send('/wd/hub/session/' + root.wdclient.sessionId + '/title', 'get', null, null)
          .then(title => {
            this.digest = title.value;
          });
      } else if (platform === 'pc-web') {
        return root.wdclient.send('/wd/hub/session/' + root.wdclient.sessionId + '/url', 'get', null, null)
          .then(title => {
            this.digest = title.value;
            this.url = this.digest;
          });
      } else {
        return new Promise((resolve, reject) => {
          this.digest = '' + (source.value.match(/node/g) || []).length +
            (source.value.match(/Android/g) || []).length +
            (source.value.match(/TextView/g) || []).length +
            (source.value.match(/EditText/g) || []).length +
            (source.value.match(/Layout/g) || []).length +
            (source.value.match(/Button/g) || []).length;
          resolve();
        });
      }
    }
  } else {
    return new Promise((resolve) => {
      resolve(this.digest);
    });
  }
};

NSAppCrawlingTreeNode.prototype.abbreviatedDigest = function() {
  if (this.digest) {
    let components = this.digest.split(/[.\-_/\\]+/);
    if (components.length <= 2 || this.digest.length < 20) {
      return components.join('-');
    } else {
      return components.slice(components.length - 4, components.length).join('-');
    }
  } else {
    return null;
  }
};

NSAppCrawlingTreeNode.prototype.updateReportData = function(crawler) {
  // empty case
  if (!this.reportSuite) {
    this.reportSuite = ReporterTemplate.suite();
    this.reportSuite.title = 'View: ' + this.abbreviatedDigest();
    this.reportSuite.uuid = `${_.uuid()}`;

    let shell = ReporterTemplate.suite();
    shell.uuid = _.uuid();
    shell.suites.push(this.reportSuite);
    this.reportSuite.context = this.fileName;
    root.mockData.suites.suites.push(shell);
  }

  // update total actions
  if (!this.reportSuite.tests.length) {
    this.reportSuite.totalPasses = 0;
    this.reportSuite.totalTests = 0;

    for (let i = 0; i < this.actions.length; i++) {
      let test = ReporterTemplate.test();
      test.title = this.actions[i].title();
      test.fullTitle = `View: ${this.abbreviatedDigest()}//${this.actions[i].title()}`;
      this.actions[i].bindedTest = test;
      this.reportSuite.tests.push(test);
      this.reportSuite.totalPasses++;
      this.reportSuite.totalTests++;
      root.mockData.stats.pending++;
      root.mockData.stats.tests++;
    }

    this.reportSuite.passes = this.reportSuite.tests;
  }

  if (crawler.currentAction &&
    crawler.currentAction.bindedTest &&
    crawler.currentAction.bindedTest.context.length === 0) {
    crawler.currentAction.checkInterval();
    crawler.currentAction.bindedTest.context = `${this.fileName}`;
    crawler.currentAction.bindedTest.pending = false;
    crawler.currentAction.bindedTest.pass = true;
    crawler.currentAction.bindedTest.state = 'passed';
    crawler.currentAction.bindedTest.duration = crawler.currentAction.interval;
    crawler.currentAction.bindedTest.code = JSON.stringify(crawler.currentAction, null, 2);
  }

  root.mockData.stats.suites = crawler.crawlingBuffer.length;
};

exports.NSAppCrawlingTreeNodeAction = NSAppCrawlingTreeNodeAction;
exports.NSAppCrawlingTreeNode = NSAppCrawlingTreeNode;
<commit_msg>stats update check<commit_after>'use strict';

const Hooks = require('../../public/hooks').Hooks;
const ReporterTemplate = require('./reporterTemplate');

const hooks = new Hooks();
const _ = require('../common/helper');

let root = require('window-or-global');

root.updatePassedTestsCount = function () {
  if (root.mockData.stats.tests > root.mockData.stats.passes) {
    root.mockData.stats.passes++;
  }

  if (root.mockData.stats.pending > 0) {
    root.mockData.stats.pending--;
  }
};

/** Crawling Node: each of the tree node represents a unique user page  */
function NSAppCrawlingTreeNode() {
  this.path = '';
  this.parent = null;
  this.type = 'normal'; //  'tab'/ 'normal'
  this.depth = 0;
  this.actions = []; // Units in {value : NSAppCrawlingTreeNodeAction}
  this.digest = null;
  this.reportSuite = null;
}

function NSAppCrawlingTreeNodeAction() {
  this.isTriggered = false;
  this.location = null;
  this.input = null;
  this.interval = 0;
  this.source = {};
}

NSAppCrawlingTreeNodeAction.prototype.title = function() {
  if (this.location) {
    let components = this.location.split(/[/]+/);
    if (components.length <= 4) {
      return components.join('-');
    } else {
      return components.slice(components.length - 4, components.length).join('-');
    }
  } else {
    return null;
  }
};

NSAppCrawlingTreeNodeAction.prototype.checkInterval = function () {
  this.previousTime = this.currentTime;
  this.currentTime = new Date();

  if (!this.previousTime) {
    this.previousTime = this.currentTime;
  }

  this.interval = this.currentTime.getTime() - this.previousTime.getTime();
};

NSAppCrawlingTreeNode.prototype.isFinishedBrowsing = function() {
  let isFinished = true;
  for (let key in this.actions) {
    if (this.actions[key].isTriggered === false) {
      isFinished = false;
      break;
    }
  }
  return isFinished;
};

NSAppCrawlingTreeNode.prototype.produceNodeActions = function(rawElements) {
  let actions = [];
  for (let index in rawElements) {
    let rawElement = rawElements[index];
    let action = new NSAppCrawlingTreeNodeAction();
    let shallowCopy = Object.assign({}, rawElement);
    delete shallowCopy.children;
    action.source = shallowCopy;
    action.location = shallowCopy.xpath;
    action.input = shallowCopy.input;
    actions.push(action);
  }

  return actions;
};

NSAppCrawlingTreeNode.prototype.checkDigest = function(platform, source) {
  if (!this.digest) {
    if (hooks.checkDigest(platform, source, this)) {
      return new Promise((resolve) => {
        resolve(this.digest);
      });
    } else {
      if (platform === 'ios') {
        return root.wdclient.send('/wd/hub/session/' + root.wdclient.sessionId + '/title', 'get', null, null)
          .then(title => {
            this.digest = title.value;
          });
      } else if (platform === 'pc-web') {
        return root.wdclient.send('/wd/hub/session/' + root.wdclient.sessionId + '/url', 'get', null, null)
          .then(title => {
            this.digest = title.value;
            this.url = this.digest;
          });
      } else {
        return new Promise((resolve, reject) => {
          this.digest = '' + (source.value.match(/node/g) || []).length +
            (source.value.match(/Android/g) || []).length +
            (source.value.match(/TextView/g) || []).length +
            (source.value.match(/EditText/g) || []).length +
            (source.value.match(/Layout/g) || []).length +
            (source.value.match(/Button/g) || []).length;
          resolve();
        });
      }
    }
  } else {
    return new Promise((resolve) => {
      resolve(this.digest);
    });
  }
};

NSAppCrawlingTreeNode.prototype.abbreviatedDigest = function() {
  if (this.digest) {
    let components = this.digest.split(/[.\-_/\\]+/);
    if (components.length <= 2 || this.digest.length < 20) {
      return components.join('-');
    } else {
      return components.slice(components.length - 4, components.length).join('-');
    }
  } else {
    return null;
  }
};

NSAppCrawlingTreeNode.prototype.updateReportData = function(crawler) {
  // empty case
  if (!this.reportSuite) {
    this.reportSuite = ReporterTemplate.suite();
    this.reportSuite.title = 'View: ' + this.abbreviatedDigest();
    this.reportSuite.uuid = `${_.uuid()}`;

    let shell = ReporterTemplate.suite();
    shell.uuid = _.uuid();
    shell.suites.push(this.reportSuite);
    this.reportSuite.context = this.fileName;
    root.mockData.suites.suites.push(shell);
  }

  // update total actions
  if (!this.reportSuite.tests.length) {
    this.reportSuite.totalPasses = 0;
    this.reportSuite.totalTests = 0;

    for (let i = 0; i < this.actions.length; i++) {
      let test = ReporterTemplate.test();
      test.title = this.actions[i].title();
      test.fullTitle = `View: ${this.abbreviatedDigest()}//${this.actions[i].title()}`;
      this.actions[i].bindedTest = test;
      this.reportSuite.tests.push(test);
      this.reportSuite.totalPasses++;
      this.reportSuite.totalTests++;
      root.mockData.stats.pending++;
      root.mockData.stats.tests++;
    }

    this.reportSuite.passes = this.reportSuite.tests;
  }

  if (crawler.currentAction &&
    crawler.currentAction.bindedTest &&
    crawler.currentAction.bindedTest.context.length === 0) {
    crawler.currentAction.checkInterval();
    crawler.currentAction.bindedTest.context = `${this.fileName}`;
    crawler.currentAction.bindedTest.pending = false;
    crawler.currentAction.bindedTest.pass = true;
    crawler.currentAction.bindedTest.state = 'passed';
    crawler.currentAction.bindedTest.duration = crawler.currentAction.interval;
    crawler.currentAction.bindedTest.code = JSON.stringify(crawler.currentAction, null, 2);
  }

  root.mockData.stats.suites = crawler.crawlingBuffer.length;
};

exports.NSAppCrawlingTreeNodeAction = NSAppCrawlingTreeNodeAction;
exports.NSAppCrawlingTreeNode = NSAppCrawlingTreeNode;
<|endoftext|>


<commit_before>'use strict';

var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var Image = require('./Image');
var moment = require('moment');
var _ = require('underscore');
var path = require('path');

var eventSchema = new mongoose.Schema({
    name: { type: String, required: true },
    author: { type: Schema.Types.ObjectId, ref: "User", required: true },
    participants: [{ type: Schema.Types.ObjectId, ref: "User" }],
    description: { type: String, required: true, trim: true },
    activity: { type: String, required: true, default: 'other' },
    place: {
        name: { type: String, required: true, trim: true },
        latitude: { type: Number, required: true },
        longitude: { type: Number, required: true }
    },
    start: {
        date: { type: Date, required: true },
        time: Number,
        dateTime: Date
    },
    end: {
        date: Date,
        time: Number,
        dateTime: Date
    },
    images: [Image.schema] // default order, logo first TODO
});

eventSchema.virtual('prettyStartDate').get(function() {
  return moment(this.start.dateTime).format('dddd Do MMMM HH:mm');
});

eventSchema.virtual('logoUrl').get(function() {
  var logo = _.find(this.images, function(img) { return img.isLogo; });
  console.log("Found logo", logo);
  if (logo) {
    return logo.url;
  } else {
    return null;
  }
});

eventSchema.virtual('participantCount').get(function() {
  return this.participants.length;
});

eventSchema.virtual('isPassed').get(function() {
  return moment(this.start.dateTime).isBefore(moment());
});

eventSchema.set('toJSON', {virtuals: true });

// FIXME: for Bezhan: this get called only on SAVE, not on UPDATE.
// See http://mongoosejs.com/docs/middleware.html#notes
eventSchema.pre('save', function(next) {
  if (this.start) {
    this.start.dateTime = mergeDateTime(this.start.date, this.start.time);
  }
  if (this.end) {
    this.end.dateTime = mergeDateTime(this.end.date, this.end.time);
  }
  next();

  function mergeDateTime(date, time) {
    if (date && time) {
      var dateTime = moment(date);
      dateTime.add({ms: time});
      return dateTime.toDate();
    }
  }
});

module.exports = mongoose.model('Event', eventSchema);<commit_msg>Fix mergeDateTime for zero time.<commit_after>'use strict';

var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var Image = require('./Image');
var moment = require('moment');
var _ = require('underscore');
var path = require('path');

var eventSchema = new mongoose.Schema({
    name: { type: String, required: true },
    author: { type: Schema.Types.ObjectId, ref: "User", required: true },
    participants: [{ type: Schema.Types.ObjectId, ref: "User" }],
    description: { type: String, required: true, trim: true },
    activity: { type: String, required: true, default: 'other' },
    place: {
        name: { type: String, required: true, trim: true },
        latitude: { type: Number, required: true },
        longitude: { type: Number, required: true }
    },
    start: {
        date: { type: Date, required: true },
        time: Number,
        dateTime: Date
    },
    end: {
        date: Date,
        time: Number,
        dateTime: Date
    },
    images: [Image.schema] // default order, logo first TODO
});

eventSchema.virtual('prettyStartDate').get(function() {
  return moment(this.start.dateTime).format('dddd Do MMMM HH:mm');
});

eventSchema.virtual('logoUrl').get(function() {
  var logo = _.find(this.images, function(img) { return img.isLogo; });
  console.log("Found logo", logo);
  if (logo) {
    return logo.url;
  } else {
    return null;
  }
});

eventSchema.virtual('participantCount').get(function() {
  return this.participants.length;
});

eventSchema.virtual('isPassed').get(function() {
  return moment(this.start.dateTime).isBefore(moment());
});

eventSchema.set('toJSON', {virtuals: true });

// FIXME: for Bezhan: this get called only on SAVE, not on UPDATE.
// See http://mongoosejs.com/docs/middleware.html#notes
eventSchema.pre('save', function(next) {
  if (this.start) {
    this.start.dateTime = mergeDateTime(this.start.date, this.start.time || 0);
    console.log('Calculated start.dateTime', this.start.dateTime);
  }
  if (this.end) {
    this.end.dateTime = mergeDateTime(this.end.date, this.end.time);
    console.log('Calculated end.dateTime', this.end.dateTime);
  }
  next();

  function mergeDateTime(date, time) {
    if (!_.isUndefined(date) && !_.isUndefined(time)) {
      var dateTime = moment(date);
      dateTime.add({ms: time});
      return dateTime.toDate();
    }
  }
});

module.exports = mongoose.model('Event', eventSchema);<|endoftext|>


<commit_before>/*! React Starter Kit | MIT License | http://www.reactstarterkit.com/ */

import 'babel/polyfill';
import React from 'react/addons';
import BrowserHistory from 'react-router/lib/BrowserHistory';
import Root from './components/Root';
// import FastClick from 'fastclick';
// import App from './components/App';
// import Dispatcher from './core/Dispatcher';
// import AppActions from './actions/AppActions';
// import ActionTypes from './constants/ActionTypes';

// let path = decodeURI(window.location.pathname);
// let onSetMeta = (name, content) => {
//   // Remove and create a new <meta /> tag in order to make it work
//   // with bookmarks in Safari
//   let elements = document.getElementsByTagName('meta');
//   [].slice.call(elements).forEach((element) => {
//     if (element.getAttribute('name') === name) {
//       element.parentNode.removeChild(element);
//     }
//   });
//   let meta = document.createElement('meta');
//   meta.setAttribute('name', name);
//   meta.setAttribute('content', content);
//   document.getElementsByTagName('head')[0].appendChild(meta);
// };

function run() {
  // Render the top-level React component

  const rootElement = document.getElementById('root');
  const history = new BrowserHistory();

  React.render(<Root history={history}/>, rootElement);

  // let props = {
  //   path: path,
  //   context: {
  //     onSetTitle: value => document.title = value,
  //     onSetMeta
  //   }
  // };
  // let element = React.createElement(App, props);
  // React.render(element, document.getElementById('app'), () => {
  //   let css = document.getElementById('css');
  //   css.parentNode.removeChild(css);
  // });

  // // Update `Application.path` prop when `window.location` is changed
  // Dispatcher.register((action) => {
  //   if (action.type === ActionTypes.CHANGE_LOCATION) {
  //     element = React.cloneElement(element, {path: action.path});
  //     React.render(element, document.getElementById('app'));
  //   }
  // });
}

// Run the application when both DOM is ready
// and page content is loaded
Promise.all([
  new Promise((resolve) => {
    if (window.addEventListener) {
      window.addEventListener('DOMContentLoaded', resolve);
    } else {
      window.attachEvent('onload', resolve);
    }
  }).then(/* () => FastClick.attach(document.body)*/ )// ,
  // new Promise((resolve) => AppActions.loadPage(path, resolve))
]).then(run);
<commit_msg>Initialize touch events<commit_after>/*! React Starter Kit | MIT License | http://www.reactstarterkit.com/ */

import 'babel/polyfill';
import React from 'react/addons';
import BrowserHistory from 'react-router/lib/BrowserHistory';
import Root from './components/Root';
// import FastClick from 'fastclick';
// import App from './components/App';
// import Dispatcher from './core/Dispatcher';
// import AppActions from './actions/AppActions';
// import ActionTypes from './constants/ActionTypes';

// let path = decodeURI(window.location.pathname);
// let onSetMeta = (name, content) => {
//   // Remove and create a new <meta /> tag in order to make it work
//   // with bookmarks in Safari
//   let elements = document.getElementsByTagName('meta');
//   [].slice.call(elements).forEach((element) => {
//     if (element.getAttribute('name') === name) {
//       element.parentNode.removeChild(element);
//     }
//   });
//   let meta = document.createElement('meta');
//   meta.setAttribute('name', name);
//   meta.setAttribute('content', content);
//   document.getElementsByTagName('head')[0].appendChild(meta);
// };

function run() {
  // Render the top-level React component

  const rootElement = document.getElementById('root');
  const history = new BrowserHistory();

  React.render(<Root history={history}/>, rootElement);

  // let props = {
  //   path: path,
  //   context: {
  //     onSetTitle: value => document.title = value,
  //     onSetMeta
  //   }
  // };
  // let element = React.createElement(App, props);
  // React.render(element, document.getElementById('app'), () => {
  //   let css = document.getElementById('css');
  //   css.parentNode.removeChild(css);
  // });

  // // Update `Application.path` prop when `window.location` is changed
  // Dispatcher.register((action) => {
  //   if (action.type === ActionTypes.CHANGE_LOCATION) {
  //     element = React.cloneElement(element, {path: action.path});
  //     React.render(element, document.getElementById('app'));
  //   }
  // });
}

// Run the application when both DOM is ready
// and page content is loaded
Promise.all([
  new Promise((resolve) => {
    if (window.addEventListener) {
      window.addEventListener('DOMContentLoaded', resolve);
    } else {
      window.attachEvent('onload', resolve);
    }
  }).then(() => React.initializeTouchEvents()) // Configure React's event system to handle touch events
  /* () => FastClick.attach(document.body) */ // ,
  // new Promise((resolve) => AppActions.loadPage(path, resolve))
]).then(run);
<|endoftext|>