<commit_before>const Deferred = require('promised-io/promise').Deferred
const when = require('promised-io/promise').when
const debug = require('debug')('p3api-server:ProteinFamily')
const request = require('request')
const config = require('../config')
const distributeURL = config.get('distributeURL')
const redisOptions = config.get('redis')
const redis = require('redis')
const redisClient = redis.createClient(redisOptions)
const RedisTTL = 60 * 60 * 24 // sec
// this is global setting for concurrent connections
currentContext = 5
const all = require('promised-io/promise').all

function fetchFamilyDescriptionBatch (familyIdList) {
  const def = Deferred()
  const familyRefHash = {}

  redisClient.mget(familyIdList, function (err, replies) {
    const missingIds = []
    replies.forEach((reply, i) => {
      if (reply == null) {
        missingIds.push(familyIdList[i])
      } else {
        redisClient.expire(familyIdList[i], RedisTTL)
        familyRefHash[familyIdList[i]] = reply
      }
    })

    if (missingIds.length === 0) {
      def.resolve(familyRefHash)
    } else {
      request.post({
        url: distributeURL + 'protein_family_ref/',
        headers: {
          'Accept': 'application/json',
          'Content-Type': 'application/solrquery+x-www-form-urlencoded',
          'Authorization': ''
        },
        body: `q=family_id:(${missingIds.join(' OR ')})&fl=family_id,family_product&rows=${missingIds.length}`
      }, function (error, resp, body) {
        if (error) {
          def.reject(error)
          return
        }

        try {
          body = JSON.parse(body)
        }catch(perr){
          return def.reject(new Error("Error Parsing JSON response from solr: " + perr));
        }
         body.forEach(family => {
          redisClient.set(family.family_id, family.family_product, 'EX', RedisTTL)
          familyRefHash[family.family_id] = family.family_product
        })

        def.resolve(familyRefHash)
      })
    }
  })

  return def.promise
}

function fetchFamilyDescriptions (familyIdList) {
  const def = Deferred()
  const fetchSize = 3000
  const steps = Math.ceil(familyIdList.length / fetchSize)
  const allRequests = []
  const qSt = Date.now()

  for (let i = 0; i < steps; i++) {
    const subFamilyIdList = familyIdList.slice(i * fetchSize, Math.min((i + 1) * fetchSize, familyIdList.length))

    allRequests.push(fetchFamilyDescriptionBatch(subFamilyIdList))
  }

  debug('protein_family_ref checking cache took', (Date.now() - qSt) / 1000, 's')

  all(allRequests).then(body => {
    debug('protein_family_ref took', (Date.now() - qSt) / 1000, 's')

    const familyRefHash = body.reduce((r, b) => {
      return Object.assign(r, b)
    }, {})

    def.resolve(familyRefHash)
  })

  return def.promise
}

async function fetchFamilyDataByGenomeId (genomeId, options) {
  return new Promise((resolve, reject) => {
    const key = 'pfs_' + genomeId

    redisClient.get(key, function (err, familyData) {
      if (familyData == null) {
        debug(`no cached data for ${key}`)

        const query = `?q=genome_id:${genomeId} AND annotation:PATRIC AND feature_type:CDS&rows=25000&fl=pgfam_id,plfam_id,figfam_id,aa_length`

        request.get({
          url: distributeURL + 'genome_feature/' + query,
          headers: {
            'Accept': 'application/json',
            'Content-Type': 'application/solrquery+x-www-form-urlencoded',
            'Authorization': options.token || ''
          },
          json: true
        }, function (error, resp, body) {
          if (error) {
            reject(error)
            return
          }
          if (typeof body === 'object') {
            redisClient.set(key, JSON.stringify(body), 'EX', RedisTTL)
            resolve(body)
          } else {
            reject(body)
          }
        })
      } else {
        // hit cache
        redisClient.expire(key, RedisTTL)
        resolve(JSON.parse(familyData))
      }
    })
  })
}

async function fetchFamilyData (familyType, genomeIdList, options) {
  const def = Deferred()
  const familyIdField = familyType + '_id'

  const qSt = Date.now()
  const body = []
  for (let i = 0, len = genomeIdList.length; i < len; i++) {
    body[i] = await fetchFamilyDataByGenomeId(genomeIdList[i], options)
  }

  debug('fetching family data took ', (Date.now() - qSt) / 1000, 's')

  const totalFamilyIdDict = {}

  body.forEach((data, i) => {
    const genomeId = genomeIdList[i]

    data.forEach(row => {
      const fid = row[familyIdField]
      if (fid === '' || fid === undefined) return

      if (totalFamilyIdDict.hasOwnProperty(fid)) {
        if (totalFamilyIdDict[fid].hasOwnProperty(genomeId)) {
          totalFamilyIdDict[fid][genomeId].push(row['aa_length'])
        } else {
          // has fid, but not genome id
          totalFamilyIdDict[fid][genomeId] = [row['aa_length']]
        }
      } else {
        totalFamilyIdDict[fid] = {}
        totalFamilyIdDict[fid][genomeId] = [row['aa_length']]
      }
    })
  })

  def.resolve(totalFamilyIdDict)

  return def.promise
}

function processProteinFamily (pfState, options) {
  const def = new Deferred()

  // moved from MemoryStore implementation.
  const familyType = pfState['familyType']
  const genomeIds = pfState.genomeIds

  when(fetchFamilyData(familyType, genomeIds, options), function (totalFamilyIdDict) {
    // debug(totalFamilyIdDict)
    const familyIdList = Object.keys(totalFamilyIdDict)

    when(fetchFamilyDescriptions(familyIdList), function (familyRefHash) {
      const qSt = Date.now()
      const data = []
      familyIdList.sort().forEach(familyId => {
        const proteins = genomeIds.map(genomeId => {
          return totalFamilyIdDict[familyId][genomeId]
        })
          .filter(row => row !== undefined)
          .reduce((total, proteins) => {
            return total.concat(proteins)
          }, [])

        const aa_length_max = Math.max.apply(Math, proteins)
        const aa_length_min = Math.min.apply(Math, proteins)
        const aa_length_sum = proteins.reduce((total, val) => total + val, 0)
        const aa_length_mean = aa_length_sum / proteins.length
        const aa_length_variance = proteins.map(val => Math.pow(val - aa_length_mean, 2))
          .reduce((total, val) => total + val, 0) / proteins.length
        const aa_length_std = Math.sqrt(aa_length_variance)

        // debug(proteins, aa_length_mean, aa_length_variance, aa_length_std);

        const genomeString = genomeIds.map(genomeId => {
          if (totalFamilyIdDict[familyId].hasOwnProperty(genomeId)) {
            const hexCount = (totalFamilyIdDict[familyId][genomeId].length).toString(16)
            return (hexCount.length === 1) ? `0${hexCount}` : hexCount
          } else {
            return '00'
          }
        }).join('')

        const row = {
          family_id: familyId,
          feature_count: proteins.length,
          genome_count: Object.keys(totalFamilyIdDict[familyId]).length,
          aa_length_std: aa_length_std,
          aa_length_max: aa_length_max,
          aa_length_mean: aa_length_mean,
          aa_length_min: aa_length_min,
          description: familyRefHash[familyId],
          genomes: genomeString
        }
        data.push(row)
      })

      debug('processing protein family data took ', (Date.now() - qSt) / 1000, 's')

      def.resolve(data)
    })
  })

  return def.promise
}

module.exports = {
  requireAuthentication: false,
  validate: function (params) {
    const pfState = params[0]
    return pfState && pfState.genomeIds.length > 0
  },
  execute: function (params) {
    const def = new Deferred()

    const pfState = params[0]
    const opts = params[1]

    when(processProteinFamily(pfState, opts), function (result) {
      def.resolve(result)
    }, function (err) {
      def.reject('Unable to process protein family queries. ' + err)
    })

    return def.promise
  }
}
<commit_msg>use standard promise<commit_after>const debug = require('debug')('p3api-server:ProteinFamily')
const request = require('request')
const config = require('../config')
const distributeURL = config.get('distributeURL')
const redisOptions = config.get('redis')
const redis = require('redis')
const redisClient = redis.createClient(redisOptions)
const RedisTTL = 60 * 60 * 24 // sec
// this is global setting for concurrent connections
currentContext = 5

function fetchFamilyDescriptionBatch (familyIdList) {
  return new Promise((resolve, reject) => {
    const familyRefHash = {}

    redisClient.mget(familyIdList, function (err, replies) {
      const missingIds = []
      replies.forEach((reply, i) => {
        if (reply == null) {
          missingIds.push(familyIdList[i])
        } else {
          redisClient.expire(familyIdList[i], RedisTTL)
          familyRefHash[familyIdList[i]] = reply
        }
      })

      if (missingIds.length === 0) {
        resolve(familyRefHash)
      } else {
        request.post({
          url: distributeURL + 'protein_family_ref/',
          headers: {
            'Accept': 'application/json',
            'Content-Type': 'application/solrquery+x-www-form-urlencoded',
            'Authorization': ''
          },
          body: `q=family_id:(${missingIds.join(' OR ')})&fl=family_id,family_product&rows=${missingIds.length}`
        }, function (error, resp, body) {
          if (error) {
            reject(error)
            return
          }

          try {
            const parsed = JSON.parse(body)
            parsed.forEach(family => {
              redisClient.set(family.family_id, family.family_product, 'EX', RedisTTL)
              familyRefHash[family.family_id] = family.family_product
            })
          } catch (perr) {
            reject(new Error('Error Parsing JSON response from solr: ' + perr))
            return
          }

          resolve(familyRefHash)
        })
      }
    })
  })
}

async function fetchFamilyDescriptions (familyIdList) {
  const fetchSize = 3000
  const steps = Math.ceil(familyIdList.length / fetchSize)
  const allRequests = []
  const qSt = Date.now()

  for (let i = 0; i < steps; i++) {
    const subFamilyIdList = familyIdList.slice(i * fetchSize, Math.min((i + 1) * fetchSize, familyIdList.length))

    allRequests.push(fetchFamilyDescriptionBatch(subFamilyIdList))
  }

  debug('protein_family_ref checking cache took', (Date.now() - qSt) / 1000, 's')

  try {
    const body = await Promise.all(allRequests)
    debug('protein_family_ref took', (Date.now() - qSt) / 1000, 's')

    return body.reduce((r, b) => {
      return Object.assign(r, b)
    }, {})

  } catch (err) {
    return err
  }
}

