<commit_before>const {
  SchedulerUtils,
  IMAPConnection,
  PubsubConnector,
  DatabaseConnector,
  MessageTypes,
} = require('nylas-core');
const {
  jsonError,
} = require('./sync-utils')

const {CLAIM_DURATION} = SchedulerUtils;

const FetchFolderList = require('./imap/fetch-category-list')
const FetchMessagesInFolder = require('./imap/fetch-messages-in-category')
const SyncbackTaskFactory = require('./syncback-task-factory')

class SyncWorker {
  constructor(account, db, onExpired) {
    this._db = db;
    this._conn = null;
    this._account = account;
    this._startTime = Date.now();
    this._lastSyncTime = null;
    this._onExpired = onExpired;

    this._syncTimer = null;
    this._expirationTimer = null;
    this._destroyed = false;

    this.syncNow();

    this._onMessage = this._onMessage.bind(this);
    this._listener = PubsubConnector.observeAccount(account.id).subscribe(this._onMessage)
  }

  cleanup() {
    this._destroyed = true;
    this._listener.dispose();
    this.closeConnection()
  }

  closeConnection() {
    if (this._conn) {
      this._conn.end();
    }
    this._conn = null
  }

  _onMessage(msg) {
    const {type} = JSON.parse(msg);
    switch (type) {
      case MessageTypes.ACCOUNT_UPDATED:
        this._onAccountUpdated(); break;
      case MessageTypes.SYNCBACK_REQUESTED:
        this.syncNow(); break;
      default:
        throw new Error(`Invalid message: ${msg}`)
    }
  }

  _onAccountUpdated() {
    console.log("SyncWorker: Detected change to account. Reloading and syncing now.");
    this._getAccount().then((account) => {
      this._account = account;
      this.syncNow();
    })
  }

  _onConnectionIdleUpdate() {
    this.syncNow();
  }

  _getAccount() {
    return DatabaseConnector.forShared().then(({Account}) =>
      Account.find({where: {id: this._account.id}})
    );
  }

  _getIdleFolder() {
    return this._db.Folder.find({where: {role: ['all', 'inbox']}})
  }

  ensureConnection() {
    if (this._conn) {
      return this._conn.connect();
    }
    const settings = this._account.connectionSettings;
    const credentials = this._account.decryptedCredentials();

    if (!settings || !settings.imap_host) {
      return Promise.reject(new Error("ensureConnection: There are no IMAP connection settings for this account."))
    }
    if (!credentials) {
      return Promise.reject(new Error("ensureConnection: There are no IMAP connection credentials for this account."))
    }

    const conn = new IMAPConnection(this._db, Object.assign({}, settings, credentials));
    conn.on('mail', () => {
      this._onConnectionIdleUpdate();
    })
    conn.on('update', () => {
      this._onConnectionIdleUpdate();
    })
    conn.on('queue-empty', () => {
    });

    this._conn = conn;
    return this._conn.connect();
  }

  syncbackMessageActions() {
    const where = {where: {status: "NEW"}, limit: 100};
    return this._db.SyncbackRequest.findAll(where)
      .map((req) => SyncbackTaskFactory.create(this._account, req))
      .each(this.runSyncbackTask.bind(this))
  }

  runSyncbackTask(task) {
    const syncbackRequest = task.syncbackRequestObject()
    return this._conn.runOperation(task)
    .then(() => {
      syncbackRequest.status = "SUCCEEDED"
    })
    .catch((error) => {
      syncbackRequest.error = error
      syncbackRequest.status = "FAILED"
    }).finally(() => syncbackRequest.save())
  }

  syncAllCategories() {
    const {Folder} = this._db;
    const {folderSyncOptions} = this._account.syncPolicy;

    return Folder.findAll().then((categories) => {
      const priority = ['inbox', 'all', 'drafts', 'sent', 'spam', 'trash'].reverse();
      const categoriesToSync = categories.sort((a, b) =>
        (priority.indexOf(a.role) - priority.indexOf(b.role)) * -1
      )

      return Promise.all(categoriesToSync.map((cat) =>
        this._conn.runOperation(new FetchMessagesInFolder(cat, folderSyncOptions))
      ))
    });
  }

  performSync() {
    return this.syncbackMessageActions()
    .then(() => this._conn.runOperation(new FetchFolderList(this._account.provider)))
    .then(() => this.syncAllCategories())
  }

  syncNow() {
    clearTimeout(this._syncTimer);

    if (!process.env.SYNC_AFTER_ERRORS && this._account.errored()) {
      console.log(`SyncWorker: Account ${this._account.emailAddress} is in error state - Skipping sync`)
      return
    }

    this.ensureConnection()
    .then(() => this._account.update({syncError: null}))
    .then(() => this.performSync())
    .then(() => this.onSyncDidComplete())
    .catch((error) => this.onSyncError(error))
    .finally(() => {
      this._lastSyncTime = Date.now()
      this.scheduleNextSync()
    })
  }

  onSyncError(error) {
    console.error(`SyncWorker: Error while syncing account ${this._account.emailAddress} `, error)
    this.closeConnection()

    if (error.source === 'socket') {
      // Continue to retry if it was a network error
      return Promise.resolve()
    }
    this._account.syncError = jsonError(error)
    return this._account.save()
  }

  onSyncDidComplete() {
    const {afterSync} = this._account.syncPolicy;

    if (!this._account.firstSyncCompletedAt) {
      this._account.firstSyncCompletedAt = Date.now()
    }

    const now = Date.now();
    const syncGraphTimeLength = 60 * 30; // 30 minutes, should be the same as SyncGraph.config.timeLength
    let lastSyncCompletions = [...this._account.lastSyncCompletions]
    lastSyncCompletions = [now, ...lastSyncCompletions]
    while (now - lastSyncCompletions[lastSyncCompletions.length - 1] > 1000 * syncGraphTimeLength) {
      lastSyncCompletions.pop();
    }

    this._account.lastSyncCompletions = lastSyncCompletions
    this._account.save()
    console.log('Syncworker: Completed sync cycle')

    if (afterSync === 'idle') {
      return this._getIdleFolder()
      .then((idleFolder) => this._conn.openBox(idleFolder.name))
      .then(() => console.log('SyncWorker: - Idling on inbox category'))
    }

    if (afterSync === 'close') {
      console.log('SyncWorker: - Closing connection');
      this.closeConnection()
      return Promise.resolve()
    }

    throw new Error(`SyncWorker.onSyncDidComplete: Unknown afterSync behavior: ${afterSync}. Closing connection`)
  }

  scheduleNextSync() {
    if (Date.now() - this._startTime > CLAIM_DURATION) {
      console.log("SyncWorker: - Has held account for more than CLAIM_DURATION, returning to pool.");
      this.cleanup();
      this._onExpired();
      return;
    }

    SchedulerUtils.checkIfAccountIsActive(this._account.id).then((active) => {
      const {intervals} = this._account.syncPolicy;
      const interval = active ? intervals.active : intervals.inactive;

      if (interval) {
        const target = this._lastSyncTime + interval;
        console.log(`SyncWorker: Account ${active ? 'active' : 'inactive'}. Next sync scheduled for ${new Date(target).toLocaleString()}`);
        this._syncTimer = setTimeout(() => {
          this.syncNow();
        }, target - Date.now());
      }
    });
  }
}

module.exports = SyncWorker;
<commit_msg>Stop syncing in a hard loop<commit_after>const {
  SchedulerUtils,
  IMAPConnection,
  PubsubConnector,
  DatabaseConnector,
  MessageTypes,
} = require('nylas-core');
const {
  jsonError,
} = require('./sync-utils')

const {CLAIM_DURATION} = SchedulerUtils;

const FetchFolderList = require('./imap/fetch-category-list')
const FetchMessagesInFolder = require('./imap/fetch-messages-in-category')
const SyncbackTaskFactory = require('./syncback-task-factory')

class SyncWorker {
  constructor(account, db, onExpired) {
    this._db = db;
    this._conn = null;
    this._account = account;
    this._startTime = Date.now();
    this._lastSyncTime = null;
    this._onExpired = onExpired;

    this._syncTimer = null;
    this._expirationTimer = null;
    this._destroyed = false;

    this.syncNow({reason: 'Initial'});

    this._onMessage = this._onMessage.bind(this);
    this._listener = PubsubConnector.observeAccount(account.id).subscribe(this._onMessage)
  }

  cleanup() {
    this._destroyed = true;
    this._listener.dispose();
    this.closeConnection()
  }

  closeConnection() {
    if (this._conn) {
      this._conn.end();
    }
    this._conn = null
  }

  _onMessage(msg) {
    const {type} = JSON.parse(msg);
    switch (type) {
      case MessageTypes.ACCOUNT_UPDATED:
        this._onAccountUpdated(); break;
      case MessageTypes.SYNCBACK_REQUESTED:
        this.syncNow({reason: 'Syncback Action Queued'}); break;
      default:
        throw new Error(`Invalid message: ${msg}`)
    }
  }

  _onAccountUpdated() {
    if (this.isNextSyncScheduled()) {
      this._getAccount().then((account) => {
        this._account = account;
        this.syncNow({reason: 'Account Modification'});
      });
    }
  }

  _onConnectionIdleUpdate() {
    this.syncNow({reason: 'IMAP IDLE Fired'});
  }

  _getAccount() {
    return DatabaseConnector.forShared().then(({Account}) =>
      Account.find({where: {id: this._account.id}})
    );
  }

  _getIdleFolder() {
    return this._db.Folder.find({where: {role: ['all', 'inbox']}})
  }

  ensureConnection() {
    if (this._conn) {
      return this._conn.connect();
    }
    const settings = this._account.connectionSettings;
    const credentials = this._account.decryptedCredentials();

    if (!settings || !settings.imap_host) {
      return Promise.reject(new Error("ensureConnection: There are no IMAP connection settings for this account."))
    }
    if (!credentials) {
      return Promise.reject(new Error("ensureConnection: There are no IMAP connection credentials for this account."))
    }

    const conn = new IMAPConnection(this._db, Object.assign({}, settings, credentials));
    conn.on('mail', () => {
      this._onConnectionIdleUpdate();
    })
    conn.on('update', () => {
      this._onConnectionIdleUpdate();
    })
    conn.on('queue-empty', () => {
    });

    this._conn = conn;
    return this._conn.connect();
  }

  syncbackMessageActions() {
    const where = {where: {status: "NEW"}, limit: 100};
    return this._db.SyncbackRequest.findAll(where)
      .map((req) => SyncbackTaskFactory.create(this._account, req))
      .each(this.runSyncbackTask.bind(this))
  }

  runSyncbackTask(task) {
    const syncbackRequest = task.syncbackRequestObject()
    return this._conn.runOperation(task)
    .then(() => {
      syncbackRequest.status = "SUCCEEDED"
    })
    .catch((error) => {
      syncbackRequest.error = error
      syncbackRequest.status = "FAILED"
    }).finally(() => syncbackRequest.save())
  }

  syncAllCategories() {
    const {Folder} = this._db;
    const {folderSyncOptions} = this._account.syncPolicy;

    return Folder.findAll().then((categories) => {
      const priority = ['inbox', 'all', 'drafts', 'sent', 'spam', 'trash'].reverse();
      const categoriesToSync = categories.sort((a, b) =>
        (priority.indexOf(a.role) - priority.indexOf(b.role)) * -1
      )

      return Promise.all(categoriesToSync.map((cat) =>
        this._conn.runOperation(new FetchMessagesInFolder(cat, folderSyncOptions))
      ))
    });
  }

  performSync() {
    return this.syncbackMessageActions()
    .then(() => this._conn.runOperation(new FetchFolderList(this._account.provider)))
    .then(() => this.syncAllCategories())
  }

  syncNow({reason} = {}) {
    clearTimeout(this._syncTimer);
    this._syncTimer = null;

    if (!process.env.SYNC_AFTER_ERRORS && this._account.errored()) {
      console.log(`SyncWorker: Account ${this._account.emailAddress} (${this._account.id}) is in error state - Skipping sync`)
      return
    }
    console.log(`SyncWorker: Account ${this._account.emailAddress} (${this._account.id}) sync started (${reason})`)

    this.ensureConnection()
    .then(() => this._account.update({syncError: null}))
    .then(() => this.performSync())
    .then(() => this.onSyncDidComplete())
    .catch((error) => this.onSyncError(error))
    .finally(() => {
      this._lastSyncTime = Date.now()
      this.scheduleNextSync()
    })
  }

  onSyncError(error) {
    console.error(`SyncWorker: Error while syncing account ${this._account.emailAddress} (${this._account.id})`, error)
    this.closeConnection()

    if (error.source === 'socket') {
      // Continue to retry if it was a network error
      return Promise.resolve()
    }
    this._account.syncError = jsonError(error)
    return this._account.save()
  }

  onSyncDidComplete() {
    const {afterSync} = this._account.syncPolicy;

    if (!this._account.firstSyncCompletedAt) {
      this._account.firstSyncCompletedAt = Date.now()
    }

    const now = Date.now();
    const syncGraphTimeLength = 60 * 30; // 30 minutes, should be the same as SyncGraph.config.timeLength
    let lastSyncCompletions = [...this._account.lastSyncCompletions]
    lastSyncCompletions = [now, ...lastSyncCompletions]
    while (now - lastSyncCompletions[lastSyncCompletions.length - 1] > 1000 * syncGraphTimeLength) {
      lastSyncCompletions.pop();
    }

    this._account.lastSyncCompletions = lastSyncCompletions
    this._account.save()
    console.log('Syncworker: Completed sync cycle')

    if (afterSync === 'idle') {
      return this._getIdleFolder()
      .then((idleFolder) => this._conn.openBox(idleFolder.name))
      .then(() => console.log('SyncWorker: - Idling on inbox category'))
    }

    if (afterSync === 'close') {
      console.log('SyncWorker: - Closing connection');
      this.closeConnection()
      return Promise.resolve()
    }

    throw new Error(`SyncWorker.onSyncDidComplete: Unknown afterSync behavior: ${afterSync}. Closing connection`)
  }

  isNextSyncScheduled() {
    return this._syncTimer != null;
  }

  scheduleNextSync() {
    if (Date.now() - this._startTime > CLAIM_DURATION) {
      console.log("SyncWorker: - Has held account for more than CLAIM_DURATION, returning to pool.");
      this.cleanup();
      this._onExpired();
      return;
    }

    SchedulerUtils.checkIfAccountIsActive(this._account.id).then((active) => {
      const {intervals} = this._account.syncPolicy;
      const interval = active ? intervals.active : intervals.inactive;

      if (interval) {
        const target = this._lastSyncTime + interval;
        console.log(`SyncWorker: Account ${active ? 'active' : 'inactive'}. Next sync scheduled for ${new Date(target).toLocaleString()}`);
        this._syncTimer = setTimeout(() => {
          this.syncNow({reason: 'Scheduled'});
        }, target - Date.now());
      }
    });
  }
}

module.exports = SyncWorker;
<|endoftext|>


<commit_before>import attached from '../lifecycle/attached';
import created from '../lifecycle/created';
import elementContains from '../util/element-contains';
import registry from '../polyfill/registry';
import walkTree from '../util/walk-tree';

var HTMLElement = window.HTMLElement;

export default function (nodes) {
  var nodesToUse = nodes;

  if (!nodes) {
    return nodes;
  }

  if (typeof nodes === 'string') {
    nodesToUse = nodes = document.querySelectorAll(nodes);
  } else if (nodes instanceof HTMLElement) {
    nodesToUse = [nodes];
  }

  walkTree(nodesToUse, function (element) {
    var components = registry.getForElement(element);
    var componentsLength = components.length;

    for (let a = 0; a < componentsLength; a++) {
      created(components[a]).call(element);
    }

    for (let a = 0; a < componentsLength; a++) {
      if (elementContains(document, element)) {
        attached(components[a]).call(element);
      }
    }
  });

  return nodes;
}
<commit_msg>getForElement() -> find()<commit_after>import attached from '../lifecycle/attached';
import created from '../lifecycle/created';
import elementContains from '../util/element-contains';
import registry from '../polyfill/registry';
import walkTree from '../util/walk-tree';

var HTMLElement = window.HTMLElement;

export default function (nodes) {
  var nodesToUse = nodes;

  if (!nodes) {
    return nodes;
  }

  if (typeof nodes === 'string') {
    nodesToUse = nodes = document.querySelectorAll(nodes);
  } else if (nodes instanceof HTMLElement) {
    nodesToUse = [nodes];
  }

  walkTree(nodesToUse, function (element) {
    var components = registry.find(element);
    var componentsLength = components.length;

    for (let a = 0; a < componentsLength; a++) {
      created(components[a]).call(element);
    }

    for (let a = 0; a < componentsLength; a++) {
      if (elementContains(document, element)) {
        attached(components[a]).call(element);
      }
    }
  });

  return nodes;
}
<|endoftext|>


<commit_before>var url         = require('url')
var MemoryCache = require('./memory-cache')
var pkg         = require('../package.json')

var t           = {
  ms:           1,
  second:       1000,
  minute:       60000,
  hour:         3600000,
  day:          3600000 * 24,
  week:         3600000 * 24 * 7,
  month:        3600000 * 24 * 30,
}

var instances = []

var matches = function(a) {
  return function(b) { return a === b }
}

var doesntMatch = function(a) {
  return function(b) { return !matches(a)(b) }
}

var logDuration = function(d, prefix) {
  var str = (d > 1000) ? ((d/1000).toFixed(2) + 'sec') : (d + 'ms')
  return '\x1b[33m- ' + (prefix ? prefix + ' ' : '') + str + '\x1b[0m'
}

function ApiCache() {
  var memCache = new MemoryCache

  var globalOptions = {
    debug:              false,
    defaultDuration:    3600000,
    enabled:            true,
    appendKey:          [],
    jsonp:              false,
    redisClient:        false,
    statusCodes: {
      include: [],
      exclude: [],
    },
    events: {
      'expire': undefined
    },
    headers: {
      // 'cache-control':  'no-cache' // example of header overwrite
    }
  }

  var middlewareOptions = []
  var instance = this
  var index = null

  instances.push(this)
  this.id = instances.length

  function debug(a,b,c,d) {
    var arr = (['\x1b[36m[apicache]\x1b[0m', a,b,c,d]).filter(function(arg) { return arg !== undefined })
    var debugEnv = process.env.DEBUG && process.env.DEBUG.split(',').indexOf('apicache') !== -1

    return (globalOptions.debug || debugEnv) && console.log.apply(null, arr)
  }

  function shouldCacheResponse(response) {
    var opt = globalOptions
    var codes = opt.statusCodes

    if (!response) return false

    if (codes.exclude && codes.exclude.length && codes.exclude.indexOf(response.statusCode) !== -1) return false
    if (codes.include && codes.include.length && codes.include.indexOf(response.statusCode) === -1) return false

    return true
  }

  function addIndexEntries(key, req) {
    var groupName = req.apicacheGroup

    if (groupName) {
      debug('group detected "' + groupName + '"')
      var group = (index.groups[groupName] = index.groups[groupName] || [])
      group.unshift(key)
    }

    index.all.unshift(key)
  }

  function createCacheObject(status, headers, data, encoding) {
    return {
      status: status,
      headers: Object.assign({}, headers),
      data: data,
      encoding: encoding
    }
  }

  function cacheResponse(key, value, duration) {
    var redis = globalOptions.redisClient
    var expireCallback = globalOptions.events.expire

    if (redis) {
      try {
        redis.hset(key, "response", JSON.stringify(value))
        redis.hset(key, "duration", duration)
        redis.expire(key, duration/1000, expireCallback)
      } catch (err) {
        debug('[apicache] error in redis.hset()')
      }
    } else {
      memCache.add(key, value, duration, expireCallback)
    }

    // add automatic cache clearing from duration, includes max limit on setTimeout
    setTimeout(function() { instance.clear(key, true) }, Math.min(duration, 2147483647))
  }

  function accumulateContent(res, content) {
    if (content) {
      if (typeof(content) == 'string') {
        res._apicache.content = (res._apicache.content || '') + content;
      } else if (Buffer.isBuffer(content)) {
        var oldContent = res._apicache.content
        if (!oldContent) {
          oldContent = !Buffer.alloc ? new Buffer(0) : Buffer.alloc(0);
        }
        res._apicache.content = Buffer.concat([oldContent, content], oldContent.length + content.length);
      } else {
        res._apicache.content = content
        // res._apicache.cacheable = false;
      }
    }
  }

  function makeResponseCacheable(req, res, next, key, duration, strDuration) {
    // monkeypatch res.end to create cache object
    res._apicache = {
      write: res.write,
      end: res.end,
      cacheable: true,
      content: undefined
    }

    // add cache control headers
    if (!globalOptions.headers['cache-control']) {
      res.header('cache-control', 'max-age=' + (duration / 1000).toFixed(0))
    }

    // append header overwrites if applicable
    Object.keys(globalOptions.headers).forEach(function(name) {
      res.header(name, globalOptions.headers[name])
    })

    // patch res.write
    res.write = function(content) {
      accumulateContent(res, content);
      return res._apicache.write.apply(this, arguments);
    }

    // patch res.end
    res.end = function(content, encoding) {
      if (shouldCacheResponse(res)) {

        accumulateContent(res, content);

        if (res._apicache.cacheable && res._apicache.content) {
          addIndexEntries(key, req)
          var cacheObject = createCacheObject(res.statusCode, res._headers, res._apicache.content, encoding)
          cacheResponse(key, cacheObject, duration)

          // display log entry
          var elapsed = new Date() - req.apicacheTimer
          debug('adding cache entry for "' + key + '" @ ' + strDuration, logDuration(elapsed))
        }
      }

      return res._apicache.end.apply(this, arguments);
    }

    next()
  }

  function sendCachedResponse(response, cacheObject) {
    var headers = (typeof response.getHeaders === 'function') ? response.getHeaders() : response._headers;
    Object.assign(headers, cacheObject.headers || {}, {
      'apicache-store': globalOptions.redisClient ? 'redis' : 'memory',
      'apicache-version': pkg.version
    })

    // unstringify buffers
    var data = cacheObject.data
    if (data && data.type === 'Buffer') {
      data = new Buffer(data.data)
    }

    response.writeHead(cacheObject.status || 200, headers)

    return response.end(data, cacheObject.encoding)
  }

  function syncOptions() {
    for (var i in middlewareOptions) {
      Object.assign(middlewareOptions[i].options, globalOptions, middlewareOptions[i].localOptions)
    }
  }

  this.clear = function(target, isAutomatic) {
    var group = index.groups[target]
    var redis = globalOptions.redisClient

    if (group) {
      debug('clearing group "' + target + '"')

      group.forEach(function(key) {
        debug('clearing cached entry for "' + key + '"')

        if (!globalOptions.redisClient) {
          memCache.delete(key)
        } else {
          try {
            redis.del(key)
          } catch(err) {
            throw Error('[apicache] error in redis.del("' + key + '"")')
          }
        }
        index.all = index.all.filter(doesntMatch(key))
      })

      delete index.groups[target]
    } else if (target) {
      debug('clearing ' + (isAutomatic ? 'expired' : 'cached') + ' entry for "' + target + '"')

      // clear actual cached entry
      if (!redis) {
        memCache.delete(target)
      } else {
        try {
          redis.del(target)
        } catch(err) {
          throw Error('[apicache] error in redis.del("' + target + '"")')
        }
      }

      // remove from global index
      index.all = index.all.filter(doesntMatch(target))

      // remove target from each group that it may exist in
      Object.keys(index.groups).forEach(function(groupName) {
        index.groups[groupName] = index.groups[groupName].filter(doesntMatch(target))

        // delete group if now empty
        if (!index.groups[groupName].length) {
          delete index.groups[groupName]
        }
      })
    } else {
      debug('clearing entire index')

      if (!redis) {
        memCache.clear()
      } else {
        // clear redis keys one by one from internal index to prevent clearing non-apicache entries
        index.all.forEach(function(key) {
          try {
            redis.del(key)
          } catch(err) {
            throw Error('[apicache] error in redis.del("' + key + '"")')
          }
        })
      }
      this.resetIndex()
    }

    return this.getIndex()
  }

  this.getDuration = function(duration) {
    if (typeof duration === 'number') return duration

    if (typeof duration === 'string') {
      var split = duration.match(/^([\d\.,]+)\s(\w+)$/)

      if (split.length === 3) {
        var len = parseFloat(split[1])
        var unit = split[2].replace(/s$/i,'').toLowerCase()
        if (unit === 'm') {
          unit = 'ms'
        }

        return (len || 1) * (t[unit] || 0)
      }
    }

    return globalOptions.defaultDuration
  }

  this.getIndex = function(group) {
    if (group) {
      return index.groups[group]
    } else {
      return index
    }
  }

  this.middleware = function cache(strDuration, middlewareToggle, localOptions) {
    var duration = instance.getDuration(strDuration)
    var opt = {}

    middlewareOptions.push({
      options: opt
    })

    var options = function (localOptions) {
      if (localOptions) {
        middlewareOptions.find(function (middleware) {
          return middleware.options === opt
        }).localOptions = localOptions
      }

      syncOptions()

      return opt
    }

    options(localOptions)

    var cache = function(req, res, next) {
      function bypass() {
        debug('bypass detected, skipping cache.')
        return next()
      }

      // initial bypass chances
      if (!opt.enabled) return bypass()
      if (req.headers['x-apicache-bypass'] || req.headers['x-apicache-force-fetch']) return bypass()
      if (typeof middlewareToggle === 'function') {
        if (!middlewareToggle(req, res)) return bypass()
      } else if (middlewareToggle !== undefined && !middlewareToggle) {
        return bypass()
      }

      // embed timer
      req.apicacheTimer = new Date()

      // In Express 4.x the url is ambigious based on where a router is mounted.  originalUrl will give the full Url
      var key = req.originalUrl || req.url

      // Remove querystring from key if jsonp option is enabled
      if (opt.jsonp) {
        key = url.parse(key).pathname
      }

      if (opt.appendKey.length > 0) {
        var appendKey = req

        for (var i = 0; i < opt.appendKey.length; i++) {
          appendKey = appendKey[opt.appendKey[i]]
        }
        key += '$$appendKey=' + appendKey
      }

      // attempt cache hit
      var redis = opt.redisClient
      var cached = !redis ? memCache.getValue(key) : null

      // send if cache hit from memory-cache
      if (cached) {
        var elapsed = new Date() - req.apicacheTimer
        debug('sending cached (memory-cache) version of', key, logDuration(elapsed))

        return sendCachedResponse(res, cached)
      }

      // send if cache hit from redis
      if (redis) {
        try {
          redis.hgetall(key, function (err, obj) {
            if (!err && obj) {
              var elapsed = new Date() - req.apicacheTimer
              debug('sending cached (redis) version of', key, logDuration(elapsed))

              return sendCachedResponse(res, JSON.parse(obj.response))
            } else {
              return makeResponseCacheable(req, res, next, key, duration, strDuration)
            }
          })
        } catch (err) {
          // bypass redis on error
          return makeResponseCacheable(req, res, next, key, duration, strDuration)
        }
      } else {
        return makeResponseCacheable(req, res, next, key, duration, strDuration)
      }
    }

    cache.options = options

    return cache
  }

  this.options = function(options) {
    if (options) {
      Object.assign(globalOptions, options)
      syncOptions()

      return this
    } else {
      return globalOptions
    }
  }

  this.resetIndex = function() {
    index = {
      all: [],
      groups: {}
    }
  }

  this.newInstance = function(config) {
    var instance = new ApiCache()

    if (config) {
      instance.options(config)
    }

    return instance
  }

  this.clone = function() {
    return this.newInstance(this.options())
  }

  // initialize index
  this.resetIndex()
}