async function fetchFamilyDataByGenomeId (genomeId, options) {
  return new Promise((resolve, reject) => {
    const key = 'pfs_' + genomeId

    redisClient.get(key, function (err, familyData) {
      if (familyData == null) {
        debug(`no cached data for ${key}`)

        const query = `?q=genome_id:${genomeId} AND annotation:PATRIC AND feature_type:CDS&rows=25000&fl=pgfam_id,plfam_id,figfam_id,aa_length`

        request.get({
          url: distributeURL + 'genome_feature/' + query,
          headers: {
            'Accept': 'application/json',
            'Content-Type': 'application/solrquery+x-www-form-urlencoded',
            'Authorization': options.token || ''
          },
          json: true
        }, function (error, resp, body) {
          if (error) {
            reject(error)
            return
          }
          if (typeof body === 'object') {
            redisClient.set(key, JSON.stringify(body), 'EX', RedisTTL)
            resolve(body)
          } else {
            reject(body)
          }
        })
      } else {
        // hit cache
        redisClient.expire(key, RedisTTL)
        resolve(JSON.parse(familyData))
      }
    })
  })
}

async function fetchFamilyData (familyType, genomeIdList, options) {
  const familyIdField = familyType + '_id'

  const qSt = Date.now()
  const body = []
  for (let i = 0, len = genomeIdList.length; i < len; i++) {
    body[i] = await fetchFamilyDataByGenomeId(genomeIdList[i], options)
  }

  debug('fetching family data took ', (Date.now() - qSt) / 1000, 's')

  const totalFamilyIdDict = {}

  body.forEach((data, i) => {
    const genomeId = genomeIdList[i]

    data.forEach(row => {
      const fid = row[familyIdField]
      if (fid === '' || fid === undefined) return

      if (totalFamilyIdDict.hasOwnProperty(fid)) {
        if (totalFamilyIdDict[fid].hasOwnProperty(genomeId)) {
          totalFamilyIdDict[fid][genomeId].push(row['aa_length'])
        } else {
          // has fid, but not genome id
          totalFamilyIdDict[fid][genomeId] = [row['aa_length']]
        }
      } else {
        totalFamilyIdDict[fid] = {}
        totalFamilyIdDict[fid][genomeId] = [row['aa_length']]
      }
    })
  })
  return totalFamilyIdDict
}

async function processProteinFamily (pfState, options) {
  // moved from MemoryStore implementation.
  const familyType = pfState['familyType']
  const genomeIds = pfState.genomeIds

  try {
    const totalFamilyIdDict = await fetchFamilyData(familyType, genomeIds, options)
    const familyIdList = Object.keys(totalFamilyIdDict)

    const familyRefHash = await fetchFamilyDescriptions(familyIdList)

    const qSt = Date.now()
    const data = []
    familyIdList.sort().forEach(familyId => {
      const proteins = genomeIds.map(genomeId => {
        return totalFamilyIdDict[familyId][genomeId]
      })
        .filter(row => row !== undefined)
        .reduce((total, proteins) => {
          return total.concat(proteins)
        }, [])

      const aa_length_max = Math.max.apply(Math, proteins)
      const aa_length_min = Math.min.apply(Math, proteins)
      const aa_length_sum = proteins.reduce((total, val) => total + val, 0)
      const aa_length_mean = aa_length_sum / proteins.length
      const aa_length_variance = proteins.map(val => Math.pow(val - aa_length_mean, 2))
        .reduce((total, val) => total + val, 0) / proteins.length
      const aa_length_std = Math.sqrt(aa_length_variance)

      // debug(proteins, aa_length_mean, aa_length_variance, aa_length_std);

      const genomeString = genomeIds.map(genomeId => {
        if (totalFamilyIdDict[familyId].hasOwnProperty(genomeId)) {
          const hexCount = (totalFamilyIdDict[familyId][genomeId].length).toString(16)
          return (hexCount.length === 1) ? `0${hexCount}` : hexCount
        } else {
          return '00'
        }
      }).join('')

      const row = {
        family_id: familyId,
        feature_count: proteins.length,
        genome_count: Object.keys(totalFamilyIdDict[familyId]).length,
        aa_length_std: aa_length_std,
        aa_length_max: aa_length_max,
        aa_length_mean: aa_length_mean,
        aa_length_min: aa_length_min,
        description: familyRefHash[familyId],
        genomes: genomeString
      }
      data.push(row)
    })

    debug('processing protein family data took ', (Date.now() - qSt) / 1000, 's')

    return data
  } catch (err) {
    return err
  }
}

module.exports = {
  requireAuthentication: false,
  validate: function (params) {
    const pfState = params[0]
    return pfState && pfState.genomeIds.length > 0
  },
  execute: async function (params) {
    const pfState = params[0]
    const opts = params[1]

    try {
      return await processProteinFamily(pfState, opts)
    } catch (err) {
      return new Error('Unable to process protein family queries. ' + err)
    }
  }
}
<|endoftext|>


<commit_before>var Component = require('yacomponent'),
	inheritClass = require('inherit-class'),
	Item = require('./Item'),
	handlebars = require('handlebars-adapter');

function Collection() {
	this.items = [];

	this.template = null;

	this.fakeItem = undefined;
}

Collection.defaults = {
	dName: 'list',
	parent: null,
	Item: Item
};

inheritClass(Collection, Component, 'component');

Collection.prototype = $.extend({}, Collection.prototype, {
	init: function() {
		this.items = [];
		this.defineParentName();
		Component.prototype.init.call(this);
		this.makeFakeItem();
		this.defineItems();
		this.defineEvents();
	},

	makeFakeItem: function() {
		this.fakeItem = this.initItem({$el: $([]), fake: true});
	},

	defineTemplate: function() {
		this.template = handlebars.compile(this.options.parent.makeName(this.getItemDName()));
	},

	defineParentName: function() {
		if (this.options.parent) return;
		this.options.parent = this;
	},

	defineEvents: function() {
		var self = this;
		this.$el.on('click', this.elDel('makeSelector', 'remove'), function(e) {
			e.preventDefault();
			self.onRemove(e);
		});
		$('.' + this.options.dName + 'Add').on('click', function(e) {
			e.preventDefault();
			self.clickAdd();
		});
	},

	elDel: function(method, arg) {
		var args = [this.getItemDName()];
		args.push(arg);
		return this.options.parent[method].call(this.options.parent, args);
	},

	onRemove: function(event) {
		var item = this.getByEl(event.currentTarget);
		this.remove(item);
	},

	getByEl: function(el) {
		return this.items.filter(function(item) {
			return item.$el[0] === el || item.$el.find(el).length;
		})[0];
	},

	getItemDName: function() {
		return this.options.Item.defaults.dName;
	},

	defineItems: function() {
		var self = this;
		this.options.parent.find(this.getItemDName()).each(function() {
			var $el = $(this);
			var data = $el.data(self.options.parent.dName + '-' + self.options.Item.defaults.dName);
			var item = self.initItem({$el: $el, data: data});
			self.add(item);
		});
	},

	add: function(item, insert) {
		insert = typeof insert == 'undefined' ? false : insert;
		this.items.push(item);
		if (insert) this.$el.append(item.$el);
	},

	addByData: function(data) {
		var html = this.template(data);
		var item = this.initItem({$el: $(html), data: data});
		this.add(item, true);
	},

	clickAdd: function() {
		var data = this.getClickAddData();
		this.addByData(data);
	},

	getClickAddData: function() {
		return {};
	},

	remove: function(item) {
		var index = this.items.indexOf(item);
		item.$el.remove();
		this.items.splice(index, 1);
	},

	initItem: function(options) {
		options = $.extend({dName: this.options.parent.makeName(this.getItemDName())}, options);
		return this.options.Item.init(options);
	},
});
Collection.prototype.constructor = Collection;

module.exports = Collection;<commit_msg>fix: set Item prop to base class<commit_after>var Component = require('yacomponent'),
	inheritClass = require('inherit-class'),
	Item = Collection.Item = require('./Item'),
	handlebars = require('handlebars-adapter');

function Collection() {
	this.items = [];

	this.template = null;

	this.fakeItem = undefined;
}

Collection.defaults = {
	dName: 'list',
	parent: null,
	Item: Item
};

inheritClass(Collection, Component, 'component');

Collection.prototype = $.extend({}, Collection.prototype, {
	init: function() {
		this.items = [];
		this.defineParentName();
		Component.prototype.init.call(this);
		this.makeFakeItem();
		this.defineItems();
		this.defineEvents();
	},

	makeFakeItem: function() {
		this.fakeItem = this.initItem({$el: $([]), fake: true});
	},

	defineTemplate: function() {
		this.template = handlebars.compile(this.options.parent.makeName(this.getItemDName()));
	},

	defineParentName: function() {
		if (this.options.parent) return;
		this.options.parent = this;
	},

	defineEvents: function() {
		var self = this;
		this.$el.on('click', this.elDel('makeSelector', 'remove'), function(e) {
			e.preventDefault();
			self.onRemove(e);
		});
		$('.' + this.options.dName + 'Add').on('click', function(e) {
			e.preventDefault();
			self.clickAdd();
		});
	},

	elDel: function(method, arg) {
		var args = [this.getItemDName()];
		args.push(arg);
		return this.options.parent[method].call(this.options.parent, args);
	},

	onRemove: function(event) {
		var item = this.getByEl(event.currentTarget);
		this.remove(item);
	},

	getByEl: function(el) {
		return this.items.filter(function(item) {
			return item.$el[0] === el || item.$el.find(el).length;
		})[0];
	},

	getItemDName: function() {
		return this.options.Item.defaults.dName;
	},

	defineItems: function() {
		var self = this;
		this.options.parent.find(this.getItemDName()).each(function() {
			var $el = $(this);
			var data = $el.data(self.options.parent.dName + '-' + self.options.Item.defaults.dName);
			var item = self.initItem({$el: $el, data: data});
			self.add(item);
		});
	},

	add: function(item, insert) {
		insert = typeof insert == 'undefined' ? false : insert;
		this.items.push(item);
		if (insert) this.$el.append(item.$el);
	},

	addByData: function(data) {
		var html = this.template(data);
		var item = this.initItem({$el: $(html), data: data});
		this.add(item, true);
	},

	clickAdd: function() {
		var data = this.getClickAddData();
		this.addByData(data);
	},

	getClickAddData: function() {
		return {};
	},

	remove: function(item) {
		var index = this.items.indexOf(item);
		item.$el.remove();
		this.items.splice(index, 1);
	},

	initItem: function(options) {
		options = $.extend({dName: this.options.parent.makeName(this.getItemDName())}, options);
		return this.options.Item.init(options);
	},
});
Collection.prototype.constructor = Collection;

module.exports = Collection;<|endoftext|>


<commit_before>require('babel-register');

const test           = require('tape');
const thunk          = require('redux-thunk').default;
const middleware     = require('../lib/middleware').default;
const RemoteResource = require('../lib/RemoteResource').default;
const { createFetchMock, nest, failTest } = require('./mock.helper');
const { applyMiddleware, createStore }    = require('redux');

function makeMockStore(remoteResourceOpts = {}) {
  const fetchMock = createFetchMock();
  const middlewares = applyMiddleware(middleware(
    Object.assign({ _fetch: fetchMock.fetchFunc }, remoteResourceOpts)
  ), thunk);

  const actionLogReducer = (state = [], action) => state.concat(action);
  const store = createStore(actionLogReducer, middlewares);

  return { fetch: fetchMock, store };
}

const actionWasDispatched = (actionType, actions) =>
  actions.some(({ type }) => type === actionType);

// test action types
const REQUEST = 'REQUEST';
const REQUEST_SUCCESS = 'REQUEST_SUCCESS';
const REQUEST_FAILURE = 'REQUEST_FAILURE';

test.only('middleware integration', st => {
  st.test(nest('idempotent request'), t => {
    const { fetch, store } = makeMockStore();

    const respData = { test: 'data' };
    fetch.respondWith(200, respData);

    const reqHeaders = { 'Accept': 'application/json' };
    const reqUrl = 'localhost/someapi';
    const action = {
      [RemoteResource]: {
        uri: reqUrl,
        headers: reqHeaders,
        lifecycle: {
          request: REQUEST,
          success(data, dispatch) {
            t.deepEquals(data, respData, 'returned correct parsed data');
            dispatch({ type: REQUEST_SUCCESS });
          },
          failure(error, dispatch) {
            t.error(error);
            dispatch({ type: REQUEST_FAILURE });
          }
        }
      }
    };

    store.dispatch(action)
      .then(() => {
        const actions = store.getState();
        t.ok(actions.length, 'state has actions');
        t.ok(actionWasDispatched(REQUEST, actions), 'request was dispatched');
        t.ok(actionWasDispatched(REQUEST_SUCCESS, actions),
          'request success was dispatched');

        t.equals(fetch.url(), reqUrl, 'fetch w/ correct url');
        t.equals(fetch.method().toLowerCase(), 'get',
          'fetched w/ method GET by default');
        t.deepEqual(fetch.headers(), reqHeaders,
          'fetch was called with correct headers');
        t.end();
      }).catch(failTest);
  });

  st.test(nest('post request w/ error'), t => {
    const { fetch, store } = makeMockStore();

    const responseStatus = 400;
    const responseBody = { error: 'Some validation error' };
    fetch.respondWith(responseStatus, responseBody);

    const reqBody = { test: 'data' };
    const action = {
      [RemoteResource]: {
        uri: 'localhost/someapi',
        headers: { 'Content-Type': 'application/json' },
        method: 'post',
        body: reqBody,
        lifecycle: {
          request: REQUEST,
          success(data, dispatch) {
            t.fail('success hook should not have been called');
            dispatch(REQUEST_SUCCESS);
          },
          failure(error, dispatch) {
            return Promise.resolve(dispatch({ type: REQUEST_FAILURE, error }));
          }
        }
      }
    };

    store.dispatch(action)
      .then(() => {
        const actions = store.getState();
        t.ok(actions.length, 'state has actions');
        t.ok(actionWasDispatched(REQUEST, actions), 'request was dispatched');
        t.ok(actionWasDispatched(REQUEST_FAILURE, actions),
          'request failure was dispatched');

        const failAction = actions.find(ac => ac.type === REQUEST_FAILURE);
        t.equals(failAction.error.statusText, responseBody.error,
          'extracts error from response');

        t.equals(fetch.method().toLowerCase(), 'post',
          'fetched w/ correct method');

        t.end();
      });
  });

  st.test(nest('post request'), t => t.fail('not implemented'));
  st.test(nest('status actions'), t => t.fail('not implemented'));
  st.test(nest('idempotent request with cache'), t => t.fail('not implemented'));
});
<commit_msg>Remove test focus<commit_after>require('babel-register');

const test           = require('tape');
const thunk          = require('redux-thunk').default;
const middleware     = require('../lib/middleware').default;
const RemoteResource = require('../lib/RemoteResource').default;
const { createFetchMock, nest, failTest } = require('./mock.helper');
const { applyMiddleware, createStore }    = require('redux');

function makeMockStore(remoteResourceOpts = {}) {
  const fetchMock = createFetchMock();
  const middlewares = applyMiddleware(middleware(
    Object.assign({ _fetch: fetchMock.fetchFunc }, remoteResourceOpts)
  ), thunk);

  const actionLogReducer = (state = [], action) => state.concat(action);
  const store = createStore(actionLogReducer, middlewares);

  return { fetch: fetchMock, store };
}

const actionWasDispatched = (actionType, actions) =>
  actions.some(({ type }) => type === actionType);

// test action types
const REQUEST = 'REQUEST';
const REQUEST_SUCCESS = 'REQUEST_SUCCESS';
const REQUEST_FAILURE = 'REQUEST_FAILURE';

test('middleware integration', st => {
  st.test(nest('idempotent request'), t => {
    const { fetch, store } = makeMockStore();

    const respData = { test: 'data' };
    fetch.respondWith(200, respData);

    const reqHeaders = { 'Accept': 'application/json' };
    const reqUrl = 'localhost/someapi';
    const action = {
      [RemoteResource]: {
        uri: reqUrl,
        headers: reqHeaders,
        lifecycle: {
          request: REQUEST,
          success(data, dispatch) {
            t.deepEquals(data, respData, 'returned correct parsed data');
            dispatch({ type: REQUEST_SUCCESS });
          },
          failure(error, dispatch) {
            t.error(error);
            dispatch({ type: REQUEST_FAILURE });
          }
        }
      }
    };

    store.dispatch(action)
      .then(() => {
        const actions = store.getState();
        t.ok(actions.length, 'state has actions');
        t.ok(actionWasDispatched(REQUEST, actions), 'request was dispatched');
        t.ok(actionWasDispatched(REQUEST_SUCCESS, actions),
          'request success was dispatched');

        t.equals(fetch.url(), reqUrl, 'fetch w/ correct url');
        t.equals(fetch.method().toLowerCase(), 'get',
          'fetched w/ method GET by default');
        t.deepEqual(fetch.headers(), reqHeaders,
          'fetch was called with correct headers');
        t.end();
      }).catch(failTest);
  });

  st.test(nest('post request w/ error'), t => {
    const { fetch, store } = makeMockStore();

    const responseStatus = 400;
    const responseBody = { error: 'Some validation error' };
    fetch.respondWith(responseStatus, responseBody);

    const reqBody = { test: 'data' };
    const action = {
      [RemoteResource]: {
        uri: 'localhost/someapi',
        headers: { 'Content-Type': 'application/json' },
        method: 'post',
        body: reqBody,
        lifecycle: {
          request: REQUEST,
          success(data, dispatch) {
            t.fail('success hook should not have been called');
            dispatch(REQUEST_SUCCESS);
          },
          failure(error, dispatch) {
            return Promise.resolve(dispatch({ type: REQUEST_FAILURE, error }));
          }
        }
      }
    };

    store.dispatch(action)
      .then(() => {
        const actions = store.getState();
        t.ok(actions.length, 'state has actions');
        t.ok(actionWasDispatched(REQUEST, actions), 'request was dispatched');
        t.ok(actionWasDispatched(REQUEST_FAILURE, actions),
          'request failure was dispatched');

        const failAction = actions.find(ac => ac.type === REQUEST_FAILURE);
        t.equals(failAction.error.statusText, responseBody.error,
          'extracts error from response');

        t.equals(fetch.method().toLowerCase(), 'post',
          'fetched w/ correct method');

        t.end();
      });
  });

  st.test(nest('post request'), t => t.fail('not implemented'));
  st.test(nest('status actions'), t => t.fail('not implemented'));
  st.test(nest('idempotent request with cache'), t => t.fail('not implemented'));
});
<|endoftext|>