module.exports = new ApiCache()
<commit_msg>Add option to disable header caching.<commit_after>var url         = require('url')
var MemoryCache = require('./memory-cache')
var pkg         = require('../package.json')

var t           = {
  ms:           1,
  second:       1000,
  minute:       60000,
  hour:         3600000,
  day:          3600000 * 24,
  week:         3600000 * 24 * 7,
  month:        3600000 * 24 * 30,
}

var instances = []

var matches = function(a) {
  return function(b) { return a === b }
}

var doesntMatch = function(a) {
  return function(b) { return !matches(a)(b) }
}

var logDuration = function(d, prefix) {
  var str = (d > 1000) ? ((d/1000).toFixed(2) + 'sec') : (d + 'ms')
  return '\x1b[33m- ' + (prefix ? prefix + ' ' : '') + str + '\x1b[0m'
}

function ApiCache() {
  var memCache = new MemoryCache

  var globalOptions = {
    debug:              false,
    defaultDuration:    3600000,
    enabled:            true,
    appendKey:          [],
    jsonp:              false,
    redisClient:        false,
    cacheHeaders:       true,
    statusCodes: {
      include: [],
      exclude: [],
    },
    events: {
      'expire': undefined
    },
    headers: {
      // 'cache-control':  'no-cache' // example of header overwrite
    }
  }

  var middlewareOptions = []
  var instance = this
  var index = null

  instances.push(this)
  this.id = instances.length

  function debug(a,b,c,d) {
    var arr = (['\x1b[36m[apicache]\x1b[0m', a,b,c,d]).filter(function(arg) { return arg !== undefined })
    var debugEnv = process.env.DEBUG && process.env.DEBUG.split(',').indexOf('apicache') !== -1

    return (globalOptions.debug || debugEnv) && console.log.apply(null, arr)
  }

  function shouldCacheResponse(response) {
    var opt = globalOptions
    var codes = opt.statusCodes

    if (!response) return false

    if (codes.exclude && codes.exclude.length && codes.exclude.indexOf(response.statusCode) !== -1) return false
    if (codes.include && codes.include.length && codes.include.indexOf(response.statusCode) === -1) return false

    return true
  }

  function addIndexEntries(key, req) {
    var groupName = req.apicacheGroup

    if (groupName) {
      debug('group detected "' + groupName + '"')
      var group = (index.groups[groupName] = index.groups[groupName] || [])
      group.unshift(key)
    }

    index.all.unshift(key)
  }

  function createCacheObject(status, headers, data, encoding) {
    return {
      status: status,
      headers: Object.assign({}, globalOptions.cacheHeaders ? headers : {}),
      data: data,
      encoding: encoding
    }
  }

  function cacheResponse(key, value, duration) {
    var redis = globalOptions.redisClient
    var expireCallback = globalOptions.events.expire

    if (redis) {
      try {
        redis.hset(key, "response", JSON.stringify(value))
        redis.hset(key, "duration", duration)
        redis.expire(key, duration/1000, expireCallback)
      } catch (err) {
        debug('[apicache] error in redis.hset()')
      }
    } else {
      memCache.add(key, value, duration, expireCallback)
    }

    // add automatic cache clearing from duration, includes max limit on setTimeout
    setTimeout(function() { instance.clear(key, true) }, Math.min(duration, 2147483647))
  }

  function accumulateContent(res, content) {
    if (content) {
      if (typeof(content) == 'string') {
        res._apicache.content = (res._apicache.content || '') + content;
      } else if (Buffer.isBuffer(content)) {
        var oldContent = res._apicache.content
        if (!oldContent) {
          oldContent = !Buffer.alloc ? new Buffer(0) : Buffer.alloc(0);
        }
        res._apicache.content = Buffer.concat([oldContent, content], oldContent.length + content.length);
      } else {
        res._apicache.content = content
        // res._apicache.cacheable = false;
      }
    }
  }

  function makeResponseCacheable(req, res, next, key, duration, strDuration) {
    // monkeypatch res.end to create cache object
    res._apicache = {
      write: res.write,
      end: res.end,
      cacheable: true,
      content: undefined
    }

    // add cache control headers
    if (!globalOptions.headers['cache-control']) {
      res.header('cache-control', 'max-age=' + (duration / 1000).toFixed(0))
    }

    // append header overwrites if applicable
    Object.keys(globalOptions.headers).forEach(function(name) {
      res.header(name, globalOptions.headers[name])
    })

    // patch res.write
    res.write = function(content) {
      accumulateContent(res, content);
      return res._apicache.write.apply(this, arguments);
    }

    // patch res.end
    res.end = function(content, encoding) {
      if (shouldCacheResponse(res)) {

        accumulateContent(res, content);

        if (res._apicache.cacheable && res._apicache.content) {
          addIndexEntries(key, req)
          var cacheObject = createCacheObject(res.statusCode, res._headers, res._apicache.content, encoding)
          cacheResponse(key, cacheObject, duration)

          // display log entry
          var elapsed = new Date() - req.apicacheTimer
          debug('adding cache entry for "' + key + '" @ ' + strDuration, logDuration(elapsed))
        }
      }

      return res._apicache.end.apply(this, arguments);
    }

    next()
  }

  function sendCachedResponse(response, cacheObject) {
    var headers = (typeof response.getHeaders === 'function') ? response.getHeaders() : response._headers;
    var cachedHeaders = globalOptions.cacheHeaders ? cacheObject.headers : {};
    Object.assign(headers, cachedHeaders || {}, {
      'apicache-store': globalOptions.redisClient ? 'redis' : 'memory',
      'apicache-version': pkg.version
    })

    // unstringify buffers
    var data = cacheObject.data
    if (data && data.type === 'Buffer') {
      data = new Buffer(data.data)
    }

    response.writeHead(cacheObject.status || 200, headers)

    return response.end(data, cacheObject.encoding)
  }

  function syncOptions() {
    for (var i in middlewareOptions) {
      Object.assign(middlewareOptions[i].options, globalOptions, middlewareOptions[i].localOptions)
    }
  }

  this.clear = function(target, isAutomatic) {
    var group = index.groups[target]
    var redis = globalOptions.redisClient

    if (group) {
      debug('clearing group "' + target + '"')

      group.forEach(function(key) {
        debug('clearing cached entry for "' + key + '"')

        if (!globalOptions.redisClient) {
          memCache.delete(key)
        } else {
          try {
            redis.del(key)
          } catch(err) {
            throw Error('[apicache] error in redis.del("' + key + '"")')
          }
        }
        index.all = index.all.filter(doesntMatch(key))
      })

      delete index.groups[target]
    } else if (target) {
      debug('clearing ' + (isAutomatic ? 'expired' : 'cached') + ' entry for "' + target + '"')

      // clear actual cached entry
      if (!redis) {
        memCache.delete(target)
      } else {
        try {
          redis.del(target)
        } catch(err) {
          throw Error('[apicache] error in redis.del("' + target + '"")')
        }
      }

      // remove from global index
      index.all = index.all.filter(doesntMatch(target))

      // remove target from each group that it may exist in
      Object.keys(index.groups).forEach(function(groupName) {
        index.groups[groupName] = index.groups[groupName].filter(doesntMatch(target))

        // delete group if now empty
        if (!index.groups[groupName].length) {
          delete index.groups[groupName]
        }
      })
    } else {
      debug('clearing entire index')

      if (!redis) {
        memCache.clear()
      } else {
        // clear redis keys one by one from internal index to prevent clearing non-apicache entries
        index.all.forEach(function(key) {
          try {
            redis.del(key)
          } catch(err) {
            throw Error('[apicache] error in redis.del("' + key + '"")')
          }
        })
      }
      this.resetIndex()
    }

    return this.getIndex()
  }

  this.getDuration = function(duration) {
    if (typeof duration === 'number') return duration

    if (typeof duration === 'string') {
      var split = duration.match(/^([\d\.,]+)\s(\w+)$/)

      if (split.length === 3) {
        var len = parseFloat(split[1])
        var unit = split[2].replace(/s$/i,'').toLowerCase()
        if (unit === 'm') {
          unit = 'ms'
        }

        return (len || 1) * (t[unit] || 0)
      }
    }

    return globalOptions.defaultDuration
  }

  this.getIndex = function(group) {
    if (group) {
      return index.groups[group]
    } else {
      return index
    }
  }

  this.middleware = function cache(strDuration, middlewareToggle, localOptions) {
    var duration = instance.getDuration(strDuration)
    var opt = {}

    middlewareOptions.push({
      options: opt
    })

    var options = function (localOptions) {
      if (localOptions) {
        middlewareOptions.find(function (middleware) {
          return middleware.options === opt
        }).localOptions = localOptions
      }

      syncOptions()

      return opt
    }

    options(localOptions)

    var cache = function(req, res, next) {
      function bypass() {
        debug('bypass detected, skipping cache.')
        return next()
      }

      // initial bypass chances
      if (!opt.enabled) return bypass()
      if (req.headers['x-apicache-bypass'] || req.headers['x-apicache-force-fetch']) return bypass()
      if (typeof middlewareToggle === 'function') {
        if (!middlewareToggle(req, res)) return bypass()
      } else if (middlewareToggle !== undefined && !middlewareToggle) {
        return bypass()
      }

      // embed timer
      req.apicacheTimer = new Date()

      // In Express 4.x the url is ambigious based on where a router is mounted.  originalUrl will give the full Url
      var key = req.originalUrl || req.url

      // Remove querystring from key if jsonp option is enabled
      if (opt.jsonp) {
        key = url.parse(key).pathname
      }

      if (opt.appendKey.length > 0) {
        var appendKey = req

        for (var i = 0; i < opt.appendKey.length; i++) {
          appendKey = appendKey[opt.appendKey[i]]
        }
        key += '$$appendKey=' + appendKey
      }

      // attempt cache hit
      var redis = opt.redisClient
      var cached = !redis ? memCache.getValue(key) : null

      // send if cache hit from memory-cache
      if (cached) {
        var elapsed = new Date() - req.apicacheTimer
        debug('sending cached (memory-cache) version of', key, logDuration(elapsed))

        return sendCachedResponse(res, cached)
      }

      // send if cache hit from redis
      if (redis) {
        try {
          redis.hgetall(key, function (err, obj) {
            if (!err && obj) {
              var elapsed = new Date() - req.apicacheTimer
              debug('sending cached (redis) version of', key, logDuration(elapsed))

              return sendCachedResponse(res, JSON.parse(obj.response))
            } else {
              return makeResponseCacheable(req, res, next, key, duration, strDuration)
            }
          })
        } catch (err) {
          // bypass redis on error
          return makeResponseCacheable(req, res, next, key, duration, strDuration)
        }
      } else {
        return makeResponseCacheable(req, res, next, key, duration, strDuration)
      }
    }

    cache.options = options

    return cache
  }

  this.options = function(options) {
    if (options) {
      Object.assign(globalOptions, options)
      syncOptions()

      return this
    } else {
      return globalOptions
    }
  }

  this.resetIndex = function() {
    index = {
      all: [],
      groups: {}
    }
  }

  this.newInstance = function(config) {
    var instance = new ApiCache()

    if (config) {
      instance.options(config)
    }

    return instance
  }

  this.clone = function() {
    return this.newInstance(this.options())
  }

  // initialize index
  this.resetIndex()
}