<commit_before>window.multigraph.util.namespace("window.multigraph.graphics.raphael", function (ns) {
    "use strict";

    ns.mixin.add(function (ns) {

        // cached settings object, for quick access during rendering, populated in begin() method:
        ns.BarRenderer.hasA("settings");

        ns.BarRenderer.respondsTo("begin", function (graphicsContext) {
            var settings = {
                "paper"         : graphicsContext.paper,
                "set"           : graphicsContext.set,
                "path"          : "",
                "barpixelwidth" : this.getOptionValue("barwidth") * this.plot().horizontalaxis().axisToDataRatio(),
                "baroffset"     : this.getOptionValue("baroffset"),
                "barpixelbase"  : (this.getOptionValue("barbase") !== null)?this.plot().verticalaxis().dataValueToAxisValue(this.getOptionValue("barbase")):0,
                "fillcolor"     : this.getOptionValue("fillcolor"),
                "linecolor"     : this.getOptionValue("linecolor"),
                "hidelines"     : this.getOptionValue("hidelines"),
                "barGroups"          : [],
                "currentBarGroup"    : null,
                "prevCorner"         : null,
                "pixelEdgeTolerance" : 1
            };

            this.settings(settings);
        });

        ns.BarRenderer.respondsTo("dataPoint", function (datap) {
            var settings = this.settings(),
                p,
                x0,
                x1;

            if (this.isMissing(datap)) {
                return;
            }
            p = this.transformPoint(datap);

            x0 = p[0] + settings.baroffset;
            x1 = p[0] + settings.baroffset + settings.barpixelwidth;

            settings.path += this.generateBar(x0, settings.barpixelbase, settings.barpixelwidth, p[1] - settings.barpixelbase);

            if (settings.barpixelwidth > settings.hidelines) {
                if (settings.prevCorner === null) {
                    settings.currentBarGroup = [ [x0,p[1]] ];
                } else {
                    if (Math.abs(x0 - settings.prevCorner[0]) <= settings.pixelEdgeTolerance) {
                        settings.currentBarGroup.push( [x0,p[1]] );
                    } else {
                        settings.currentBarGroup.push( settings.prevCorner );
                        settings.barGroups.push( settings.currentBarGroup );
                        settings.currentBarGroup = [ [x0,p[1]] ];
                    }
                }
                settings.prevCorner = [x1,p[1]];
            }
        });

        ns.BarRenderer.respondsTo("end", function () {
            var settings = this.settings(),
                p,
                outlinePath = "",
                barGroup,
                i,
                j,
                n;

            if (settings.prevCorner !== null && settings.currentBarGroup !== null) {
                settings.currentBarGroup.push( settings.prevCorner );
                settings.barGroups.push( settings.currentBarGroup );
            }

            for (i = 0; i < settings.barGroups.length; i++) {
                barGroup = settings.barGroups[i];
                n = barGroup.length;
                if (n < 2) { return; } // this should never happen

                // For the first point, draw 3 lines:
                //
                //       y |------
                //         |
                //         |
                //    base |------
                //         ^     ^
                //         x     x(next)
                //

                //   horizontal line @ y from x(next) to x
                outlinePath += "M" + barGroup[1][0] + "," + barGroup[0][1];
                outlinePath += "L" + barGroup[0][0] + "," + barGroup[0][1];
                //   vertical line @ x from y to base
                outlinePath += "L" + barGroup[0][0] + "," + settings.barpixelbase;
                //   horizontal line @ base from x to x(next)
                outlinePath += "L" + barGroup[1][0] + "," + settings.barpixelbase;

                for (j = 1; j < n - 1; ++j) {
                    // For intermediate points, draw 3 lines:
                    //
                    //       y |
                    //         |
                    //         |
                    //         |------ y(next)
                    //         |
                    //         |
                    //         |------ base
                    //         ^     ^
                    //         x     x(next)
                    //
                    //   vertical line @ x from min to max of (y, y(next), base)
                    outlinePath += "M" + barGroup[j][0] + "," + Math.min(barGroup[j-1][1], barGroup[j][1], settings.barpixelbase);
                    outlinePath += "L" + barGroup[j][0] + "," + Math.max(barGroup[j-1][1], barGroup[j][1], settings.barpixelbase);
                    //   horizontal line @ y(next) from x to x(next)
                    outlinePath += "M" + barGroup[j][0] + "," +   barGroup[j][1];
                    outlinePath += "L" + barGroup[j+1][0] + "," + barGroup[j][1];
                    //   horizontal line @ base from x to x(next)
                    outlinePath += "M" + barGroup[j][0] + "," +   settings.barpixelbase;
                    outlinePath += "L" + barGroup[j+1][0] + "," + settings.barpixelbase;
                }
                // For last point, draw one line:
                //
                //       y |
                //         |
                //         |
                //    base |
                //         ^     ^
                //         x     x(next)
                //
                //   vertical line @ x from base to y
                outlinePath += "M" + barGroup[n-1][0] + "," + barGroup[n-1][1];
                outlinePath += "L" + barGroup[n-1][0] + "," + settings.barpixelbase;
            }

            settings.set.push( settings.paper.path(settings.path)
                               .attr({
                                   "stroke-width": 1,
                                   "fill": settings.fillcolor.getHexString("#"),
                                   "stroke": settings.fillcolor.getHexString("#")
                               })
                             );

            settings.set.push( settings.paper.path(outlinePath)
                               .attr({
                                   "stroke-width": 1,
                                   "stroke": settings.linecolor.getHexString("#")
                               })
                             );

        });

        ns.BarRenderer.respondsTo("generateBar", function (x, y, width, height) {
            var path = "M" + x + "," + y;
            path    += "L" + x + "," + (y + height);
            path    += "L" + (x + width) + "," + (y + height);
            path    += "L" + (x + width) + "," + y;
            path    += "Z";
            return path;
        });

        ns.BarRenderer.respondsTo("renderLegendIcon", function (graphicsContext, x, y, icon, opacity) {
            var settings = this.settings(),
                iconAttrs,
                barwidth;

            // Draw icon background (with opacity)
            graphicsContext.paper.rect(x, y, icon.width(), icon.height())
                .attr({
                    "stroke" : "#ffffff",
                    "fill" : "#ffffff"
                })
                .transform(graphicsContext.transformString);

            // TODO: need to get the fillcolor at y=0
            iconAttrs = {
                "stroke-width" : 1,
                "fill" : settings.fillcolor.getHexString("#")
            };

            if (settings.barpixelwidth < settings.hidelines) {
                iconAttrs.stroke = settings.fillcolor.getHexString("#");
            } else {
                iconAttrs.stroke = settings.linecolor.getHexString("#");
            }

            // Adjust the width of the icons bars based upon the width and height of the icon Ranges: {20, 10, 0}
            if (icon.width() > 20 || icon.height() > 20) {
                barwidth = icon.width() / 6;
            } else if (icon.width() > 10 || icon.height() > 10) {
                barwidth = icon.width() / 4;
            } else {
                barwidth = icon.width() / 4;
            }

        // If the icon is large enough draw extra bars
            if (icon.width() > 20 && icon.height() > 20) {
                graphicsContext.paper.rect((icon.width() / 4) - (barwidth / 2), 0, barwidth, icon.height() / 2)
                    .attr(iconAttrs)
                    .transform(graphicsContext.transformString + "t" + x + "," + y);

                graphicsContext.paper.rect(icon.width() - (icon.width() / 4) - (barwidth / 2), 0, barwidth, icon.height() / 3)
                    .attr(iconAttrs)
                    .transform(graphicsContext.transformString + "t" + x + "," + y);
            }

            graphicsContext.paper.rect((icon.width() / 2) - (barwidth / 2), 0, barwidth, icon.height() - (icon.height() / 4))
                .attr(iconAttrs)
                .transform(graphicsContext.transformString + "t" + x + "," + y);

        });

    });

});
<commit_msg>updates raphaels bar renderer<commit_after>window.multigraph.util.namespace("window.multigraph.graphics.raphael", function (ns) {
    "use strict";

    ns.mixin.add(function (ns) {

        // cached settings object, for quick access during rendering, populated in begin() method:
        ns.BarRenderer.hasA("settings");

        ns.BarRenderer.respondsTo("begin", function (graphicsContext) {
            var settings = {
                "paper"              : graphicsContext.paper,
                "set"                : graphicsContext.set,
                "paths"              : [],
                "barpixelwidth"      : this.getOptionValue("barwidth").getRealValue() * this.plot().horizontalaxis().axisToDataRatio(),
                "baroffset"          : this.getOptionValue("baroffset"),
                "barpixelbase"       : (this.getOptionValue("barbase") !== null)?this.plot().verticalaxis().dataValueToAxisValue(this.getOptionValue("barbase")):0,
                "fillcolor"          : this.getOptionValue("fillcolor"),
                "linecolor"          : this.getOptionValue("linecolor"),
                "hidelines"          : this.getOptionValue("hidelines"),
                "barGroups"          : [],
                "currentBarGroup"    : null,
                "prevCorner"         : null,
                "pixelEdgeTolerance" : 1
            };

            this.settings(settings);
        });

        ns.BarRenderer.respondsTo("dataPoint", function (datap) {
            var settings = this.settings(),
                p,
                x0,
                x1,
                fillcolor = this.getOptionValue("fillcolor", datap[1]),
                flag = false,
                i;

            if (this.isMissing(datap)) {
                return;
            }

            p = this.transformPoint(datap);

            x0 = p[0] + settings.baroffset;
            x1 = p[0] + settings.baroffset + settings.barpixelwidth;

            for (i = 0; i < settings.paths.length; i++) {
                if (settings.paths[i].fillcolor === fillcolor) {
                    settings.paths[i].path += this.generateBar(x0, settings.barpixelbase, settings.barpixelwidth, p[1] - settings.barpixelbase);
                    flag = true;
                    break;
                }
            }

            if (flag === false) {
                i = settings.paths.length;
                settings.paths[i] = {};
                settings.paths[i].fillcolor = fillcolor;
                settings.paths[i].path = this.generateBar(x0, settings.barpixelbase, settings.barpixelwidth, p[1] - settings.barpixelbase);
            }

            if (settings.barpixelwidth > settings.hidelines) {
                if (settings.prevCorner === null) {
                    settings.currentBarGroup = [ [x0,p[1]] ];
                } else {
                    if (Math.abs(x0 - settings.prevCorner[0]) <= settings.pixelEdgeTolerance) {
                        settings.currentBarGroup.push( [x0,p[1]] );
                    } else {
                        settings.currentBarGroup.push( settings.prevCorner );
                        settings.barGroups.push( settings.currentBarGroup );
                        settings.currentBarGroup = [ [x0,p[1]] ];
                    }
                }
                settings.prevCorner = [x1,p[1]];
            }
        });

        ns.BarRenderer.respondsTo("end", function () {
            var settings = this.settings(),
                p,
                outlinePath = "",
                barGroup,
                i,
                j,
                n;

            if (settings.prevCorner !== null && settings.currentBarGroup !== null) {
                settings.currentBarGroup.push( settings.prevCorner );
                settings.barGroups.push( settings.currentBarGroup );
            }

            for (i = 0; i < settings.barGroups.length; i++) {
                barGroup = settings.barGroups[i];
                n = barGroup.length;
                if (n < 2) { return; } // this should never happen

                // For the first point, draw 3 lines:
                //
                //       y |------
                //         |
                //         |
                //    base |------
                //         ^     ^
                //         x     x(next)
                //

                //   horizontal line @ y from x(next) to x
                outlinePath += "M" + barGroup[1][0] + "," + barGroup[0][1];
                outlinePath += "L" + barGroup[0][0] + "," + barGroup[0][1];
                //   vertical line @ x from y to base
                outlinePath += "L" + barGroup[0][0] + "," + settings.barpixelbase;
                //   horizontal line @ base from x to x(next)
                outlinePath += "L" + barGroup[1][0] + "," + settings.barpixelbase;

                for (j = 1; j < n - 1; ++j) {
                    // For intermediate points, draw 3 lines:
                    //
                    //       y |
                    //         |
                    //         |
                    //         |------ y(next)
                    //         |
                    //         |
                    //         |------ base
                    //         ^     ^
                    //         x     x(next)
                    //
                    //   vertical line @ x from min to max of (y, y(next), base)
                    outlinePath += "M" + barGroup[j][0] + "," + Math.min(barGroup[j-1][1], barGroup[j][1], settings.barpixelbase);
                    outlinePath += "L" + barGroup[j][0] + "," + Math.max(barGroup[j-1][1], barGroup[j][1], settings.barpixelbase);
                    //   horizontal line @ y(next) from x to x(next)
                    outlinePath += "M" + barGroup[j][0] + "," +   barGroup[j][1];
                    outlinePath += "L" + barGroup[j+1][0] + "," + barGroup[j][1];
                    //   horizontal line @ base from x to x(next)
                    outlinePath += "M" + barGroup[j][0] + "," +   settings.barpixelbase;
                    outlinePath += "L" + barGroup[j+1][0] + "," + settings.barpixelbase;
                }
                // For last point, draw one line:
                //
                //       y |
                //         |
                //         |
                //    base |
                //         ^     ^
                //         x     x(next)
                //
                //   vertical line @ x from base to y
                outlinePath += "M" + barGroup[n-1][0] + "," + barGroup[n-1][1];
                outlinePath += "L" + barGroup[n-1][0] + "," + settings.barpixelbase;
            }

            for (i = 0; i < settings.paths.length; i++) {
                settings.set.push( settings.paper.path(settings.paths[i].path)
                                   .attr({
                                       "stroke-width": 1,
                                       "fill": settings.paths[i].fillcolor.getHexString("#"),
                                       "stroke": settings.fillcolor.getHexString("#")
                                   })
                                 );
            }

            settings.set.push( settings.paper.path(outlinePath)
                               .attr({
                                   "stroke-width": 1,
                                   "stroke": settings.linecolor.getHexString("#")
                               })
                             );

        });

        ns.BarRenderer.respondsTo("generateBar", function (x, y, width, height) {
            var path = "M" + x + "," + y;
            path    += "L" + x + "," + (y + height);
            path    += "L" + (x + width) + "," + (y + height);
            path    += "L" + (x + width) + "," + y;
            path    += "Z";
            return path;
        });

        ns.BarRenderer.respondsTo("renderLegendIcon", function (graphicsContext, x, y, icon, opacity) {
            var settings = this.settings(),
                iconAttrs,
                barwidth;

            // Draw icon background (with opacity)
            graphicsContext.paper.rect(x, y, icon.width(), icon.height())
                .attr({
                    "stroke" : "#ffffff",
                    "fill" : "#ffffff"
                })
                .transform(graphicsContext.transformString);

            // TODO: need to get the fillcolor at y=0
            iconAttrs = {
                "stroke-width" : 1,
                "fill" : settings.fillcolor.getHexString("#")
            };

            if (settings.barpixelwidth < settings.hidelines) {
                iconAttrs.stroke = settings.fillcolor.getHexString("#");
            } else {
                iconAttrs.stroke = settings.linecolor.getHexString("#");
            }

            // Adjust the width of the icons bars based upon the width and height of the icon Ranges: {20, 10, 0}
            if (icon.width() > 20 || icon.height() > 20) {
                barwidth = icon.width() / 6;
            } else if (icon.width() > 10 || icon.height() > 10) {
                barwidth = icon.width() / 4;
            } else {
                barwidth = icon.width() / 4;
            }

        // If the icon is large enough draw extra bars
            if (icon.width() > 20 && icon.height() > 20) {
                graphicsContext.paper.rect((icon.width() / 4) - (barwidth / 2), 0, barwidth, icon.height() / 2)
                    .attr(iconAttrs)
                    .transform(graphicsContext.transformString + "t" + x + "," + y);

                graphicsContext.paper.rect(icon.width() - (icon.width() / 4) - (barwidth / 2), 0, barwidth, icon.height() / 3)
                    .attr(iconAttrs)
                    .transform(graphicsContext.transformString + "t" + x + "," + y);
            }

            graphicsContext.paper.rect((icon.width() / 2) - (barwidth / 2), 0, barwidth, icon.height() - (icon.height() / 4))
                .attr(iconAttrs)
                .transform(graphicsContext.transformString + "t" + x + "," + y);

        });

    });

});
<|endoftext|>


<commit_before>const jestCLI = {
  success: true,
  runCLI: jest.fn((config, rootDir, callback) => {
    callback(jestCLI.success);
  }),
};

module.exports = jestCLI;
<commit_msg>Fix the unit tests<commit_after>const jestCLI = {
  success: true,
  runCLI: jest.fn(() => new Promise((resolve, reject) => {
    if (jestCLI.success) {
      resolve();
    } else {
      reject();
    }
  })),
};

module.exports = jestCLI;
<|endoftext|>


<commit_before>/*
 * expand_selection.js
 *
 * Copyright (C) 2009-12 by RStudio, Inc.
 *
 * The Initial Developer of the Original Code is
 * Ajax.org B.V.
 * Portions created by the Initial Developer are Copyright (C) 2010
 * the Initial Developer. All Rights Reserved.
 *
 * Unless you have received this program directly from RStudio pursuant
 * to the terms of a commercial license agreement with RStudio, then
 * this program is licensed to you under the terms of version 3 of the
 * GNU Affero General Public License. This program is distributed WITHOUT
 * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
 * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
 *
 */

define("util/expand_selection", function(require, exports, module) {

var Editor = require("ace/editor").Editor;
var Range = require("ace/range").Range;
var VimProvider = require("ace/keyboard/vim");
var TokenIterator = require("ace/token_iterator").TokenIterator;
var Utils = require("mode/utils");

(function() {

   var that = this;

   function isSingleLineString(string)
   {
      if (string.length < 2)
         return false;

      var firstChar = string[0];
      if (firstChar !== "'" && firstChar !== "\"")
         return false;

      var lastChar = string[string.length - 1];
      if (lastChar !== firstChar)
         return false;

      var isEscaped = string[string.length - 2] === "\\" &&
                      string[string.length - 3] !== "\\";

      if (isEscaped)
         return false;

      return true;
   }

   var onDocumentChange = function(editor)
   {
      editor.$clearSelectionHistory();
   };

   this.$onClearSelectionHistory = function()
   {
      return that.$clearSelectionHistory();
   };

   this.$clearSelectionHistory = function()
   {
      this.$selectionRangeHistory = null;
      this.off("change", this.$clearSelectionHistory);
   };

   this.$acceptSelection = function(selection, newRange, oldRange)
   {
      if (this.$selectionRangeHistory == null)
         this.$selectionRangeHistory = [];
      this.$selectionRangeHistory.push(oldRange);

      selection.setSelectionRange(newRange);
      if (!(this.isRowFullyVisible(newRange.start.row) &&
            this.isRowFullyVisible(newRange.end.row)))
      {
         this.centerSelection(selection);
      }

      return newRange;
   };

   this.$expandSelection = function()
   {
      // Extract some useful objects / variables
      var session = this.getSession();
      var selection = this.getSelection();
      var range = selection.getRange();

      this.on("change", this.$onClearSelectionHistory);

      // Place a token iterator at the cursor position
      var position = range.start;
      var iterator = new TokenIterator(session, position.row, position.column);
      var token = iterator.getCurrentToken();

      // A null token implies the document is empty.
      if (token == null)
         return false;

      // If we currently have no selection, select the current word.
      if (selection.isEmpty())
      {
         var prevChar = session.getLine(range.start.row)[range.start.column - 1];
         if (prevChar !== ' ' &&
             prevChar !== '\n' &&
             prevChar !== '\t')
         {
            this.navigateWordLeft();
         }
         selection.selectWordRight();
         return this.$acceptSelection(selection, selection.getRange(), range);
      }

      // If the current token is a string and the current selection lies within
      // the string, then expand to select the string.
      var candidate;
      if (token.type === "string" && isSingleLineString(token.value))
      {
         candidate = iterator.getCurrentTokenRange();
         if (candidate.containsRange(range) && !range.isEqual(candidate))
            return this.$acceptSelection(selection, candidate, range);
      }

      // Handle 'small' expansions of the current
      // selection.
      var prevToken = iterator.peekBwd(1);
      if (prevToken.type === "support.function.codebegin") {

         var startPos = {
            row: range.start.row - 1,
            column: 0
         };

         var endPos = {
            row: range.end.row + 1,
            column: iterator.$session.getLine(range.end.row + 1).length
         };

         candidate = Range.fromPoints(startPos, endPos);
         return this.$acceptSelection(selection, candidate, range);
      }

      if (Utils.isOpeningBracket(token.value)) {
         clone = iterator.clone();
         var startPos = iterator.getCurrentTokenPosition();
         if (clone.fwdToMatchingToken()) {
            candidate = Range.fromPoints(range.start, range.end);
            candidate.start.column--;
            candidate.end.column++;
            return this.$acceptSelection(selection, candidate, range);
         }
      }

      // Look for matching bracket pairs.
      while ((token = iterator.stepBackward()))
      {
         if (token == null)
            break;

         if (iterator.bwdToMatchingToken())
            continue;

         if (Utils.isOpeningBracket(token.value) ||
             token.type === "support.function.codebegin")
         {
            var startPos = iterator.getCurrentTokenPosition();
            if (token.type === "support.function.codebegin") {
               startPos.row++;
               startPos.column = 0;
            } else {
               startPos.column += token.value.length;
            }

            var clone = iterator.clone();
            if (clone.fwdToMatchingToken()) {
               var endPos = clone.getCurrentTokenPosition();
               if (token.type === "support.function.codebegin") {
                   endPos.row--;
                   endPos.column = iterator.$session.getLine(startPos.row).length;
               }
               candidate = Range.fromPoints(startPos, endPos);
               if (!range.isEqual(candidate))
                  return this.$acceptSelection(selection, candidate, range);
            }

         }
      }

      // If we get here, just select everything.
      selection.selectAll();

      // If the new selection is not equal to the previous
      // selection, add it to the selection history.
      if (!this.getSelectionRange().isEqual(range))
         return this.$acceptSelection(selection, selection.getRange(), range);

      return true;
   };

   this.$shrinkSelection = function()
   {
      var history = this.$selectionRangeHistory;
      if (history && history.length) {
          var range = history.pop();
          this.getSelection().setSelectionRange(range);
          if (!(this.isRowFullyVisible(range.start.row) &&
                this.isRowFullyVisible(range.end.row)))
          {
             this.centerSelection(this.getSelection());
          }
          return range;
      }
      this.off("change", this.$onClearSelectionHistory);
   };

}).call(Editor.prototype);

});
<commit_msg>add comment blocks to 'expand selection' util<commit_after>/*
 * expand_selection.js
 *
 * Copyright (C) 2009-12 by RStudio, Inc.
 *
 * The Initial Developer of the Original Code is
 * Ajax.org B.V.
 * Portions created by the Initial Developer are Copyright (C) 2010
 * the Initial Developer. All Rights Reserved.
 *
 * Unless you have received this program directly from RStudio pursuant
 * to the terms of a commercial license agreement with RStudio, then
 * this program is licensed to you under the terms of version 3 of the
 * GNU Affero General Public License. This program is distributed WITHOUT
 * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
 * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
 *
 */