module.exports = new ApiCache()
<|endoftext|>


<commit_before>'use strict';

// ------------------------------------------------------------------------------------------ Test Dependencies

var path = require('path');
var should = require('chai').should();
var nconf = require('nconf');
nconf.argv()
	 .env()
	 .file('local', { file: path.join(__dirname, '../../../local.json') })
	 .file({ file: path.join(__dirname, '../../../config.json') });
var settings = require('../../../lib/settings');

describe('Storage Settings View', function() {

	var sandbox;

	before(function (done) {
		settings.get(function(err, settings) {
			should.exist(settings);
			should.not.exist(err);
			sandbox = settings;
			done();
		});
	});

	beforeEach(function *() {
		yield browser.url(sandbox.baseUrl + '/settings/storage');
	});

	it('should have the window title based on user settings', function *() {

		var title = yield browser.getTitle()
		title.should.be.equal(sandbox.title);

	});

	it('should have the correct header title', function *() {

		var header = yield browser.getText('.tab-pane.active h1')
		header.should.be.equal('Storage');

	});

	it('should have a "Location" field with the current value based on user settings', function *() {

		var value = yield browser.getValue('select#StorageLocation');
		value.should.be.equal(sandbox.StorageLocation);

	});

	it('should have a "Retention" field with the current value based on user settings', function *() {

		var retention = yield browser.getValue('input#retention');
		retention.should.be.equal(sandbox.retention.toString());

		var retentionUnit = yield browser.getValue('select#retentionUnit');
		retentionUnit.should.be.equal(sandbox.retentionUnit);

	});

	it('should show all fields related to "Local file system" if the option is selected in the "Location" field', function *() {

		var value = yield browser.selectByVisibleText('select#StorageLocation', 'Local file system')
								 .waitForVisible('div#local')
								 .getValue('input#localstoragepath');
		value.should.be.equal(sandbox.localstoragepath);

	});

	it('should show all fields related to "Amazon S3" if the option is selected in the "Location" field', function *() {

		var value = yield browser.selectByVisibleText('select#StorageLocation', 'Amazon S3')
								 .waitForVisible('div#s3')
								 .getValue('input#S3AccessKeyId');
		value.should.be.equal(sandbox.S3AccessKeyId || '');

		var S3SecretAccessKey = yield browser.getValue('input#S3SecretAccessKey');
		S3SecretAccessKey.should.be.equal(sandbox.S3SecretAccessKey || '');

		var S3Bucket = yield browser.getValue('input#S3Bucket');
		S3Bucket.should.be.equal(sandbox.S3Bucket || '');

		var S3Region = yield browser.getValue('input#S3Region');
		S3Region.should.be.equal(sandbox.S3Region);

		var S3SSLEnabled = yield browser.isExisting('input#S3SSLEnabled:checked');
		if(sandbox.S3SSLEnabled) {
			S3SSLEnabled.should.equals(true);
		} else {
			S3SSLEnabled.should.equals(false);
		}
	});

	it('should be able to save the settings', function *() {

		var alert = yield browser.isExisting('.tab-pane.active .alert strong');
		alert.should.be.equal(false);

		var value = yield browser.submitForm('.tab-pane.active form')
								 .waitForExist('.tab-pane.active .alert strong', 5000)
								 .getText('.tab-pane.active .alert strong');
		value.should.be.equal('Success!');

	});

	it('should be able to alter and save the settings', function *() {

		var alert = yield browser.isExisting('.tab-pane.active .alert strong');
		alert.should.be.equal(false);

		var currentValue = sandbox.retention.toString();
		var newValue = currentValue + '2';

		var value = yield browser.setValue('input#retention', newValue)
								 .submitForm('.tab-pane.active form')
								 .waitForExist('.tab-pane.active .alert strong', 5000)
								 .getText('.tab-pane.active .alert strong');
		value.should.be.equal('Success!');

		var title = yield browser.getValue('input#retention');
		title.should.be.equal(newValue);

		value = yield browser.setValue('input#retention', currentValue)
							 .submitForm('.tab-pane.active form')
							 .waitForExist('.tab-pane.active .alert strong', 5000)
							 .getText('.tab-pane.active .alert strong');
		value.should.be.equal('Success!');

		var value = yield browser.getValue('input#retention');
		value.should.be.equal(currentValue);

	});
});<commit_msg>fixes #80 : Write UI tests for important views Small UI test refactoring<commit_after>'use strict';

// ------------------------------------------------------------------------------------------ Test Dependencies

var path = require('path');
var should = require('chai').should();
var nconf = require('nconf');
nconf.argv()
	 .env()
	 .file('local', { file: path.join(__dirname, '../../../local.json') })
	 .file({ file: path.join(__dirname, '../../../config.json') });
var settings = require('../../../lib/settings');

describe('Storage Settings View', function() {

	var sandbox;

	before(function (done) {
		settings.get(function(err, settings) {
			should.exist(settings);
			should.not.exist(err);
			sandbox = settings;
			done();
		});
	});

	beforeEach(function *() {
		yield browser.url(sandbox.baseUrl + '/settings/storage');
	});

	it('should have the window title based on user settings', function *() {

		var title = yield browser.getTitle()
		title.should.be.equal(sandbox.title);

	});

	it('should have the correct header title', function *() {

		var header = yield browser.getText('.tab-pane.active h1')
		header.should.be.equal('Storage');

	});

	it('should have a "Location" field with the current value based on user settings', function *() {

		var value = yield browser.getValue('select#StorageLocation');
		value.should.be.equal(sandbox.StorageLocation);

	});

	it('should have a "Retention" field with the current value based on user settings', function *() {

		var retention = yield browser.getValue('input#retention');
		retention.should.be.equal(sandbox.retention.toString());

		var retentionUnit = yield browser.getValue('select#retentionUnit');
		retentionUnit.should.be.equal(sandbox.retentionUnit);

	});

	it('should show all fields related to "Local file system" if the option is selected in the "Location" field', function *() {

		var value = yield browser.selectByVisibleText('select#StorageLocation', 'Local file system')
								 .waitForVisible('div#local')
								 .getValue('input#localstoragepath');
		value.should.be.equal(sandbox.localstoragepath);

	});

	it('should show all fields related to "Amazon S3" if the option is selected in the "Location" field', function *() {

		var value = yield browser.selectByVisibleText('select#StorageLocation', 'Amazon S3')
								 .waitForVisible('div#s3')
								 .getValue('input#S3AccessKeyId');
		value.should.be.equal(sandbox.S3AccessKeyId || '');

		var S3SecretAccessKey = yield browser.getValue('input#S3SecretAccessKey');
		S3SecretAccessKey.should.be.equal(sandbox.S3SecretAccessKey || '');

		var S3Bucket = yield browser.getValue('input#S3Bucket');
		S3Bucket.should.be.equal(sandbox.S3Bucket || '');

		var S3Region = yield browser.getValue('input#S3Region');
		S3Region.should.be.equal(sandbox.S3Region);

		var S3SSLEnabled = yield browser.isExisting('input#S3SSLEnabled:checked');
		if(sandbox.S3SSLEnabled) {
			S3SSLEnabled.should.equals(true);
		} else {
			S3SSLEnabled.should.equals(false);
		}
	});

	it('should be able to save the settings', function *() {

		var alert = yield browser.isExisting('.tab-pane.active .alert strong');
		alert.should.be.equal(false);

		var value = yield browser.submitForm('.tab-pane.active form')
								 .waitForExist('.tab-pane.active .alert strong', 5000)
								 .getText('.tab-pane.active .alert strong');
		value.should.be.equal('Success!');

	});

	it('should be able to alter and save the settings', function *() {

		var alert = yield browser.isExisting('.tab-pane.active .alert strong');
		alert.should.be.equal(false);

		var currentValue = sandbox.retention.toString();
		var newValue = currentValue + '2';

		var alert = yield browser.setValue('input#retention', newValue)
								 .submitForm('.tab-pane.active form')
								 .waitForExist('.tab-pane.active .alert strong', 5000)
								 .getText('.tab-pane.active .alert strong');
		alert.should.be.equal('Success!');

		var value = yield browser.getValue('input#retention');
		value.should.be.equal(newValue);

		alert = yield browser.setValue('input#retention', currentValue)
							 .submitForm('.tab-pane.active form')
							 .waitForExist('.tab-pane.active .alert strong', 5000)
							 .getText('.tab-pane.active .alert strong');
		alert.should.be.equal('Success!');

		value = yield browser.getValue('input#retention');
		value.should.be.equal(currentValue);

	});
});<|endoftext|>