define("util/expand_selection", function(require, exports, module) {

var Editor = require("ace/editor").Editor;
var Range = require("ace/range").Range;
var VimProvider = require("ace/keyboard/vim");
var TokenIterator = require("ace/token_iterator").TokenIterator;
var Utils = require("mode/utils");

(function() {

   var that = this;

   function isComment(editor, row)
   {
      var token = editor.getSession().getTokenAt(row, 0);
      if (token === null)
         return false;

      return /\bcomment\b/.test(token.type);
   }

   function commentRange(editor)
   {
      var startRow = editor.getCursorPosition().row;
      var endRow = startRow;

      while (isComment(editor, startRow))
         startRow--;

      while (isComment(editor, endRow))
         endRow++;

      return new Range(startRow + 1, 0, endRow, 0);
   }

   function isSingleLineString(string)
   {
      if (string.length < 2)
         return false;

      var firstChar = string[0];
      if (firstChar !== "'" && firstChar !== "\"")
         return false;

      var lastChar = string[string.length - 1];
      if (lastChar !== firstChar)
         return false;

      var isEscaped = string[string.length - 2] === "\\" &&
                      string[string.length - 3] !== "\\";

      if (isEscaped)
         return false;

      return true;
   }

   var onDocumentChange = function(editor)
   {
      editor.$clearSelectionHistory();
   };

   this.$onClearSelectionHistory = function()
   {
      return that.$clearSelectionHistory();
   };

   this.$clearSelectionHistory = function()
   {
      this.$selectionRangeHistory = null;
      this.off("change", this.$clearSelectionHistory);
   };

   this.$acceptSelection = function(selection, newRange, oldRange)
   {
      if (this.$selectionRangeHistory == null)
         this.$selectionRangeHistory = [];
      this.$selectionRangeHistory.push(oldRange);

      selection.setSelectionRange(newRange);
      if (!(this.isRowFullyVisible(newRange.start.row) &&
            this.isRowFullyVisible(newRange.end.row)))
      {
         this.centerSelection(selection);
      }

      return newRange;
   };

   this.$expandSelection = function()
   {
      // Extract some useful objects / variables
      var session = this.getSession();
      var selection = this.getSelection();
      var range = selection.getRange();

      this.on("change", this.$onClearSelectionHistory);

      // Place a token iterator at the cursor position
      var position = range.start;
      var iterator = new TokenIterator(session, position.row, position.column);
      var token = iterator.getCurrentToken();

      // A null token implies the document is empty.
      if (token == null)
         return false;

      // If we currently have no selection, select the current word.
      if (selection.isEmpty())
      {
         var prevChar = session.getLine(range.start.row)[range.start.column - 1];
         if (prevChar !== ' ' &&
             prevChar !== '\n' &&
             prevChar !== '\t')
         {
            this.navigateWordLeft();
         }
         selection.selectWordRight();
         return this.$acceptSelection(selection, selection.getRange(), range);
      }

      // If the current token is a string and the current selection lies within
      // the string, then expand to select the string.
      var candidate;
      if (token.type === "string" && isSingleLineString(token.value))
      {
         candidate = iterator.getCurrentTokenRange();
         if (candidate.containsRange(range) && !range.isEqual(candidate))
            return this.$acceptSelection(selection, candidate, range);
      }

      // Handle 'small' expansions of the current
      // selection.
      var prevToken = iterator.peekBwd(1);
      if (prevToken.type === "support.function.codebegin") {

         var startPos = {
            row: range.start.row - 1,
            column: 0
         };

         var endPos = {
            row: range.end.row + 1,
            column: iterator.$session.getLine(range.end.row + 1).length
         };

         candidate = Range.fromPoints(startPos, endPos);
         return this.$acceptSelection(selection, candidate, range);
      }

      if (Utils.isOpeningBracket(token.value)) {
         clone = iterator.clone();
         var startPos = iterator.getCurrentTokenPosition();
         if (clone.fwdToMatchingToken()) {
            candidate = Range.fromPoints(range.start, range.end);
            candidate.start.column--;
            candidate.end.column++;
            return this.$acceptSelection(selection, candidate, range);
         }
      }

      // If the current selection is in, or contains, a comment block,
      // expand selection to entire comment block.
      if (/\bcomment\b/.test(token.type))
      {
         var candidate = commentRange(this);
         selection.setSelectionRange(range);
         return this.$acceptSelection(selection, candidate, range);
      }

      // Look for matching bracket pairs.
      while ((token = iterator.stepBackward()))
      {
         if (token == null)
            break;

         if (iterator.bwdToMatchingToken())
            continue;

         if (Utils.isOpeningBracket(token.value) ||
             token.type === "support.function.codebegin")
         {
            var startPos = iterator.getCurrentTokenPosition();
            if (token.type === "support.function.codebegin") {
               startPos.row++;
               startPos.column = 0;
            } else {
               startPos.column += token.value.length;
            }

            var clone = iterator.clone();
            if (clone.fwdToMatchingToken()) {
               var endPos = clone.getCurrentTokenPosition();
               if (token.type === "support.function.codebegin") {
                   endPos.row--;
                   endPos.column = iterator.$session.getLine(startPos.row).length;
               }
               candidate = Range.fromPoints(startPos, endPos);
               if (!range.isEqual(candidate))
                  return this.$acceptSelection(selection, candidate, range);
            }

         }
      }

      // If we get here, just select everything.
      selection.selectAll();

      // If the new selection is not equal to the previous
      // selection, add it to the selection history.
      if (!this.getSelectionRange().isEqual(range))
         return this.$acceptSelection(selection, selection.getRange(), range);

      return true;
   };

   this.$shrinkSelection = function()
   {
      var history = this.$selectionRangeHistory;
      if (history && history.length) {
          var range = history.pop();
          this.getSelection().setSelectionRange(range);
          if (!(this.isRowFullyVisible(range.start.row) &&
                this.isRowFullyVisible(range.end.row)))
          {
             this.centerSelection(this.getSelection());
          }
          return range;
      }
      this.off("change", this.$onClearSelectionHistory);
   };

}).call(Editor.prototype);

});
<|endoftext|>


<commit_before>/* Task.js
**
** Logic for client interactions with the MuckRock task system.
** Mostly involves submitting task forms via AJAX and handling successful responses.
*/

import Cookie from 'js-cookie';

function authenticateAjax() {
    // Sets up authentication for AJAX transactions
    var csrftoken = Cookie.get('csrftoken');
    function csrfSafeMethod(method) {
        // these HTTP methods do not require CSRF protection
        return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
    }
    $.ajaxSetup({
        beforeSend: function(xhr, settings) {
            if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
                xhr.setRequestHeader("X-CSRFToken", csrftoken);
            }
        }
    });
}

function ajaxPost(task, endpoint, data) {
    var pendingOverlay = $(task).children('.pending.overlay');
    var errorOverlay = $(task).children('.error.overlay');
    $(document).ajaxStart(function(){
        $(pendingOverlay).addClass('visible');
    }).ajaxError(function(){
        $(pendingOverlay).removeClass('visible');
        $(errorOverlay).addClass('visible');
        setTimeout(function(){
            $(errorOverlay).removeClass('visible');
        }, 3000);
        $(document).off('ajaxStart').off('ajaxError').off('ajaxComplete');
    }).ajaxComplete(function(){
        $(pendingOverlay).removeClass('visible');
        markAsResolved(task);
        $(document).off('ajaxStart').off('ajaxError').off('ajaxComplete');
    });
    $.ajax({
        url: endpoint,
        type: 'post',
        data: data,
        success: null
    });
}

/*
Need to add the action as a value since the button is being overridden.
In a non-JS form submission, the button would also include its value in the posted data.
*/

function batchAction(forms, action) {
    var taskID;
    var taskIDs = [];
    var taskData = {'tasks': []};
    taskData[action] = true;
    $(forms).each(function() {
        taskID = getTaskID($(this).serializeArray());
        taskIDs.push('#' + taskID + '-task');
        taskData['tasks'].push(taskID);
    });
    if (forms.length > 0) {
        var taskEndpoint = $(forms[0]).attr('action');
        ajaxPost(taskIDs.join(', '), taskEndpoint, taskData);
    }
}

function singleAction(taskForm, action) {
    var taskID = '#' + getTaskID($(taskForm).serializeArray()) + '-task';
    var taskData = $(taskForm).serialize() + '&' + action + '=true';
    var taskEndpoint = $(taskForm).attr('action');
    ajaxPost(taskID, taskEndpoint, taskData);
}

function getTaskID(taskFormData) {
    // Gets the task ID from a task form
    var taskID;
    $.each(taskFormData, function(i, input){
        if (input.name == 'task') {
            taskID = input.value;
        }
    });
    return taskID;
}

function markAsResolved(task) {
    $(task).addClass('green');
}

function formHasAction(taskForm, action) {
    // checks whether the task form has a 'Resolve' action or not
    var actionExists = false;
    var actionButton = 'button[name="' + action + '"]';
    if ($(taskForm).children(actionButton).length) {
        actionExists = true;
    }
    return actionExists;
}

function checkPdfExists(pdfName) {
  var url = 'https://muckrock.s3.amazonaws.com/' + pdfName;
  $.ajax({
    url: url,
    type: 'head',
    success: function() {
      $('#snail-mail-bulk-download').text('Downloading');
      window.location.href = url;
    },
    error: function() {
      setTimeout(checkPdfExists, 5000, pdfName);
    }
  });
}

////////////////////////////////////////////////////////////////////////////////

$('document').ready(function(){
  authenticateAjax();

  // Hide all the resolved tasks
  $('.resolved.task')
      .each(function(){
          markAsResolved(this);
      })
      .addClass('collapsed');

  /* TODO
  ** This should be rewritten to bind to the task's form submission event,
  ** not the form's resolve button click.
  */

  function ajaxSubmit(action) {
    return function(e) {
      /* If the button clicked is the "resolve all" button, then get forms
      for all the currently checked tasks. Else, just get the form for the
      task that owns the button. */
      e.preventDefault();
      var forms = [];
      if ($(this).attr('id') == 'batched-' + action) {
          $(':checked[form=batched]').each(function() {
              var taskForm = $(this).closest('.task').find('form');
              // the form needs to have a resolve action in order to be added
              if (formHasAction(taskForm, action)) {
                  forms.push(taskForm);
              }
          });
          batchAction(forms, action);
      } else {
          singleAction($(this).closest('form'), action);
      }
      return false;
    };
  }

  var actions = ['resolve', 'reject', 'spam', 'defer',
    'approve', 'save', 'move', 'no_mail'];
  for (var i = 0; i < actions.length; i++) {
    $('button[name="'+ actions[i] +'"]').click(ajaxSubmit(actions[i]));
  }

  var checkboxes = $('.task header').find(':checkbox');
  var batchedButtons = $('#batched button').not('#collapse-all');
  function toggleBatchedButtons() {
      if ($('.task header').find(':checked').length > 0) {
          batchedButtons.attr('disabled', false);
      } else {
          batchedButtons.attr('disabled', true);
      }
  }
  batchedButtons.attr('disabled', true);
  checkboxes.change(toggleBatchedButtons);

  $('#collapse-all').click(function(e){
      e.preventDefault();
      if ($(this).text() == "Collapse All") {
          $(this).text("Reveal All");
          $('.task').addClass('collapsed');
      } else {
          $(this).text("Collapse All");
          $('.task').removeClass('collapsed');
      }
  });

  $('.reveal-extra-context').click(function(e){
      e.preventDefault();
      if ($(this).data['state']) {
          // if on, turn off
          $(this).data['state'] = 0;
          $(this).text("More Info");
          $(this).next().addClass('hidden');
      } else {
          // if off, turn on
          $(this).data['state'] = 1;
          $(this).text("Less Info");
          $(this).next().removeClass('hidden');
      }
  });

  $('#snail-mail-bulk-download').click(function(){
    $(this).prop('disabled', 'disabled');
    $(this).text('Generating...');
    $.ajax({
      url: '/task/snail-mail/pdf/',
      data: window.location.search.substr(1),
      type: 'get',
      success: function(data) {
        checkPdfExists(data['pdf_name']);
      },
      error: function() {
        $(this.text('Error!'));
      }
    });
  });

  $('select.assigned-chooser').change(function(){
    $.ajax({
      url: '/task/assign-to/',
      data: {
        task_pk: $(this).prop('name'),
        asignee: $(this).val()
      },
      type: 'post'
    });
  });

  // New Agency replacement agency chooser
  $(".new-agency-forms .new-agency.red select[name=replace_jurisdiction]").change(function() {
    var val = $(this).val();
    var agency = $(this).closest("main").find(".autocomplete-light-widget.replace_agency");
    if (val) {
      agency.yourlabsWidget().autocomplete.data = {
        'jurisdiction_id': val[0]
      };
    } else {
      agency.yourlabsWidget().autocomplete.data = {};
    }
  });
  $(".new-agency-forms .new-agency.red select[name=replace_jurisdiction]").change();

  // Defered loading of review agency tasks
  $(".review-agency-placeholder").each(function() {
    var id = $(this).data('id');
    var that = this;
    console.log(id);
    $.ajax({
      url: '/task/review-agency-ajax/' + id + '/',
      type: 'get',
      success: function(data) {
        $(that).html(data);
      },
      error: function() {
        $(that).html("
Error
");
      }
    });
  });

});
<commit_msg>eslint<commit_after>/* Task.js
**
** Logic for client interactions with the MuckRock task system.
** Mostly involves submitting task forms via AJAX and handling successful responses.
*/

import Cookie from 'js-cookie';

function authenticateAjax() {
    // Sets up authentication for AJAX transactions
    var csrftoken = Cookie.get('csrftoken');
    function csrfSafeMethod(method) {
        // these HTTP methods do not require CSRF protection
        return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
    }
    $.ajaxSetup({
        beforeSend: function(xhr, settings) {
            if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
                xhr.setRequestHeader("X-CSRFToken", csrftoken);
            }
        }
    });
}

function ajaxPost(task, endpoint, data) {
    var pendingOverlay = $(task).children('.pending.overlay');
    var errorOverlay = $(task).children('.error.overlay');
    $(document).ajaxStart(function(){
        $(pendingOverlay).addClass('visible');
    }).ajaxError(function(){
        $(pendingOverlay).removeClass('visible');
        $(errorOverlay).addClass('visible');
        setTimeout(function(){
            $(errorOverlay).removeClass('visible');
        }, 3000);
        $(document).off('ajaxStart').off('ajaxError').off('ajaxComplete');
    }).ajaxComplete(function(){
        $(pendingOverlay).removeClass('visible');
        markAsResolved(task);
        $(document).off('ajaxStart').off('ajaxError').off('ajaxComplete');
    });
    $.ajax({
        url: endpoint,
        type: 'post',
        data: data,
        success: null
    });
}

/*
Need to add the action as a value since the button is being overridden.
In a non-JS form submission, the button would also include its value in the posted data.
*/

function batchAction(forms, action) {
    var taskID;
    var taskIDs = [];
    var taskData = {'tasks': []};
    taskData[action] = true;
    $(forms).each(function() {
        taskID = getTaskID($(this).serializeArray());
        taskIDs.push('#' + taskID + '-task');
        taskData['tasks'].push(taskID);
    });
    if (forms.length > 0) {
        var taskEndpoint = $(forms[0]).attr('action');
        ajaxPost(taskIDs.join(', '), taskEndpoint, taskData);
    }
}

function singleAction(taskForm, action) {
    var taskID = '#' + getTaskID($(taskForm).serializeArray()) + '-task';
    var taskData = $(taskForm).serialize() + '&' + action + '=true';
    var taskEndpoint = $(taskForm).attr('action');
    ajaxPost(taskID, taskEndpoint, taskData);
}

function getTaskID(taskFormData) {
    // Gets the task ID from a task form
    var taskID;
    $.each(taskFormData, function(i, input){
        if (input.name == 'task') {
            taskID = input.value;
        }
    });
    return taskID;
}

function markAsResolved(task) {
    $(task).addClass('green');
}

function formHasAction(taskForm, action) {
    // checks whether the task form has a 'Resolve' action or not
    var actionExists = false;
    var actionButton = 'button[name="' + action + '"]';
    if ($(taskForm).children(actionButton).length) {
        actionExists = true;
    }
    return actionExists;
}

function checkPdfExists(pdfName) {
  var url = 'https://muckrock.s3.amazonaws.com/' + pdfName;
  $.ajax({
    url: url,
    type: 'head',
    success: function() {
      $('#snail-mail-bulk-download').text('Downloading');
      window.location.href = url;
    },
    error: function() {
      setTimeout(checkPdfExists, 5000, pdfName);
    }
  });
}

////////////////////////////////////////////////////////////////////////////////