<commit_before>/**
 * Module dependencies
 */
var assign = require('object-assign-deep');
var isEmpty = require('lodash.isempty');
var difference = require('lodash.difference');
var bemLinter = require('postcss-bem-linter');
var postcss = require('postcss');
var cssnano = require('cssnano');
var reporter = require('postcss-reporter');
var stylelint = require('stylelint');

/**
 * Module export
 */

module.exports = preprocessor;

/**
 * Default options to PostCSS plugins
 */

var defaults = {
  minify: false,
  lint: false,
  use: [
    'postcss-import',
    'postcss-custom-properties',
    'postcss-calc',
    'postcss-custom-media',
    'autoprefixer',
    'postcss-reporter'
  ],
  'postcss-import': {
    onImport: function(imported) {
      // Update the watch task with the list of imported files
      if (typeof global.watchCSS === 'function') {
        global.watchCSS(imported);
      }
    }
  },
  'postcss-reporter': {
    clearMessages: true
  },
  // http://cssnano.co/optimisations/
  cssnano: {
    calc: false,
    autoprefixer: false,
    mergeRules: false,
    safe: true
  }
};

/**
 * Process CSS
 *
 * @param {String} css
 * @returns {Promise}
 */

function preprocessor(css, options) {
  options = mergeOptions(options);

  var plugins = options.use.map(function(p) {
    var plugin = require(p);
    var settings = options[p];

    return settings ? plugin(settings) : plugin;
  });

  var processor = postcss(plugins);

  if (options.minify) {
    processor.use(cssnano(options.cssnano));
  }

  return processor.process(css, options.postcss);
}

/**
 * Merge options with defaults and set root
 *
 * @param {Object} options
 * @returns {Object} Merged options object
 */

function mergeOptions(options) {
  options = options || {};

  var merged = assign({}, defaults, options);

  // Set some core options
  if (merged.root) {
    merged['postcss-import'].root = merged.root;
  }

  // Call beforeLint function and pass processed css to bem-linter
  var beforeLint = merged.beforeLint;
  merged['postcss-import'].transform = function(css, filename) {
    if (typeof beforeLint === 'function') {
      css = beforeLint(css, filename, merged);
    }

    return lintImportedFiles(merged, css, filename).then(function(result) {
      return result.css;
    });
  };

  // Allow additional plugins to be merged with the defaults
  // but remove any duplicates so that it respects the new order
  if (!isEmpty(options.use)) {
    var plugins = difference(merged.use, options.use);
    merged.use = plugins.concat(options.use);
  }

  return merged;
}

/**
 * Returns a function to be used by postcss-import
 * Lint each imported component with postcss-bem-linter
 *
 * @param {Object} options
 * @returns {Function} Used by postcss-import transform
 */
function lintImportedFiles(options, css, filename) {
  var processor = postcss();

  if (options.lint) {
    processor.use(stylelint(options.stylelint));
  }

  processor
  .use(bemLinter(options['postcss-bem-linter']))
  .use(reporter(options['postcss-reporter']));

  return processor.process(css, {from: filename});
}
<commit_msg>Update comment<commit_after>/**
 * Module dependencies
 */
var assign = require('object-assign-deep');
var isEmpty = require('lodash.isempty');
var difference = require('lodash.difference');
var bemLinter = require('postcss-bem-linter');
var postcss = require('postcss');
var cssnano = require('cssnano');
var reporter = require('postcss-reporter');
var stylelint = require('stylelint');

/**
 * Module export
 */

module.exports = preprocessor;

/**
 * Default options to PostCSS plugins
 */

var defaults = {
  minify: false,
  lint: false,
  use: [
    'postcss-import',
    'postcss-custom-properties',
    'postcss-calc',
    'postcss-custom-media',
    'autoprefixer',
    'postcss-reporter'
  ],
  'postcss-import': {
    onImport: function(imported) {
      // Update the watch task with the list of imported files
      if (typeof global.watchCSS === 'function') {
        global.watchCSS(imported);
      }
    }
  },
  'postcss-reporter': {
    clearMessages: true
  },
  // http://cssnano.co/optimisations/
  cssnano: {
    calc: false,
    autoprefixer: false,
    mergeRules: false,
    safe: true
  }
};

/**
 * Process CSS
 *
 * @param {String} css
 * @returns {Promise}
 */

function preprocessor(css, options) {
  options = mergeOptions(options);

  var plugins = options.use.map(function(p) {
    var plugin = require(p);
    var settings = options[p];

    return settings ? plugin(settings) : plugin;
  });

  var processor = postcss(plugins);

  if (options.minify) {
    processor.use(cssnano(options.cssnano));
  }

  return processor.process(css, options.postcss);
}

/**
 * Merge options with defaults and set root
 *
 * @param {Object} options
 * @returns {Object} Merged options object
 */

function mergeOptions(options) {
  options = options || {};

  var merged = assign({}, defaults, options);

  // Set some core options
  if (merged.root) {
    merged['postcss-import'].root = merged.root;
  }

  // Call beforeLint function and pass processed css to bem-linter
  var beforeLint = merged.beforeLint;
  merged['postcss-import'].transform = function(css, filename) {
    if (typeof beforeLint === 'function') {
      css = beforeLint(css, filename, merged);
    }

    return lintImportedFiles(merged, css, filename).then(function(result) {
      return result.css;
    });
  };

  // Allow additional plugins to be merged with the defaults
  // but remove any duplicates so that it respects the new order
  if (!isEmpty(options.use)) {
    var plugins = difference(merged.use, options.use);
    merged.use = plugins.concat(options.use);
  }

  return merged;
}

/**
 * Lint each imported component with postcss-bem-linter
 * and stylelint
 *
 * @param {Object} options
 * @returns {Promise} Used by postcss-import transform
 */
function lintImportedFiles(options, css, filename) {
  var processor = postcss();

  if (options.lint) {
    processor.use(stylelint(options.stylelint));
  }

  processor
  .use(bemLinter(options['postcss-bem-linter']))
  .use(reporter(options['postcss-reporter']));

  return processor.process(css, {from: filename});
}
<|endoftext|>


<commit_before>/**
 * @since 0.0.1
 * @copyright 2015 Spatial Vision, Inc. http://spatialvision.com.au
 * @license The MIT License
 * @author Spatial Vision
 * @version 0.1.0
 */

'use strict';

angular.module('farmbuild.soilSampleImporter')
    .factory('paddockSelector',
    function ($log, farmdata, soilSampleImporter, soilClassificationTypes,
              collections, paddockSelectionValidator) {
        $log.info("paddockSelector ");

        var paddockSelector = {},
            _paddocks = [],
            _types = soilClassificationTypes.toArray();

        paddockSelector.createNew = function(myFarmData, columnHeaders, rows, paddockColumnIndex) {

            if(!paddockSelectionValidator.validateCreateNew(columnHeaders, rows)) {
                return undefined;
            }

            _paddocks = myFarmData.paddocks;
            paddockSelector.paddocks = _paddocks;

            var result= {
                "dateLastUpdated": new Date(),
                "results": {
                    "columnHeaders": columnHeaders,
                    "rows": rows
                },
                "selected": [],
                "classificationColumnDictionary": {},
                "paddockRowDictionary": {},
                "paddockNameColumn":paddockColumnIndex
            };
            return result;
        }

        /*paddockSelector.load = function() {
            var test= {
                "dateLastUpdated": "2015-05-25T02:23:51",
                "columnHeaders" : [
                    "Paddock",
                    "SampleId",
                    "SampleName",
                    "Ph",
                    "Saturation"
                ],
                "rows" : [
                    ['P1','123', 'Front Barn', 1,2,3,4,5,6,7],
                    ['P2','456', 'Left Barn', 1,2,3,4,5,6,7]
                ],
                "classificationColumnDictionary": {},
                "paddockRowDictionary": {},
                "paddockColumnIndex":0
            };
            return test;
        }*/

        paddockSelector.save = function(myFarmData, paddockSelection) {
            $log.info(JSON.stringify(paddockSelection));

            if (!paddockSelectionValidator.validatePaddockSelection(paddockSelection)) {
                return undefined;
            }

            return soilSampleImporter.toFarmData(myFarmData, paddockSelection);

        }

        /**
         *
         * @param paddockSelection
         * @param paddock
         * @param rowIndex
         */
        paddockSelector.connectRow =  function(paddockSelection, paddock, rowIndex) {
            if (!paddockSelection.paddockRowDictionary.hasOwnProperty(paddock.name)) {
                paddockSelection.paddockRowDictionary[paddock.name]= [];
            }
            collections.add(paddockSelection.paddockRowDictionary[paddock.name], rowIndex);

        }

        paddockSelector.disconnectRow =  function(paddockSelection, paddock, index) {
            if (paddockSelection.paddockRowDictionary.hasOwnProperty(paddock.name)) {
                collections.remove(paddockSelection.paddockRowDictionary[paddock.name], index);
            }
        }

        paddockSelector.resetPaddockRowDictionary =  function(paddockSelection) {
            paddockSelection.paddockRowDictionary = {};

            return paddockSelection;
        }

        paddockSelector.selectColumn =  function(paddockSelection, value) {
            collections.add(paddockSelection.selected, value);
        }

        paddockSelector.deselectColumn =  function(paddockSelection, value) {
            collections.remove(paddockSelection.selected, value)
        }

        /**
         * Add classification for column with given index
         * @param paddockSelection
         * @param classificationType
         * @param index
         */
        paddockSelector.classifyColumn =  function(paddockSelection, classificationType, index) {
            paddockSelection.classificationColumnDictionary[classificationType.name] = index;
            $log.info("paddockSelection "+JSON.stringify(paddockSelection));
            this.selectColumn(paddockSelection, index);
        }

        /**
         * remove classification for column with given index
         * @param paddockSelection
         * @param classificationType
         * @param index
         */
        paddockSelector.declassifyColumn =  function(paddockSelection, classificationType, index) {
            this.deselectColumn(paddockSelection, index);
            delete paddockSelection.classificationColumnDictionary[classificationType.name];
        }

        paddockSelector.types = _types;
        paddockSelector.paddocks = _paddocks;

        return paddockSelector;
    });
<commit_msg>fill in paddock name in the results.row<commit_after>/**
 * @since 0.0.1
 * @copyright 2015 Spatial Vision, Inc. http://spatialvision.com.au
 * @license The MIT License
 * @author Spatial Vision
 * @version 0.1.0
 */

'use strict';