$('document').ready(function(){
  authenticateAjax();

  // Hide all the resolved tasks
  $('.resolved.task')
      .each(function(){
          markAsResolved(this);
      })
      .addClass('collapsed');

  /* TODO
  ** This should be rewritten to bind to the task's form submission event,
  ** not the form's resolve button click.
  */

  function ajaxSubmit(action) {
    return function(e) {
      /* If the button clicked is the "resolve all" button, then get forms
      for all the currently checked tasks. Else, just get the form for the
      task that owns the button. */
      e.preventDefault();
      var forms = [];
      if ($(this).attr('id') == 'batched-' + action) {
          $(':checked[form=batched]').each(function() {
              var taskForm = $(this).closest('.task').find('form');
              // the form needs to have a resolve action in order to be added
              if (formHasAction(taskForm, action)) {
                  forms.push(taskForm);
              }
          });
          batchAction(forms, action);
      } else {
          singleAction($(this).closest('form'), action);
      }
      return false;
    };
  }

  var actions = ['resolve', 'reject', 'spam', 'defer',
    'approve', 'save', 'move', 'no_mail'];
  for (var i = 0; i < actions.length; i++) {
    $('button[name="'+ actions[i] +'"]').click(ajaxSubmit(actions[i]));
  }

  var checkboxes = $('.task header').find(':checkbox');
  var batchedButtons = $('#batched button').not('#collapse-all');
  function toggleBatchedButtons() {
      if ($('.task header').find(':checked').length > 0) {
          batchedButtons.attr('disabled', false);
      } else {
          batchedButtons.attr('disabled', true);
      }
  }
  batchedButtons.attr('disabled', true);
  checkboxes.change(toggleBatchedButtons);

  $('#collapse-all').click(function(e){
      e.preventDefault();
      if ($(this).text() == "Collapse All") {
          $(this).text("Reveal All");
          $('.task').addClass('collapsed');
      } else {
          $(this).text("Collapse All");
          $('.task').removeClass('collapsed');
      }
  });

  $('.reveal-extra-context').click(function(e){
      e.preventDefault();
      if ($(this).data['state']) {
          // if on, turn off
          $(this).data['state'] = 0;
          $(this).text("More Info");
          $(this).next().addClass('hidden');
      } else {
          // if off, turn on
          $(this).data['state'] = 1;
          $(this).text("Less Info");
          $(this).next().removeClass('hidden');
      }
  });

  $('#snail-mail-bulk-download').click(function(){
    $(this).prop('disabled', 'disabled');
    $(this).text('Generating...');
    $.ajax({
      url: '/task/snail-mail/pdf/',
      data: window.location.search.substr(1),
      type: 'get',
      success: function(data) {
        checkPdfExists(data['pdf_name']);
      },
      error: function() {
        $(this.text('Error!'));
      }
    });
  });

  $('select.assigned-chooser').change(function(){
    $.ajax({
      url: '/task/assign-to/',
      data: {
        task_pk: $(this).prop('name'),
        asignee: $(this).val()
      },
      type: 'post'
    });
  });

  // New Agency replacement agency chooser
  $(".new-agency-forms .new-agency.red select[name=replace_jurisdiction]").change(function() {
    var val = $(this).val();
    var agency = $(this).closest("main").find(".autocomplete-light-widget.replace_agency");
    if (val) {
      agency.yourlabsWidget().autocomplete.data = {
        'jurisdiction_id': val[0]
      };
    } else {
      agency.yourlabsWidget().autocomplete.data = {};
    }
  });
  $(".new-agency-forms .new-agency.red select[name=replace_jurisdiction]").change();

  // Defered loading of review agency tasks
  $(".review-agency-placeholder").each(function() {
    var id = $(this).data('id');
    var that = this;
    $.ajax({
      url: '/task/review-agency-ajax/' + id + '/',
      type: 'get',
      success: function(data) {
        $(that).html(data);
      },
      error: function() {
        $(that).html("
Error
");
      }
    });
  });

});
<|endoftext|>


<commit_before>import { merge } from './updates.js';
import * as symbols from './symbols.js';
import * as template from './template.js';
import WrappedStandardElement from './WrappedStandardElement.js';

const Base = WrappedStandardElement.wrap('input');

/**
 * Base class for custom input elements
 *
 * @inherits WrappedStandardElement
 */
class Input extends Base {

  componentDidMount() {
    if (super.componentDidMount) { super.componentDidMount(); }
    this.$.inner.addEventListener('input', () => {
      this[symbols.raiseChangeEvents] = true;
      /** @type {any} */
      const cast = this.inner;
      // Invoke the value setter to fix up selectionStart/selectionEnd too.
      this.value = cast.value;
      this[symbols.raiseChangeEvents] = false;
    });
  }

  componentDidUpdate(previousState) {
    if (super.componentDidUpdate) { super.componentDidUpdate(previousState); }
    const value = this.state.value;
    const changed = value !== previousState.value;
    if (changed && this[symbols.raiseChangeEvents]) {
      const event = new CustomEvent('value-changed', {
        detail: { value }
      });
      this.dispatchEvent(event);
    }
  }

  get [symbols.template]() {
    return template.html`
      <style>
        :host {
          display: inline-block;
        }

        #inner {
          box-sizing: border-box;
          font-family: inherit;
          font-size: inherit;
          font-style: inherit;
          font-weight: inherit;
          height: 100%;
          width: 100%;
        }
      </style>

      <input id="inner">
        <slot></slot>
      </input>
    `;
  }

  get updates() {
    // The base class wants to update the inner input element's value to match
    // the current state. However, in Safari, even if the value hasn't actually
    // changed, updating the value will still collapse the selection. Chrome's
    // input handles this as we'd like: setting the value will leave the
    // selection unaffected if the value is the same as before. We want to
    // emulate Chrome's behavior. So if the value doesn't actually need to be
    // updated, we remove it from the updates. We normally don't do this kind of
    // check, but here we need to do it to preserve selection in Safari.
    const base = super.updates;
    const value = this.state.innerProperties.value;
    if (this.$.inner.value === value && base.$.inner.value === value) {
      delete base.$.inner.value;
    }
    return merge(base, {
      attributes: {
        'aria-hidden': 'true'
      }
    });
  }

  // Updating the value can also update the selectionStart and selectionEnd
  // properties, so we have to update our state to match.
  get value() {
    return super.value;
  }
  set value(value) {
    // @ts-ignore
    super.value = value;
    /** @type {any} */
    const cast = this.inner;
    this.setInnerProperty('selectionStart', cast.selectionStart);
    this.setInnerProperty('selectionEnd', cast.selectionEnd);
  }

}

export default Input;
customElements.define('elix-input', Input);
<commit_msg>Read value out of innerProperties.<commit_after>import { merge } from './updates.js';
import * as symbols from './symbols.js';
import * as template from './template.js';
import WrappedStandardElement from './WrappedStandardElement.js';

const Base = WrappedStandardElement.wrap('input');

/**
 * Base class for custom input elements
 *
 * @inherits WrappedStandardElement
 */
class Input extends Base {

  componentDidMount() {
    if (super.componentDidMount) { super.componentDidMount(); }
    this.$.inner.addEventListener('input', () => {
      this[symbols.raiseChangeEvents] = true;
      /** @type {any} */
      const cast = this.inner;
      // Invoke the value setter to fix up selectionStart/selectionEnd too.
      this.value = cast.value;
      this[symbols.raiseChangeEvents] = false;
    });
  }

  componentDidUpdate(previousState) {
    if (super.componentDidUpdate) { super.componentDidUpdate(previousState); }
    const value = this.state.innerProperties.value;
    const changed = value !== previousState.innerProperties.value;
    if (changed && this[symbols.raiseChangeEvents]) {
      const event = new CustomEvent('value-changed', {
        detail: { value }
      });
      this.dispatchEvent(event);
    }
  }

  get [symbols.template]() {
    return template.html`
      <style>
        :host {
          display: inline-block;
        }

        #inner {
          box-sizing: border-box;
          font-family: inherit;
          font-size: inherit;
          font-style: inherit;
          font-weight: inherit;
          height: 100%;
          width: 100%;
        }
      </style>

      <input id="inner">
        <slot></slot>
      </input>
    `;
  }

  get updates() {
    // The base class wants to update the inner input element's value to match
    // the current state. However, in Safari, even if the value hasn't actually
    // changed, updating the value will still collapse the selection. Chrome's
    // input handles this as we'd like: setting the value will leave the
    // selection unaffected if the value is the same as before. We want to
    // emulate Chrome's behavior. So if the value doesn't actually need to be
    // updated, we remove it from the updates. We normally don't do this kind of
    // check, but here we need to do it to preserve selection in Safari.
    const base = super.updates;
    const value = this.state.innerProperties.value;
    if (this.$.inner.value === value && base.$.inner.value === value) {
      delete base.$.inner.value;
    }
    return merge(base, {
      attributes: {
        'aria-hidden': 'true'
      }
    });
  }

  // Updating the value can also update the selectionStart and selectionEnd
  // properties, so we have to update our state to match.
  get value() {
    return super.value;
  }
  set value(value) {
    // @ts-ignore
    super.value = value;
    if (this.shadowRoot) {
      /** @type {any} */
      const cast = this.inner;
      this.setInnerProperty('selectionStart', cast.selectionStart);
      this.setInnerProperty('selectionEnd', cast.selectionEnd);
    }
  }

}

export default Input;
customElements.define('elix-input', Input);
<|endoftext|>


<commit_before>var unparse = require('unparse-args');
var keypress = require('keypress');

var MultiView = require('../main');
var Server = require('./Server');
var Streamer = require('./Streamer');
var Spawn = require('../lib/Spawn');

module.exports = function(args, flags) {

    var channel = flags.channel || 'multiview_main';

    if (flags.help || flags.version) {
        // don't do anything if help selected
    } else if (flags.stream) {
        setStream(args, flags, channel);
    } else {
        setMain(args, flags, channel);
    }
};

function setStream(args, flags, channel) {

    var name = typeof flags.stream === 'string' ? flags.stream : 'PID:' + process.pid;
    var command;
    var command_args;
    var streamer;
    var streamers = [];
    var streamerCount = 0;
    var streamerEnded = 0;
    var unparsed_args;

    var stdin_streamer = new Streamer(name, channel, {
        logConnectMessages: false
    });

    streamers.push(stdin_streamer);

    process.stdin.pipe(stdin_streamer, {
        end: false
    });

    process.stdin.on('end', function() {
        stdin_streamer.exit(0);
    });

    for (var i = 0; i < args.length; i++) {

        if (typeof args[i]._ === 'object') {

            unparsed_args = unparse(args[i]);
            command = unparsed_args[0];
            command_args = unparsed_args.slice(1);

            streamer = new Streamer(unparsed_args.join(' '), channel);
            streamers.push(streamer);

            new Spawn(
                streamer,
                command,
                command_args,
                command + (command_args ? ' ' + command_args.join(' ') : '')
            );
        }
    }

    for (var i = 0; i < streamers.length; i++) {

        streamers[i].on('socketBegun', function() {
            streamerCount++;
        });

        streamers[i].on('socketEnded', function() {
            streamerEnded++;
            if (streamerEnded === streamerCount) {
                process.exit(0);
            }
        });
    }
}

function setMain(args, flags, channel) {

    var mv = new MultiView(flags);
    var server = new Server(mv, channel);
    var exitCount = 0;
    var exitCode = 0;
    var autoexit = flags.autoexit !== undefined ? (typeof flags.autoexit === 'number' ? flags.autoexit : 500) : false;

    var argGroups;

    mv.on('exit', function(stream, code) {
        exitCount++;

        if (exitCode === 0 && code !== 0) {
            exitCode = code;
        }
    });

    if (autoexit !== false) {
        mv.on('exit', function(stream, code) {
            setTimeout(function() {

                if (exitCount === mv.streams.length) {
                    process.exit(exitCode);
                }

            }, autoexit);
        });
    }

    if (process.stdin.isTTY) {

        keypress(process.stdin);
        process.stdin.setRawMode(true);

        process.stdin.on('keypress', function(ch, key) {
            if (key && ((key.ctrl && key.name === 'c') || key.name === 'q')) {
                process.stdin.pause();
                process.exit(exitCode);
            }
        });
    }

    argGroups = buildSpawnOrder(args);
    spawnArgGroups(mv, argGroups);
}

function buildSpawnOrder(args) {

    console.log("building spawn order", args);

    var order = [
        []
    ];
    var i = 0;
    var j = 0;

    for (i = 0; i < args.length; i++) {

        if (typeof args[i]._ === 'object') {
            order[j].push(unparse(args[i]));
        }
        if (args[i + 1] !== 'PIPE' && i < args.length - 1) {
            j++;
            order[j] = [];
        } else {
            i++;
        }
    }

    console.log("Ordered", order);

    return order;
}

function spawnArgGroups(mv, argGroups) {

    var args;
    var unparsed_args;
    var piper;
    var options;
    var piper_name;

    var i;
    var j;

    for (i = 0; i < argGroups.length; i++) {

        args = argGroups[i];
        piper_name = generateName(args);
        piper = null;

        for (j = args.length - 1; j >= 0; j--) {
            options = {};
            unparsed_args = args[j];

            if (piper) {
                options.stdout = piper;
                options.silent = true;
            } else {
                options.name = piper_name;
            }

            piper = mv.spawn(unparsed_args[0], unparsed_args.slice(1), options);
        }
    }
}

function generateName(args) {
    var name = '';
    var i;

    for (i = 0; i < args.length; i++) {
        name += (i > 0 ? ' | ' : '') + args[i].join(' ');
    }

    return name;
}
<commit_msg>Removing console.log.<commit_after>var unparse = require('unparse-args');
var keypress = require('keypress');

var MultiView = require('../main');
var Server = require('./Server');
var Streamer = require('./Streamer');
var Spawn = require('../lib/Spawn');

module.exports = function(args, flags) {

    var channel = flags.channel || 'multiview_main';

    if (flags.help || flags.version) {
        // don't do anything if help selected
    } else if (flags.stream) {
        setStream(args, flags, channel);
    } else {
        setMain(args, flags, channel);
    }
};

function setStream(args, flags, channel) {

    var name = typeof flags.stream === 'string' ? flags.stream : 'PID:' + process.pid;
    var command;
    var command_args;
    var streamer;
    var streamers = [];
    var streamerCount = 0;
    var streamerEnded = 0;
    var unparsed_args;

    var stdin_streamer = new Streamer(name, channel, {
        logConnectMessages: false
    });

    streamers.push(stdin_streamer);

    process.stdin.pipe(stdin_streamer, {
        end: false
    });

    process.stdin.on('end', function() {
        stdin_streamer.exit(0);
    });

    for (var i = 0; i < args.length; i++) {

        if (typeof args[i]._ === 'object') {

            unparsed_args = unparse(args[i]);
            command = unparsed_args[0];
            command_args = unparsed_args.slice(1);

            streamer = new Streamer(unparsed_args.join(' '), channel);
            streamers.push(streamer);

            new Spawn(
                streamer,
                command,
                command_args,
                command + (command_args ? ' ' + command_args.join(' ') : '')
            );
        }
    }

    for (var i = 0; i < streamers.length; i++) {

        streamers[i].on('socketBegun', function() {
            streamerCount++;
        });

        streamers[i].on('socketEnded', function() {
            streamerEnded++;
            if (streamerEnded === streamerCount) {
                process.exit(0);
            }
        });
    }
}

function setMain(args, flags, channel) {

    var mv = new MultiView(flags);
    var server = new Server(mv, channel);
    var exitCount = 0;
    var exitCode = 0;
    var autoexit = flags.autoexit !== undefined ? (typeof flags.autoexit === 'number' ? flags.autoexit : 500) : false;

    var argGroups;

    mv.on('exit', function(stream, code) {
        exitCount++;

        if (exitCode === 0 && code !== 0) {
            exitCode = code;
        }
    });

    if (autoexit !== false) {
        mv.on('exit', function(stream, code) {
            setTimeout(function() {

                if (exitCount === mv.streams.length) {
                    process.exit(exitCode);
                }

            }, autoexit);
        });
    }

    if (process.stdin.isTTY) {

        keypress(process.stdin);
        process.stdin.setRawMode(true);

        process.stdin.on('keypress', function(ch, key) {
            if (key && ((key.ctrl && key.name === 'c') || key.name === 'q')) {
                process.stdin.pause();
                process.exit(exitCode);
            }
        });
    }

    argGroups = buildSpawnOrder(args);
    spawnArgGroups(mv, argGroups);
}

function buildSpawnOrder(args) {

    var order = [
        []
    ];
    var i = 0;
    var j = 0;

    for (i = 0; i < args.length; i++) {

        if (typeof args[i]._ === 'object') {
            order[j].push(unparse(args[i]));
        }
        if (args[i + 1] !== 'PIPE' && i < args.length - 1) {
            j++;
            order[j] = [];
        } else {
            i++;
        }
    }

    return order;
}

function spawnArgGroups(mv, argGroups) {

    var args;
    var unparsed_args;
    var piper;
    var options;
    var piper_name;

    var i;
    var j;

    for (i = 0; i < argGroups.length; i++) {

        args = argGroups[i];
        piper_name = generateName(args);
        piper = null;

        for (j = args.length - 1; j >= 0; j--) {
            options = {};
            unparsed_args = args[j];

            if (piper) {
                options.stdout = piper;
                options.silent = true;
            } else {
                options.name = piper_name;
            }

            piper = mv.spawn(unparsed_args[0], unparsed_args.slice(1), options);
        }
    }
}

function generateName(args) {
    var name = '';
    var i;

    for (i = 0; i < args.length; i++) {
        name += (i > 0 ? ' | ' : '') + args[i].join(' ');
    }

    return name;
}
<|endoftext|>