angular.module('farmbuild.soilSampleImporter')
    .factory('paddockSelector',
    function ($log, farmdata, soilSampleImporter, soilClassificationTypes,
              collections, paddockSelectionValidator) {
        $log.info("paddockSelector ");

        var paddockSelector = {},
            _paddocks = [],
            _types = soilClassificationTypes.toArray();

        paddockSelector.createNew = function(myFarmData, columnHeaders, rows, paddockColumnIndex) {

            if(!paddockSelectionValidator.validateCreateNew(columnHeaders, rows)) {
                return undefined;
            }

            _paddocks = myFarmData.paddocks;
            paddockSelector.paddocks = _paddocks;

            var result= {
                "dateLastUpdated": new Date(),
                "results": {
                    "columnHeaders": columnHeaders,
                    "rows": rows
                },
                "selected": [],
                "classificationColumnDictionary": {},
                "paddockRowDictionary": {},
                "paddockNameColumn":paddockColumnIndex
            };
            return result;
        }

        /*paddockSelector.load = function() {
            var test= {
                "dateLastUpdated": "2015-05-25T02:23:51",
                "columnHeaders" : [
                    "Paddock",
                    "SampleId",
                    "SampleName",
                    "Ph",
                    "Saturation"
                ],
                "rows" : [
                    ['P1','123', 'Front Barn', 1,2,3,4,5,6,7],
                    ['P2','456', 'Left Barn', 1,2,3,4,5,6,7]
                ],
                "classificationColumnDictionary": {},
                "paddockRowDictionary": {},
                "paddockColumnIndex":0
            };
            return test;
        }*/

        paddockSelector.save = function(myFarmData, paddockSelection) {
            $log.info(JSON.stringify(paddockSelection));

            if (!paddockSelectionValidator.validatePaddockSelection(paddockSelection)) {
                return undefined;
            }

            for (var key in paddockSelection.paddockRowDictionary) {
                for(var i=0; i<paddockSelection.paddockRowDictionary[key].length; i++) {
                    var rowIndex = paddockSelection.paddockRowDictionary[key][i];
                    paddockSelection.results.rows[rowIndex][paddockSelection.paddockNameColumn] = key;
                }
            }
            $log.info(JSON.stringify(paddockSelection));
            return soilSampleImporter.toFarmData(myFarmData, paddockSelection);

        }

        /**
         *
         * @param paddockSelection
         * @param paddock
         * @param rowIndex
         */
        paddockSelector.connectRow =  function(paddockSelection, paddock, rowIndex) {
            if (!paddockSelection.paddockRowDictionary.hasOwnProperty(paddock.name)) {
                paddockSelection.paddockRowDictionary[paddock.name]= [];
            }
            collections.add(paddockSelection.paddockRowDictionary[paddock.name], rowIndex);

        }

        paddockSelector.disconnectRow =  function(paddockSelection, paddock, index) {
            if (paddockSelection.paddockRowDictionary.hasOwnProperty(paddock.name)) {
                collections.remove(paddockSelection.paddockRowDictionary[paddock.name], index);
            }
        }

        paddockSelector.resetPaddockRowDictionary =  function(paddockSelection) {
            paddockSelection.paddockRowDictionary = {};

            return paddockSelection;
        }

        paddockSelector.selectColumn =  function(paddockSelection, value) {
            collections.add(paddockSelection.selected, value);
        }

        paddockSelector.deselectColumn =  function(paddockSelection, value) {
            collections.remove(paddockSelection.selected, value)
        }

        /**
         * Add classification for column with given index
         * @param paddockSelection
         * @param classificationType
         * @param index
         */
        paddockSelector.classifyColumn =  function(paddockSelection, classificationType, index) {
            paddockSelection.classificationColumnDictionary[classificationType.name] = index;
            $log.info("paddockSelection "+JSON.stringify(paddockSelection));
            this.selectColumn(paddockSelection, index);
        }

        /**
         * remove classification for column with given index
         * @param paddockSelection
         * @param classificationType
         * @param index
         */
        paddockSelector.declassifyColumn =  function(paddockSelection, classificationType, index) {
            this.deselectColumn(paddockSelection, index);
            delete paddockSelection.classificationColumnDictionary[classificationType.name];
        }

        paddockSelector.types = _types;
        paddockSelector.paddocks = _paddocks;

        return paddockSelector;
    });
<|endoftext|>


<commit_before>define('parsley/validator_registry', [
  'parsley/defaults'
], function (ParsleyDefaults) {

  var ParsleyValidatorRegistry = function (validators, catalog) {
    this.__class__ = 'ParsleyValidatorRegistry';

    // Default Parsley locale is en
    this.locale = 'en';

    this.init(validators || {}, catalog || {});
  };

  var typeRegexes =  {
    email: /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))$/i,

    number: /^-?(?:\d+|\d{1,3}(?:,\d{3})+)?(?:\.\d+)?$/,

    integer: /^-?\d+$/,

    digits: /^\d+$/,

    alphanum: /^\w+$/i,

    url: new RegExp(
        "^" +
          // protocol identifier
          "(?:(?:https?|ftp)://)?" + // ** mod: make scheme optional
          // user:pass authentication
          "(?:\\S+(?::\\S*)?@)?" +
          "(?:" +
            // IP address exclusion
            // private & local networks
            // "(?!(?:10|127)(?:\\.\\d{1,3}){3})" +   // ** mod: allow local networks
            // "(?!(?:169\\.254|192\\.168)(?:\\.\\d{1,3}){2})" +  // ** mod: allow local networks
            // "(?!172\\.(?:1[6-9]|2\\d|3[0-1])(?:\\.\\d{1,3}){2})" +  // ** mod: allow local networks
            // IP address dotted notation octets
            // excludes loopback network 0.0.0.0
            // excludes reserved space >= 224.0.0.0
            // excludes network & broacast addresses
            // (first & last IP address of each class)
            "(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])" +
            "(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}" +
            "(?:\\.(?:[1-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))" +
          "|" +
            // host name
            "(?:(?:[a-z\\u00a1-\\uffff0-9]-*)*[a-z\\u00a1-\\uffff0-9]+)" +
            // domain name
            "(?:\\.(?:[a-z\\u00a1-\\uffff0-9]-*)*[a-z\\u00a1-\\uffff0-9]+)*" +
            // TLD identifier
            "(?:\\.(?:[a-z\\u00a1-\\uffff]{2,}))" +
          ")" +
          // port number
          "(?::\\d{2,5})?" +
          // resource path
          "(?:/\\S*)?" +
        "$", 'i'
      )
  };
  typeRegexes.range = typeRegexes.number;

  ParsleyValidatorRegistry.prototype = {
    init: function (validators, catalog) {
      this.catalog = catalog;
      // Copy prototype's validators:
      this.validators = $.extend({}, this.validators);

      for (var name in validators)
        this.addValidator(name, validators[name].fn, validators[name].priority);

      window.Parsley.trigger('parsley:validator:init');
    },

    // Set new messages locale if we have dictionary loaded in ParsleyConfig.i18n
    setLocale: function (locale) {
      if ('undefined' === typeof this.catalog[locale])
        throw new Error(locale + ' is not available in the catalog');

      this.locale = locale;

      return this;
    },

    // Add a new messages catalog for a given locale. Set locale for this catalog if set === `true`
    addCatalog: function (locale, messages, set) {
      if ('object' === typeof messages)
        this.catalog[locale] = messages;

      if (true === set)
        return this.setLocale(locale);

      return this;
    },

    // Add a specific message for a given constraint in a given locale
    addMessage: function (locale, name, message) {
      if ('undefined' === typeof this.catalog[locale])
        this.catalog[locale] = {};

      this.catalog[locale][name.toLowerCase()] = message;

      return this;
    },

    // Add a new validator
    addValidator: function (name, fn, priority) {
      if (this.validators[name])
        ParsleyUtils.warn('Validator "' + name + '" is already defined.');
      else if (ParsleyDefaults.hasOwnProperty(name)) {
        ParsleyUtils.warn('"' + name + '" is a restricted keyword and is not a valid validator name.');
        return;
      };
      return this._setValidator(name, fn, priority);
    },

    updateValidator: function (name, fn, priority) {
      if (!this.validators[name]) {
        ParsleyUtils.warn('Validator "' + name + '" is not already defined.');
        return this.addValidator(name, fn, priority);
      }
      return this._setValidator(name, fn, priority);
    },

    removeValidator: function (name) {
      if (!this.validators[name])
        ParsleyUtils.warn('Validator "' + name + '" is not defined.');

      delete this.validators[name];

      return this;
    },

    _setValidator: function (name, fn, priority) {
      this.validators[name] = {
        fn: fn,
        priority: priority
      };

      return this;
    },

    getErrorMessage: function (constraint) {
      var message;

      // Type constraints are a bit different, we have to match their requirements too to find right error message
      if ('type' === constraint.name) {
        var typeMessages = this.catalog[this.locale][constraint.name] || {};
        message = typeMessages[constraint.requirements];
      } else
        message = this.formatMessage(this.catalog[this.locale][constraint.name], constraint.requirements);

      return message || this.catalog[this.locale].defaultMessage || this.catalog.en.defaultMessage;
    },

    // Kind of light `sprintf()` implementation
    formatMessage: function (string, parameters) {
      if ('object' === typeof parameters) {
        for (var i in parameters)
          string = this.formatMessage(string, parameters[i]);

        return string;
      }

      return 'string' === typeof string ? string.replace(new RegExp('%s', 'i'), parameters) : '';
    },

    // Here is the Parsley default validators list.
    // A validator is an object with the following key values:
    //  - priority: an integer
    //  - requirement: 'string' (default), 'integer', 'number', 'regexp' or an Array of these
    //  - validateString, validateMultiple, validateNumber: functions returning `true`, `false` or a promise
    // Alternatively, a validator can be a function that returns such an object
    //
    validators: {
      notblank: {
        validateString: function(value) {
          return /\S/.test(value);
        },
        priority: 2
      },
      required: {
        validateMultiple: function(values) {
          return values.length > 0;
        },
        validateString: function(value) {
          return /\S/.test(value);
        },
        priority: 512
      },
      type: {
        validateString: function(value, type) {
          var regex = typeRegexes[type];
          if (!regex)
            throw new Error('validator type `' + type + '` is not supported');
          return regex.test(value);
        },
        priority: 256
      },
      pattern: {
        validateString: function(value, regexp) {
          return regexp.test(value);
        },
        requirementType: 'regexp',
        priority: 64
      },
      minlength: {
        validateString: function (value, requirement) {
          return value.length >= requirement;
        },
        requirementType: 'integer',
        priority: 30
      },
      maxlength: {
        validateString: function (value, requirement) {
          return value.length <= requirement;
        },
        requirementType: 'integer',
        priority: 30
      },
      length: {
        validateString: function (value, min, max) {
          return value.length >= min && value.length <= max;
        },
        requirementType: ['integer', 'integer'],
        priority: 30
      },
      mincheck: {
        validateMultiple: function (values, requirement) {
          return values.length >= requirement;
        },
        requirementType: 'integer',
        priority: 30
      },
      maxcheck: {
        validateMultiple: function (values, requirement) {
          return values.length <= requirement;
        },
        requirementType: 'integer',
        priority: 30
      },
      check: {
        validateMultiple: function (values, min, max) {
          return values.length >= min && values.length <= max;
        },
        requirementType: ['integer', 'integer'],
        priority: 30
      },
      min: {
        validateNumber: function (value, requirement) {
          return value >= requirement;
        },
        requirementType: 'number',
        priority: 30
      },
      max: {
        validateNumber: function (value, requirement) {
          return value <= requirement;
        },
        requirementType: 'number',
        priority: 30
      },
      range: {
        validateNumber: function (value, min, max) {
          return value >= min && value <= max;
        },
        requirementType: ['number', 'number'],
        priority: 30
      },
      equalto: {
        validateString: function (value, refOrValue) {
          var $reference = $(refOrValue);
          if ($reference.length)
            return value === $reference.val();
          else
            return value === refOrValue;
        },
        priority: 256
      }
    }
  };

  return ParsleyValidatorRegistry;
});
<commit_msg>Tweak validator calling<commit_after>define('parsley/validator_registry', [
  'parsley/defaults',
  'parsley/validator'
], function (ParsleyDefaults, ParsleyValidator) {

  var ParsleyValidatorRegistry = function (validators, catalog) {
    this.__class__ = 'ParsleyValidatorRegistry';

    // Default Parsley locale is en
    this.locale = 'en';

    this.init(validators || {}, catalog || {});
  };

  var typeRegexes =  {
    email: /^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))$/i,

    number: /^-?(?:\d+|\d{1,3}(?:,\d{3})+)?(?:\.\d+)?$/,

    integer: /^-?\d+$/,

    digits: /^\d+$/,

    alphanum: /^\w+$/i,

    url: new RegExp(
        "^" +
          // protocol identifier
          "(?:(?:https?|ftp)://)?" + // ** mod: make scheme optional
          // user:pass authentication
          "(?:\\S+(?::\\S*)?@)?" +
          "(?:" +
            // IP address exclusion
            // private & local networks
            // "(?!(?:10|127)(?:\\.\\d{1,3}){3})" +   // ** mod: allow local networks
            // "(?!(?:169\\.254|192\\.168)(?:\\.\\d{1,3}){2})" +  // ** mod: allow local networks
            // "(?!172\\.(?:1[6-9]|2\\d|3[0-1])(?:\\.\\d{1,3}){2})" +  // ** mod: allow local networks
            // IP address dotted notation octets
            // excludes loopback network 0.0.0.0
            // excludes reserved space >= 224.0.0.0
            // excludes network & broacast addresses
            // (first & last IP address of each class)
            "(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])" +
            "(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}" +
            "(?:\\.(?:[1-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))" +
          "|" +
            // host name
            "(?:(?:[a-z\\u00a1-\\uffff0-9]-*)*[a-z\\u00a1-\\uffff0-9]+)" +
            // domain name
            "(?:\\.(?:[a-z\\u00a1-\\uffff0-9]-*)*[a-z\\u00a1-\\uffff0-9]+)*" +
            // TLD identifier
            "(?:\\.(?:[a-z\\u00a1-\\uffff]{2,}))" +
          ")" +
          // port number
          "(?::\\d{2,5})?" +
          // resource path
          "(?:/\\S*)?" +
        "$", 'i'
      )
  };
  typeRegexes.range = typeRegexes.number;

  ParsleyValidatorRegistry.prototype = {
    init: function (validators, catalog) {
      this.catalog = catalog;
      // Copy prototype's validators:
      this.validators = $.extend({}, this.validators);

      for (var name in validators)
        this.addValidator(name, validators[name].fn, validators[name].priority);

      window.Parsley.trigger('parsley:validator:init');
    },

    // Set new messages locale if we have dictionary loaded in ParsleyConfig.i18n
    setLocale: function (locale) {
      if ('undefined' === typeof this.catalog[locale])
        throw new Error(locale + ' is not available in the catalog');

      this.locale = locale;

      return this;
    },

    // Add a new messages catalog for a given locale. Set locale for this catalog if set === `true`
    addCatalog: function (locale, messages, set) {
      if ('object' === typeof messages)
        this.catalog[locale] = messages;

      if (true === set)
        return this.setLocale(locale);

      return this;
    },

    // Add a specific message for a given constraint in a given locale
    addMessage: function (locale, name, message) {
      if ('undefined' === typeof this.catalog[locale])
        this.catalog[locale] = {};

      this.catalog[locale][name.toLowerCase()] = message;

      return this;
    },

    // Add a new validator
    addValidator: function (name, arg1, arg2) {
      if (this.validators[name])
        ParsleyUtils.warn('Validator "' + name + '" is already defined.');
      else if (ParsleyDefaults.hasOwnProperty(name)) {
        ParsleyUtils.warn('"' + name + '" is a restricted keyword and is not a valid validator name.');
        return;
      };
      return this._setValidator.apply(this, arguments);
    },

    updateValidator: function (name, arg1, arg2) {
      if (!this.validators[name]) {
        ParsleyUtils.warn('Validator "' + name + '" is not already defined.');
        return this.addValidator.apply(this, arguments);
      }
      return this._setValidator(this, arguments);
    },

    removeValidator: function (name) {
      if (!this.validators[name])
        ParsleyUtils.warn('Validator "' + name + '" is not defined.');

      delete this.validators[name];

      return this;
    },

    _setValidator: function (name, validator, priority) {
      if ('object' !== typeof validator) {
        // Old style validator, with `fn` and `priority`
        validator = {
          fn: validator,
          priority: priority
        }
      };
      if (!validator.validate) {
        validator = new ParsleyValidator(validator);
      };
      this.validators[name] = validator;

      return this;
    },

    getErrorMessage: function (constraint) {
      var message;

      // Type constraints are a bit different, we have to match their requirements too to find right error message
      if ('type' === constraint.name) {
        var typeMessages = this.catalog[this.locale][constraint.name] || {};
        message = typeMessages[constraint.requirements];
      } else
        message = this.formatMessage(this.catalog[this.locale][constraint.name], constraint.requirements);

      return message || this.catalog[this.locale].defaultMessage || this.catalog.en.defaultMessage;
    },

    // Kind of light `sprintf()` implementation
    formatMessage: function (string, parameters) {
      if ('object' === typeof parameters) {
        for (var i in parameters)
          string = this.formatMessage(string, parameters[i]);

        return string;
      }

      return 'string' === typeof string ? string.replace(new RegExp('%s', 'i'), parameters) : '';
    },

    // Here is the Parsley default validators list.
    // A validator is an object with the following key values:
    //  - priority: an integer
    //  - requirement: 'string' (default), 'integer', 'number', 'regexp' or an Array of these
    //  - validateString, validateMultiple, validateNumber: functions returning `true`, `false` or a promise
    // Alternatively, a validator can be a function that returns such an object
    //
    validators: {
      notblank: {
        validateString: function(value) {
          return /\S/.test(value);
        },
        priority: 2
      },
      required: {
        validateMultiple: function(values) {
          return values.length > 0;
        },
        validateString: function(value) {
          return /\S/.test(value);
        },
        priority: 512
      },
      type: {
        validateString: function(value, type) {
          var regex = typeRegexes[type];
          if (!regex)
            throw new Error('validator type `' + type + '` is not supported');
          return regex.test(value);
        },
        priority: 256
      },
      pattern: {
        validateString: function(value, regexp) {
          return regexp.test(value);
        },
        requirementType: 'regexp',
        priority: 64
      },
      minlength: {
        validateString: function (value, requirement) {
          return value.length >= requirement;
        },
        requirementType: 'integer',
        priority: 30
      },
      maxlength: {
        validateString: function (value, requirement) {
          return value.length <= requirement;
        },
        requirementType: 'integer',
        priority: 30
      },
      length: {
        validateString: function (value, min, max) {
          return value.length >= min && value.length <= max;
        },
        requirementType: ['integer', 'integer'],
        priority: 30
      },
      mincheck: {
        validateMultiple: function (values, requirement) {
          return values.length >= requirement;
        },
        requirementType: 'integer',
        priority: 30
      },
      maxcheck: {
        validateMultiple: function (values, requirement) {
          return values.length <= requirement;
        },
        requirementType: 'integer',
        priority: 30
      },
      check: {
        validateMultiple: function (values, min, max) {
          return values.length >= min && values.length <= max;
        },
        requirementType: ['integer', 'integer'],
        priority: 30
      },
      min: {
        validateNumber: function (value, requirement) {
          return value >= requirement;
        },
        requirementType: 'number',
        priority: 30
      },
      max: {
        validateNumber: function (value, requirement) {
          return value <= requirement;
        },
        requirementType: 'number',
        priority: 30
      },
      range: {
        validateNumber: function (value, min, max) {
          return value >= min && value <= max;
        },
        requirementType: ['number', 'number'],
        priority: 30
      },
      equalto: {
        validateString: function (value, refOrValue) {
          var $reference = $(refOrValue);
          if ($reference.length)
            return value === $reference.val();
          else
            return value === refOrValue;
        },
        priority: 256
      }
    }
  };

  return ParsleyValidatorRegistry;
});
<|endoftext|>


<commit_before>define(["plask", "pex/core/Context", "pex/core/Texture2D"], function(plask, Context, Texture2D) {
  function HTMLCanvasRenderer(width, height) {
    this.gl = Context.currentContext.gl;
    this.canvas = document.createElement("canvas");
    this.tex = Texture2D.create(width, height);
    this.canvas.width = width;
    this.canvas.height = height;
    this.ctx = this.canvas.getContext("2d");
  }

  HTMLCanvasRenderer.prototype.draw = function(items) {
    var ctx = this.ctx;
    ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);

    var dy;
    var dx;
    var w = 160;

    dy = 10;

    ctx.font = "10px Monaco";

    for(var i=0; i<items.length; i++) {
      var e = items[i];
      var dx = 10;
      var eh = 20;

      if (e.type == "slider") eh = 34;
      if (e.type == "button") eh = 24;
      if (e.type == "texture2D") eh = 24 + e.texture.height * w / e.texture.width;

      ctx.fillStyle = "rgba(0, 0, 0, 0.56)";
      ctx.fillRect(dx, dy, w, eh - 2);

      if (e.type == "slider") {
        ctx.fillStyle = "rgba(150, 150, 150, 1)";
        ctx.fillRect(dx + 3, dy + 18, w - 3 - 3, eh - 5 - 18);

        ctx.fillStyle = "rgba(255, 255, 0, 1)";
        ctx.fillRect(dx + 3, dy + 18, (w - 3 - 3)*e.getNormalizedValue(), eh - 5 - 18);

        e.activeArea.set(dx + 3, dy + 18, w - 3 - 3, eh - 5 - 18);

        ctx.fillStyle = "rgba(255, 255, 255, 1)";
        ctx.fillText(items[i].title + " : " + e.getValue(), dx + 5, dy + 13);
      }
      else if (e.type == "button"){
        ctx.fillStyle = e.active ? "rgba(255, 255, 0, 1)" : "rgba(150, 150, 150, 1)";
        ctx.fillRect(dx + 3, dy + 3, w - 3 - 3, eh - 5 - 3);

        e.activeArea.set(dx + 3, dy + 3, w - 3 - 3, eh - 5 - 3);

        ctx.fillStyle = e.active ? "rgba(100, 100, 100, 1)" : "rgba(255, 255, 255, 1)";
        ctx.fillText(items[i].title, dx + 5, dy + 15);
      }
      else if (e.type == "toggle"){
        var on = e.contextObject[e.attributeName];

        ctx.fillStyle = on ? "rgba(255, 255, 0, 1)" : "rgba(150, 150, 150, 1)";
        ctx.fillRect(dx + 3, dy + 3, eh - 5 - 3, eh - 5 - 3);

        e.activeArea.set(dx + 3, dy + 3, eh - 5 - 3, eh - 5 - 3);

        ctx.fillStyle = "rgba(255, 255, 255, 1)";
        ctx.fillText(items[i].title, dx + 5 + eh - 5, dy + 12);
      }
      else if (e.type == "radiolist"){
        //console.error("radio list hasn't been implemented yet");
      }
      else if (e.type == "texture2D") {
        //canvas.drawText(this.fontPaint, e.title, dx + 3, dy + 13);
        ctx.fillStyle = "rgba(255, 255, 255, 1)";
        ctx.fillText(items[i].title, dx + 5, dy + 15);
        e.activeArea.set(dx + 3, dy + 18, w - 3 - 3, eh - 5 - 18);
      }
      else {
        ctx.fillStyle = "rgba(255, 255, 255, 1)";
        ctx.fillText(items[i].title, dx + 5, dy + 13);
      }

      //      else if (e.type == "toggle") {
      //        var on = e.contextObject[e.attributeName];
      //        var toggleColor = on ? this.controlHighlightPaint : this.controlBgPaint;
      //        canvas.drawRect(toggleColor, dx + 3, dy + 3, dx + eh - 5, dy + eh - 5);
      //        e.activeArea.set(dx + 3, dy + 3, dx + eh - 5, eh - 5);
      //        canvas.drawText(this.fontPaint, items[i].title, dx + 5 + eh - 5, dy + 13);
      //      }

      dy += eh;
    }

    this.updateTexture();
  }

  HTMLCanvasRenderer.prototype.getTexture = function() {
    return this.tex;
  }

  HTMLCanvasRenderer.prototype.updateTexture = function() {
    var gl = this.gl;
    this.tex.bind();
    gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, this.canvas);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
    gl.bindTexture(gl.TEXTURE_2D, null);
  }

  return HTMLCanvasRenderer;
});
<commit_msg>Added radio list to html gui<commit_after>define(["plask", "pex/core/Context", "pex/core/Texture2D"], function(plask, Context, Texture2D) {
  function HTMLCanvasRenderer(width, height) {
    this.gl = Context.currentContext.gl;
    this.canvas = document.createElement("canvas");
    this.tex = Texture2D.create(width, height);
    this.canvas.width = width;
    this.canvas.height = height;
    this.ctx = this.canvas.getContext("2d");
  }

  HTMLCanvasRenderer.prototype.draw = function(items) {
    var ctx = this.ctx;
    ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
    ctx.font = "10px Monaco";

    var dy = 10;
    var dx = 10;
    var w = 160;
    for(var i=0; i<items.length; i++) {
      var e = items[i];
      if (e.px && e.px) {
        dx = e.px;
        dy = e.py;
      }
      var eh = 20;

      if (e.type == "slider") eh = 34;
      if (e.type == "button") eh = 24;
      if (e.type == "texture2D") eh = 24 + e.texture.height * w / e.texture.width;

      ctx.fillStyle = "rgba(0, 0, 0, 0.56)";
      ctx.fillRect(dx, dy, w, eh - 2);

      if (e.type == "slider") {
        ctx.fillStyle = "rgba(150, 150, 150, 1)";
        ctx.fillRect(dx + 3, dy + 18, w - 3 - 3, eh - 5 - 18);

        ctx.fillStyle = "rgba(255, 255, 0, 1)";
        ctx.fillRect(dx + 3, dy + 18, (w - 3 - 3)*e.getNormalizedValue(), eh - 5 - 18);

        e.activeArea.set(dx + 3, dy + 18, w - 3 - 3, eh - 5 - 18);

        ctx.fillStyle = "rgba(255, 255, 255, 1)";
        ctx.fillText(items[i].title + " : " + e.getValue(), dx + 5, dy + 13);
      }
      else if (e.type == "button"){
        ctx.fillStyle = e.active ? "rgba(255, 255, 0, 1)" : "rgba(150, 150, 150, 1)";
        ctx.fillRect(dx + 3, dy + 3, w - 3 - 3, eh - 5 - 3);

        e.activeArea.set(dx + 3, dy + 3, w - 3 - 3, eh - 5 - 3);

        ctx.fillStyle = e.active ? "rgba(100, 100, 100, 1)" : "rgba(255, 255, 255, 1)";
        ctx.fillText(items[i].title, dx + 5, dy + 15);
      }
      else if (e.type == "toggle"){
        var on = e.contextObject[e.attributeName];

        ctx.fillStyle = on ? "rgba(255, 255, 0, 1)" : "rgba(150, 150, 150, 1)";
        ctx.fillRect(dx + 3, dy + 3, eh - 5 - 3, eh - 5 - 3);
        e.activeArea.set(dx + 3, dy + 3, eh - 5 - 3, eh - 5 - 3);

        ctx.fillStyle = "rgba(255, 255, 255, 1)";
        ctx.fillText(items[i].title, dx + 5 + eh - 5, dy + 12);
      }
      else if (e.type == "radiolist") {
        ctx.fillStyle = "rgba(255, 255, 255, 1)";
        ctx.fillText(e.title, dx + 5, dy + 13);
        for(var j=0; j<e.items.length; j++) {
          var item = e.items[j];
          var on = (e.contextObject[e.attributeName] == item.value);
          ctx.fillStyle = on ? "rgba(255, 255, 0, 1)" : "rgba(150, 150, 150, 1)";
          ctx.fillRect(dx + 3, eh + j*eh + dy + 3, eh - 5 - 3, eh - 5 - 3);
          ctx.fillStyle = "rgba(255, 255, 255, 1)";
          ctx.fillText(item.name, dx + 5 + eh - 5, eh + j*eh + dy + 13);
        }
        e.activeArea.set(dx + 3, eh + dy + 3, eh - 5, e.items.length * eh - 5);
        eh = eh + e.items.length * eh;
      }
      else if (e.type == "texture2D") {
        ctx.fillStyle = "rgba(255, 255, 255, 1)";
        ctx.fillText(items[i].title, dx + 5, dy + 15);
        e.activeArea.set(dx + 3, dy + 18, w - 3 - 3, eh - 5 - 18);
      }
      else {
        ctx.fillStyle = "rgba(255, 255, 255, 1)";
        ctx.fillText(items[i].title, dx + 5, dy + 13);
      }

      dy += eh;
    }

    this.updateTexture();
  }

  HTMLCanvasRenderer.prototype.getTexture = function() {
    return this.tex;
  }

  HTMLCanvasRenderer.prototype.updateTexture = function() {
    var gl = this.gl;
    this.tex.bind();
    gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, this.canvas);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
    gl.bindTexture(gl.TEXTURE_2D, null);
  }

  return HTMLCanvasRenderer;
});
<|endoftext|>


<commit_before>import React from 'react'
import { findDOMNode } from 'react-dom'

import { changeEvent } from "../controllers/values";
import { resultsEvent } from "../controllers/pipeline";

const RIGHT_ARROW_KEYCODE = 39
const TAB_KEYCODE = 9
const RETURN_KEYCODE = 13

class AutocompleteInput extends React.Component {
  constructor(props) {
    super(props)
    this.setText = this.setText.bind(this);
    this.valuesUpdated = this.valuesUpdated.bind(this);
    this.getState = this.getState.bind(this);
    const qParam = props.qParam || "q";
    const qOverrideParam = props.qOverrideParam || "q.override";
    this.state = {
      ...this.getState(props.values, props.pipeline, qParam),
      qParam,
      qOverrideParam,
    };
  }

  getState(values, pipeline, qParam) {
    const text = values.get()[qParam] || "";
    const responseValues = pipeline.getResponseValues();
    const completion = responseValues ? (responseValues[qParam] || "") : "";
    return { text, completion };
  }

  componentDidMount() {
    if (this.props.focus) {
      findDOMNode(this.refs.searchInput).focus()
    }

    this.removeValuesListener = this.props.values.listen(changeEvent, this.valuesUpdated);
    this.removeResultsListener = this.props.pipeline.listen(resultsEvent, this.valuesUpdated);
  }

  componentWillUnmount() {
    this.removeValuesListener();
    this.removeResultsListener();
  }

  valuesUpdated() {
    this.setState(this.getState(this.props.values, this.props.pipeline, this.state.qParam));
  }

  setText(text, override = false) {
    const textValues = {
      [this.state.qParam] : text,
      [this.state.qOverrideParam]: override ? "true" : undefined,
    };
    this.props.values.set(textValues);
    if (textValues[this.state.qParam]) {
      this.props.pipeline.search();
    } else {
      this.props.pipeline.clearResults();
      this.props.pipeline.emitTrackingReset();
    }
  }

  render() {
    const { text, completion } = this.state
    const { placeHolder } = this.props

    return (

          <input
            type="text"
            id='sj-search-bar-completion'
            className='sj-search-bar-input-common'
            value={completion.indexOf(text) === 0 ? completion : text}
            readOnly
          />
          <input
            type="text"
            ref='searchInput'
            id='sj-search-bar-input'
            className='sj-search-bar-input-common'
            placeholder={placeHolder}
            value={text}
            onChange={e => this.setText(e.target.value)}
            onKeyDown={e => {
              if (e.keyCode === RETURN_KEYCODE) {
                e.preventDefault()
                this.setText(text, true)
              }
              if (!completion) { return }
              if (e.keyCode === TAB_KEYCODE || (e.keyCode === RIGHT_ARROW_KEYCODE && e.target.selectionStart === text.length)) {
                e.preventDefault()
                this.setText(completion)
              }
            }}
          />

    )
  }
}

export default AutocompleteInput;
<commit_msg>pipeline: autocomplete to use default props<commit_after>import React from 'react'
import { findDOMNode } from 'react-dom'

import { changeEvent } from "../controllers/values";
import { resultsEvent } from "../controllers/pipeline";

const RIGHT_ARROW_KEYCODE = 39
const TAB_KEYCODE = 9
const RETURN_KEYCODE = 13

class AutocompleteInput extends React.Component {
  constructor(props) {
    super(props)
    this.setText = this.setText.bind(this);
    this.valuesUpdated = this.valuesUpdated.bind(this);
    this.getState = this.getState.bind(this);
    this.state = {
      ...this.getState(props.values, props.pipeline, props.qParam),
      qParam: props.qParam,
      qOverrideParam: props.qOverrideParam,
    };
  }

  getState(values, pipeline, qParam) {
    const text = values.get()[qParam] || "";
    const responseValues = pipeline.getResponseValues();
    const completion = responseValues ? (responseValues[qParam] || "") : "";
    return { text, completion };
  }

  componentDidMount() {
    if (this.props.focus) {
      findDOMNode(this.refs.searchInput).focus()
    }

    this.removeValuesListener = this.props.values.listen(changeEvent, this.valuesUpdated);
    this.removeResultsListener = this.props.pipeline.listen(resultsEvent, this.valuesUpdated);
  }

  componentWillUnmount() {
    this.removeValuesListener();
    this.removeResultsListener();
  }

  valuesUpdated() {
    this.setState(this.getState(this.props.values, this.props.pipeline, this.state.qParam));
  }

  setText(text, override = false) {
    const textValues = {
      [this.state.qParam] : text,
      [this.state.qOverrideParam]: override ? "true" : undefined,
    };
    this.props.values.set(textValues);
    if (textValues[this.state.qParam]) {
      this.props.pipeline.search();
    } else {
      this.props.pipeline.clearResults();
      this.props.pipeline.emitTrackingReset();
    }
  }

  render() {
    const { text, completion } = this.state
    const { placeHolder } = this.props

    return (

          <input
            type="text"
            id='sj-search-bar-completion'
            className='sj-search-bar-input-common'
            value={completion.indexOf(text) === 0 ? completion : text}
            readOnly
          />
          <input
            type="text"
            ref='searchInput'
            id='sj-search-bar-input'
            className='sj-search-bar-input-common'
            placeholder={placeHolder}
            value={text}
            onChange={e => this.setText(e.target.value)}
            onKeyDown={e => {
              if (e.keyCode === RETURN_KEYCODE) {
                e.preventDefault()
                this.setText(text, true)
              }
              if (!completion) { return }
              if (e.keyCode === TAB_KEYCODE || (e.keyCode === RIGHT_ARROW_KEYCODE && e.target.selectionStart === text.length)) {
                e.preventDefault()
                this.setText(completion)
              }
            }}
          />

    )
  }
}

AutocompleteInput.defaultProps = {
  qParam: "q",
  qOverrideParam: "q.override",
};

export default AutocompleteInput;
<|endoftext|>