<commit_before>var path = require('path');
var util = require('util');
var _ = require('underscore');
var test = require('tap').test;
var yaml = require('./yaml');

// Test that only provides JS repr.
exports.withJs = function(name, js) {
  exports.dumpAndLoad(name, js);
};

// Test that provides both YAML and JS repr.
exports.withYamlAndJs = function(name, js) {
  exports.load(name, js);
  exports.dumpAndLoad(name, js);
};

// Load YAML and compare with expected JS repr.
exports.load = function(name, tags, expected) {
  if (expected === undefined) {
    expected = tags;
    tags = undefined;
  }

  test(name, function(t) {
    var file = path.resolve(module.filename, '..', 'tests', name + '.yaml');
    var input = yaml.loadFileSync(file, tags);

    t.ok(_.isEqual(input, expected), 'load YAML', {
      input: input,
      expected: util.inspect(expected)
    });
    t.end();
  });
};

// Dump JS to YAML, load again, and compare the two.
exports.dumpAndLoad = function(name, input) {
  test(name, function(t) {
    var data = yaml.dump.apply(null, input);
    var output = yaml.load(data);
    t.ok(_.isEqual(input, output), 'dump & load YAML', {
      inputJs: util.inspect(input),
      yaml: data,
      outputJs: util.inspect(output)
    });
    t.end();
  });
};
<commit_msg>Better formatting of test 'load' failure.<commit_after>var path = require('path');
var util = require('util');
var _ = require('underscore');
var test = require('tap').test;
var yaml = require('./yaml');

// Test that only provides JS repr.
exports.withJs = function(name, js) {
  exports.dumpAndLoad(name, js);
};

// Test that provides both YAML and JS repr.
exports.withYamlAndJs = function(name, js) {
  exports.load(name, js);
  exports.dumpAndLoad(name, js);
};

// Load YAML and compare with expected JS repr.
exports.load = function(name, tags, expected) {
  if (expected === undefined) {
    expected = tags;
    tags = undefined;
  }

  test(name, function(t) {
    var file = path.resolve(module.filename, '..', 'tests', name + '.yaml');
    var input = yaml.loadFileSync(file, tags);

    t.ok(_.isEqual(input, expected), 'load YAML', {
      input: util.inspect(input),
      expected: util.inspect(expected)
    });
    t.end();
  });
};

// Dump JS to YAML, load again, and compare the two.
exports.dumpAndLoad = function(name, input) {
  test(name, function(t) {
    var data = yaml.dump.apply(null, input);
    var output = yaml.load(data);
    t.ok(_.isEqual(input, output), 'dump & load YAML', {
      inputJs: util.inspect(input),
      yaml: data,
      outputJs: util.inspect(output)
    });
    t.end();
  });
};
<|endoftext|>


<commit_before>const Reflux = require('reflux');
const { NamespaceStore } = require('mongodb-reflux-store');

/**
 * Sets global collection information.
 */
const CollectionStore = Reflux.createStore({

  /**
   * Initialize the store.
   */
  init() {
    this.collection = {};
    this.activeTabIndex = 0;
  },

  /**
   * Set the collection information.
   *
   * @param {Object} collection - The collection info.
   */
  setCollection(collection) {
    this.collection = collection;
    if (collection._id) {
      NamespaceStore.ns = collection._id;
    }
  },

  /**
   * Set the active tab idx for the current collection
   * @param {number} idx current tab idx
   */
  setActiveTab(idx) {
    this.activeTabIndex = idx;
  },

  /**
   * Get the active tab idx for the current collection
   * @returns {number} the current idx
   */
  getActiveTab() {
    return this.activeTabIndex;
  },

  /**
   * Get the collection ns.
   *
   * @returns {String} The collection ns.
   */
  ns() {
    return this.collection._id;
  },

  /**
   * Is the collection readonly?
   *
   * @returns {Boolean} If the collection is readonly.
   */
  isReadonly() {
    return this.collection.readonly;
  }
});

module.exports = CollectionStore;
<commit_msg>Remove collection store as it no longer exists<commit_after><|endoftext|>


<commit_before>import { BANNER } from '../src/mediaTypes';
import { registerBidder } from '../src/adapters/bidderFactory';
import * as utils from '../src/utils';

const BIDDER_CODE = 'triplelift';
const STR_ENDPOINT = 'https://tlx.3lift.com/header/auction?';
let gdprApplies = true;
let consentString = null;

export const tripleliftAdapterSpec = {

  code: BIDDER_CODE,
  supportedMediaTypes: [BANNER],
  isBidRequestValid: function(bid) {
    return (typeof bid.params.inventoryCode !== 'undefined');
  },

  buildRequests: function(bidRequests, bidderRequest) {
    let tlCall = STR_ENDPOINT;
    let data = _buildPostBody(bidRequests, bidderRequest);

    tlCall = utils.tryAppendQueryString(tlCall, 'lib', 'prebid');
    tlCall = utils.tryAppendQueryString(tlCall, 'v', '$prebid.version$');

    if (bidderRequest && bidderRequest.refererInfo) {
      let referrer = bidderRequest.refererInfo.referer;
      tlCall = utils.tryAppendQueryString(tlCall, 'referrer', referrer);
    }

    if (bidderRequest && bidderRequest.timeout) {
      tlCall = utils.tryAppendQueryString(tlCall, 'tmax', bidderRequest.timeout);
    }

    if (bidderRequest && bidderRequest.gdprConsent) {
      if (typeof bidderRequest.gdprConsent.gdprApplies !== 'undefined') {
        gdprApplies = bidderRequest.gdprConsent.gdprApplies;
        tlCall = utils.tryAppendQueryString(tlCall, 'gdpr', gdprApplies.toString());
      }
      if (typeof bidderRequest.gdprConsent.consentString !== 'undefined') {
        consentString = bidderRequest.gdprConsent.consentString;
        tlCall = utils.tryAppendQueryString(tlCall, 'cmp_cs', consentString);
      }
    }

    if (tlCall.lastIndexOf('&') === tlCall.length - 1) {
      tlCall = tlCall.substring(0, tlCall.length - 1);
    }
    utils.logMessage('tlCall request built: ' + tlCall);

    return {
      method: 'POST',
      url: tlCall,
      data,
      bidderRequest
    };
  },

  interpretResponse: function(serverResponse, {bidderRequest}) {
    let bids = serverResponse.body.bids || [];
    return bids.map(function(bid) {
      return _buildResponseObject(bidderRequest, bid);
    });
  },

  getUserSyncs: function(syncOptions) {
    let ibCall = '//ib.3lift.com/sync?';
    if (consentString !== null) {
      ibCall = utils.tryAppendQueryString(ibCall, 'gdpr', gdprApplies);
      ibCall = utils.tryAppendQueryString(ibCall, 'cmp_cs', consentString);
    }

    if (syncOptions.iframeEnabled) {
      return [{
        type: 'iframe',
        url: ibCall
      }];
    }
  }
}

function _buildPostBody(bidRequests, bidderRequest) {
  let data = {};
  data.imp = bidRequests.map(function(bid, index) {
    return {
      id: index,
      tagid: bid.params.inventoryCode,
      floor: bid.params.floor,
      banner: {
        format: _sizes(bid.sizes)
      }
    }
  });

  let eids = handleConsortiaUserIds(bidderRequest)
  if (eids.length > 0) {
    data.user = {
      ext: {eids}
    }
  }

  return data;
}

function _sizes(sizeArray) {
  let sizes = sizeArray.filter(_isValidSize);
  return sizes.map(function(size) {
    return {
      w: size[0],
      h: size[1]
    };
  });
}

function _isValidSize(size) {
  return (size.length === 2 && typeof size[0] === 'number' && typeof size[1] === 'number');
}

function handleConsortiaUserIds(bidderRequest) {
  let eids = [];
  if (bidderRequest.userId && bidderRequest.userId.tdid) {
    eids.push({
      source: 'adserver.org',
      uids: [{
        id: bidderRequest.userId.tdid,
        ext: {
          rtiPartner: 'TDID'
        }
      }]
    })
  }

  return eids;
}

function _buildResponseObject(bidderRequest, bid) {
  let bidResponse = {};
  let width = bid.width || 1;
  let height = bid.height || 1;
  let dealId = bid.deal_id || '';
  let creativeId = bid.crid || '';

  if (bid.cpm != 0 && bid.ad) {
    bidResponse = {
      requestId: bidderRequest.bids[bid.imp_id].bidId,
      cpm: bid.cpm,
      width: width,
      height: height,
      netRevenue: true,
      ad: bid.ad,
      creativeId: creativeId,
      dealId: dealId,
      currency: 'USD',
      ttl: 33,
    };
  };
  return bidResponse;
}

registerBidder(tripleliftAdapterSpec);
<commit_msg>tlbidadapter missing semicolons (#4076)<commit_after>import { BANNER } from '../src/mediaTypes';
import { registerBidder } from '../src/adapters/bidderFactory';
import * as utils from '../src/utils';

const BIDDER_CODE = 'triplelift';
const STR_ENDPOINT = 'https://tlx.3lift.com/header/auction?';
let gdprApplies = true;
let consentString = null;

export const tripleliftAdapterSpec = {

  code: BIDDER_CODE,
  supportedMediaTypes: [BANNER],
  isBidRequestValid: function(bid) {
    return (typeof bid.params.inventoryCode !== 'undefined');
  },

  buildRequests: function(bidRequests, bidderRequest) {
    let tlCall = STR_ENDPOINT;
    let data = _buildPostBody(bidRequests, bidderRequest);

    tlCall = utils.tryAppendQueryString(tlCall, 'lib', 'prebid');
    tlCall = utils.tryAppendQueryString(tlCall, 'v', '$prebid.version$');

    if (bidderRequest && bidderRequest.refererInfo) {
      let referrer = bidderRequest.refererInfo.referer;
      tlCall = utils.tryAppendQueryString(tlCall, 'referrer', referrer);
    }

    if (bidderRequest && bidderRequest.timeout) {
      tlCall = utils.tryAppendQueryString(tlCall, 'tmax', bidderRequest.timeout);
    }

    if (bidderRequest && bidderRequest.gdprConsent) {
      if (typeof bidderRequest.gdprConsent.gdprApplies !== 'undefined') {
        gdprApplies = bidderRequest.gdprConsent.gdprApplies;
        tlCall = utils.tryAppendQueryString(tlCall, 'gdpr', gdprApplies.toString());
      }
      if (typeof bidderRequest.gdprConsent.consentString !== 'undefined') {
        consentString = bidderRequest.gdprConsent.consentString;
        tlCall = utils.tryAppendQueryString(tlCall, 'cmp_cs', consentString);
      }
    }

    if (tlCall.lastIndexOf('&') === tlCall.length - 1) {
      tlCall = tlCall.substring(0, tlCall.length - 1);
    }
    utils.logMessage('tlCall request built: ' + tlCall);

    return {
      method: 'POST',
      url: tlCall,
      data,
      bidderRequest
    };
  },

  interpretResponse: function(serverResponse, {bidderRequest}) {
    let bids = serverResponse.body.bids || [];
    return bids.map(function(bid) {
      return _buildResponseObject(bidderRequest, bid);
    });
  },

  getUserSyncs: function(syncOptions) {
    let ibCall = '//ib.3lift.com/sync?';
    if (consentString !== null) {
      ibCall = utils.tryAppendQueryString(ibCall, 'gdpr', gdprApplies);
      ibCall = utils.tryAppendQueryString(ibCall, 'cmp_cs', consentString);
    }

    if (syncOptions.iframeEnabled) {
      return [{
        type: 'iframe',
        url: ibCall
      }];
    }
  }
}

function _buildPostBody(bidRequests, bidderRequest) {
  let data = {};
  data.imp = bidRequests.map(function(bid, index) {
    return {
      id: index,
      tagid: bid.params.inventoryCode,
      floor: bid.params.floor,
      banner: {
        format: _sizes(bid.sizes)
      }
    };
  });

  let eids = handleConsortiaUserIds(bidderRequest);
  if (eids.length > 0) {
    data.user = {
      ext: {eids}
    };
  }

  return data;
}

function _sizes(sizeArray) {
  let sizes = sizeArray.filter(_isValidSize);
  return sizes.map(function(size) {
    return {
      w: size[0],
      h: size[1]
    };
  });
}

function _isValidSize(size) {
  return (size.length === 2 && typeof size[0] === 'number' && typeof size[1] === 'number');
}

function handleConsortiaUserIds(bidderRequest) {
  let eids = [];
  if (bidderRequest.userId && bidderRequest.userId.tdid) {
    eids.push({
      source: 'adserver.org',
      uids: [{
        id: bidderRequest.userId.tdid,
        ext: {
          rtiPartner: 'TDID'
        }
      }]
    });
  }

  return eids;
}

function _buildResponseObject(bidderRequest, bid) {
  let bidResponse = {};
  let width = bid.width || 1;
  let height = bid.height || 1;
  let dealId = bid.deal_id || '';
  let creativeId = bid.crid || '';

  if (bid.cpm != 0 && bid.ad) {
    bidResponse = {
      requestId: bidderRequest.bids[bid.imp_id].bidId,
      cpm: bid.cpm,
      width: width,
      height: height,
      netRevenue: true,
      ad: bid.ad,
      creativeId: creativeId,
      dealId: dealId,
      currency: 'USD',
      ttl: 33,
    };
  };
  return bidResponse;
}

registerBidder(tripleliftAdapterSpec);
<|endoftext|>


<commit_before>import Em from 'ember';
var get = Em.get;
var set = Em.set;

var DEFAULT_OFFSET = 0;
var DEFAULT_LIMIT = Infinity;

var ArraySlice = Em.ArrayProxy.extend({
	offset: function (key, offset, old) {
		// getter
		if (arguments.length <= 1) {
			// default value
			return DEFAULT_OFFSET;
		}
		// do not allow negative offset
		offset = Math.max(offset, 0);
		// setter
		var diff = offset - old || 0;
		// no work to do
		if (diff === 0) {
			return offset;
		}
		var content = get(this, 'content');
		var limit = get(this, 'limit');
		var arranged = get(this, 'arrangedContent');
		var arrangedLength = get(arranged, 'length');
		diff = Math.min(diff, limit);
		// unshift
		arranged.replace(
			// beginning of array
			0,
			// remove count
			Math.min(diff, arrangedLength),
			// items to add
			content.slice(offset, offset - diff)
		);
		// pop
		var index = Math.min(limit, arrangedLength - diff);
		index = Math.max(index, 0);
		arranged.replace(
			// end of array
			index,
			// remove count
			get(arranged, 'length') - index,
			// items to add
			content.slice(offset + limit - diff, offset + limit)
		);
		return offset;
	}.property(),

	limit: function (key, limit, old) {
		// getter
		if (arguments.length <= 1) {
			// default value
			return DEFAULT_LIMIT;
		}
		// do not allow negative limit
		limit = Math.max(limit, 0);
		// setter
		var offset = get(this, 'offset');
		var content = get(this, 'content');
		var arrangedContent = get(this, 'arrangedContent');
		var arrangedLength = get(arrangedContent, 'length');
		var diff = limit - arrangedLength;
		var index = arrangedLength + offset;
		var addedItems = content.slice(index, index + Math.max(diff, 0));
		diff = Math.max(-diff, 0);
		arrangedContent.replace(arrangedLength - diff, diff, addedItems);
		return limit;
	}.property(),

	arrangedContent: function () {
		var content = get(this, 'content');
		var offset = get(this, 'offset');
		var limit = get(this, 'limit');
		var slice = content.slice(offset, offset + limit);
		return slice;
	}.property('content'),

	contentArrayDidChange: function (content, i, removedCount, addedCount) {
		var offset = get(this, 'offset');
		var limit = get(this, 'limit');
		var arranged = get(this, 'arrangedContent');
		var arrangedLength = get(arranged, 'length');
		var addedItems = content.slice(offset, offset + addedCount);
		arranged.replace(
			Math.max(0, arrangedLength - i - addedCount + offset),
			addedCount
		);
		arranged.replace(0, 0, addedItems);
	}
});

ArraySlice.computed = {
	slice: function (prop, offset, limit) {
		return function () {
			return ArraySlice.create({
				content: this.get(prop),
				offset: offset,
				limit: limit
			});
		}.property(prop);
	}
};

export default ArraySlice;
<commit_msg>Separated logic into ArrayLimit and ArrayOffset proxies<commit_after>import Em from 'ember';
import ArrayLimit from 'array-limit';
import ArrayOffset from 'array-offset';

var computed = Em.computed;
var alias = computed.alias;

var DEFAULT_OFFSET = 0;
var DEFAULT_LIMIT = Infinity;

var ArraySlice = Em.ArrayProxy.extend({
	offset: computed({
		get: function () {
			return DEFAULT_OFFSET;
		},
		set: function (key, offset) {
			this.set('_offsetProxy.offset', offset);
			return this.get('_offsetProxy.offset');
		}
	}),

	limit: computed({
		get: function () {
			return DEFAULT_LIMIT;
		},
		set: function (key, limit) {
			this.set('_limitProxy.limit', limit);
			return this.get('_limitProxy.limit');
		}
	}),

	content: null,

	_offsetProxy: computed(function () {
		return ArrayOffset.create({
			content: this.get('content'),
			offset: this.get('offset')
		});
	}),

	_limitProxy: computed(function () {
		return ArrayLimit.create({
			content: this.get('_offsetProxy.arrangedContent'),
			limit: this.get('limit')
		});
	}),

	arrangedContent: alias('_limitProxy')
});

ArraySlice.computed = {
	slice: function (prop, offset, limit) {
		return computed(prop, function () {
			return ArraySlice.create({
				content: this.get(prop),
				offset: offset,
				limit: limit
			});
		});
	}
};

export default ArraySlice;
<|endoftext|>


<commit_before>// Process files with iterator, trying to use cached result.
// Cache will be used in this case:
//
// - mtime not changed or md5 not changes
// - the same for dependencies
//
'use strict';

var fs      = require('fs');
var path    = require('path');
var crypto  = require('crypto');
var assert  = require('assert');

var _       = require('lodash');
var fstools = require('fs-tools');
var fscached   = require('./fs_cached');

// Cache structure:
//
// {
//   <file_path>: {
//     data: ...
//     deps: {
//       <dep_file_path>: {
//         mtime: ...
//         md5: ...
//       }
//     }
//   }
// }
//
// NOTE: Dependencies include processed file itself

// calculate file digest (md5), cached
//
var fileDigest = _.memoize(function (file) {
  var content = '';
  try { content = fs.readFileSync(file); } catch(e) {}
  return crypto.createHash('md5').update(content).digest('hex');
});

/**
 * new CachedProcessor
 * - options (Object):
 *
 * options:
 *
 * - cache - path to cache file
 * - self (true) - add self to dependencies. Set `false` to skip.
 * - compact (true) - set `false` to keep records without hits on save
 * - autosave (500) - delay in ms to autosave cache state. set `false` or 0 to disable.
 **/
function CachedProcessor(options) {
  assert.ok(options, 'You must define cached processor options');

  this.cachePath = options.cache;
  this.compact = options.compact === false ? false : true;

  assert.ok(this.cachePath, 'You must define cache path');

  // Cache
  this.cache = {};
  try {
    this.cache = JSON.parse(fs.readFileSync(this.cachePath, 'utf8'));
  } catch (__) {}

  // Cache hits, used to remove old entries
  this.hits = {};

  this.addSelf = (options.self === false) ? false : true;

  this.autosave = 0;
  if (options.autosave !== false && options.autosave !== 0) {
    this.autosave = options.autosave || 500;
  }

  var self = this;
  this.__save = _.debounce(function () {
    self.save();
  }, this.autosave);
}

CachedProcessor.prototype.process = function (/*path*/) {
  throw new Error('Transformer not defined');
};

// Set dependencies for given file. Cache become invalid
// if dependencies change.
//
CachedProcessor.prototype.addDependencies = function (path, list) {
  assert.ok(this.cache[path], 'Object "' + path + '" not found.');
  if (list.length === 0) { return; }

  var self = this;

  list.forEach(function (file) {
    var stat = fscached.statSync(file);
    var deps = self.cache[path].deps;

    deps[file] = {};
    deps[file].mtime = stat.mtime.getTime();
    deps[file].md5   = crypto
                          .createHash('md5')
                          .update(fs.readFileSync(file))
                          .digest('hex');
  });

  if (this.autosave) { this.__save(); }
};

// Save all cached data to disk
//
CachedProcessor.prototype.save = function () {
  var to_save = {};

  if (this.compact) {
    // remove cache entries without hits
    _.forOwn(this.hits, function (val, key) {
      if (val) { to_save[key] = this.cache[key]; }
    }, this);
  } else {
    to_save = this.cache;
  }
  fstools.mkdirSync(path.dirname(this.cachePath));
  fs.writeFileSync(this.cachePath, JSON.stringify(to_save, null, 2));
};

// Get processed data or build if not exists
//
CachedProcessor.prototype.get = function (file) {
  var cacheValid = false;

  // If cache exists, try to validate
  if (this.cache[file]) {
    cacheValid = true;

    _.forOwn(this.cache[file].deps, function (dep, name) {
      var stat;
      try {
        stat = fscached.statSync(name);
      } catch (__) {
        // file not exists
        cacheValid = false;
        return false; // terminate
      }

      // check mtime first, that's faster
      if (dep.mtime === stat.mtime.getTime()) { return; }
      // check md5
      if (dep.md5 === fileDigest(name)) {
        // update mtime value for next time
        dep.mtime = stat.mtime.getTime();
        if (this.autosave) { this.__save(); }
        return;
      }

      cacheValid = false;
      return false; // terminate
    }, this);
  }

  if (cacheValid) {
    // can be undefined (on load from cache)
    this.hits[file] = (~~this.hits[file]) + 1;
    return this.cache[file].data;
  }

  // Create cache entry
  // Do it before `process` call, to allow add
  // dependencies from iterator
  this.cache[file] = { data: '', deps: {} };
  this.hits[file] = 1;
  if (this.addSelf) {
    this.addDependencies(file, [file]); // add self
  }

  this.cache[file].data = this.process(file);

  if (this.autosave) { this.__save(); }

  return this.cache[file].data;
};

// Make sure that files from list exists in item dependencies.
// Or reset cache if not.
//
CachedProcessor.prototype.checkDepList = function (item, list) {
  if (!this.cache[item]) { return; }

  // compare sorted arrays as strings
  if (Object.keys(this.cache[item].deps).sort().join('::') !== list.sort().join('::')) {
    delete this.cache[item];
  }
};

module.exports = CachedProcessor;<commit_msg>lint fix<commit_after>// Process files with iterator, trying to use cached result.
// Cache will be used in this case:
//
// - mtime not changed or md5 not changes
// - the same for dependencies
//
'use strict';

var fs      = require('fs');
var path    = require('path');
var crypto  = require('crypto');
var assert  = require('assert');

var _       = require('lodash');
var fstools = require('fs-tools');
var fscached   = require('./fs_cached');

// Cache structure:
//
// {
//   <file_path>: {
//     data: ...
//     deps: {
//       <dep_file_path>: {
//         mtime: ...
//         md5: ...
//       }
//     }
//   }
// }
//
// NOTE: Dependencies include processed file itself

// calculate file digest (md5), cached
//
var fileDigest = _.memoize(function (file) {
  var content = '';
  try { content = fs.readFileSync(file); } catch(e) {}
  return crypto.createHash('md5').update(content).digest('hex');
});

/**
 * new CachedProcessor
 * - options (Object):
 *
 * options:
 *
 * - cache - path to cache file
 * - self (true) - add self to dependencies. Set `false` to skip.
 * - compact (true) - set `false` to keep records without hits on save
 * - autosave (500) - delay in ms to autosave cache state. set `false` or 0 to disable.
 **/
function CachedProcessor(options) {
  assert.ok(options, 'You must define cached processor options');

  this.cachePath = options.cache;
  this.compact = options.compact === false ? false : true;

  assert.ok(this.cachePath, 'You must define cache path');

  // Cache
  this.cache = {};
  try {
    this.cache = JSON.parse(fs.readFileSync(this.cachePath, 'utf8'));
  } catch (__) {}

  // Cache hits, used to remove old entries
  this.hits = {};

  this.addSelf = (options.self === false) ? false : true;

  this.autosave = 0;
  if (options.autosave !== false && options.autosave !== 0) {
    this.autosave = options.autosave || 500;
  }

  var self = this;
  this.__save = _.debounce(function () {
    self.save();
  }, this.autosave);
}

CachedProcessor.prototype.process = function (/*path*/) {
  throw new Error('Transformer not defined');
};

// Set dependencies for given file. Cache become invalid
// if dependencies change.
//
CachedProcessor.prototype.addDependencies = function (path, list) {
  assert.ok(this.cache[path], 'Object "' + path + '" not found.');
  if (list.length === 0) { return; }

  var self = this;

  list.forEach(function (file) {
    var stat = fscached.statSync(file);
    var deps = self.cache[path].deps;

    deps[file] = {};
    deps[file].mtime = stat.mtime.getTime();
    deps[file].md5   = crypto
                          .createHash('md5')
                          .update(fs.readFileSync(file))
                          .digest('hex');
  });

  if (this.autosave) { this.__save(); }
};

// Save all cached data to disk
//
CachedProcessor.prototype.save = function () {
  var to_save = {};

  if (this.compact) {
    // remove cache entries without hits
    _.forOwn(this.hits, function (val, key) {
      if (val) { to_save[key] = this.cache[key]; }
    }, this);
  } else {
    to_save = this.cache;
  }
  fstools.mkdirSync(path.dirname(this.cachePath));
  fs.writeFileSync(this.cachePath, JSON.stringify(to_save, null, 2));
};

// Get processed data or build if not exists
//
CachedProcessor.prototype.get = function (file) {
  var cacheValid = false;

  // If cache exists, try to validate
  if (this.cache[file]) {
    cacheValid = true;

    _.forOwn(this.cache[file].deps, function (dep, name) {
      var stat;
      try {
        stat = fscached.statSync(name);
      } catch (__) {
        // file not exists
        cacheValid = false;
        return false; // terminate
      }

      // check mtime first, that's faster
      if (dep.mtime === stat.mtime.getTime()) { return; }
      // check md5
      if (dep.md5 === fileDigest(name)) {
        // update mtime value for next time
        dep.mtime = stat.mtime.getTime();
        if (this.autosave) { this.__save(); }
        return;
      }

      cacheValid = false;
      return false; // terminate
    }, this);
  }

  if (cacheValid) {
    // can be undefined (on load from cache)
    this.hits[file] = (this.hits[file] || 0) + 1;
    return this.cache[file].data;
  }

  // Create cache entry
  // Do it before `process` call, to allow add
  // dependencies from iterator
  this.cache[file] = { data: '', deps: {} };
  this.hits[file] = 1;
  if (this.addSelf) {
    this.addDependencies(file, [file]); // add self
  }

  this.cache[file].data = this.process(file);

  if (this.autosave) { this.__save(); }

  return this.cache[file].data;
};

// Make sure that files from list exists in item dependencies.
// Or reset cache if not.
//
CachedProcessor.prototype.checkDepList = function (item, list) {
  if (!this.cache[item]) { return; }

  // compare sorted arrays as strings
  if (Object.keys(this.cache[item].deps).sort().join('::') !== list.sort().join('::')) {
    delete this.cache[item];
  }
};

module.exports = CachedProcessor;<|endoftext|>


<commit_before>'use strict';

var cleanBaseURL = require('../../../../utilities/clean-base-url');
var debug        = require('debug')('ember-cli:serve-files');

function ServeFilesAddon(project) {
  this.project = project;
  this.name = 'serve-files-middleware';
}

ServeFilesAddon.prototype.serverMiddleware = function(options) {
  var app = options.app;
  options = options.options;
  debug('serverMiddleware: %s',options.outputPath);

  var broccoliMiddleware = options.middleware || require('broccoli/lib/middleware');
  var middleware = broccoliMiddleware(options.watcher);

  var baseURL = cleanBaseURL(options.baseURL);

  debug('serverMiddleware: output: %s baseURL: %s', options.outputPath, baseURL);

  app.use(function(req, res, next) {
    var oldURL = req.url;
    var url = req.serveUrl || req.url;
    debug('serving: %s', url);

    var actualPrefix   = req.url.slice(0, baseURL.length - 1); // Don't care
    var expectedPrefix = baseURL.slice(0, baseURL.length - 1); // about last slash

    if (actualPrefix === expectedPrefix) {
      req.url = url.slice(actualPrefix.length); // Remove baseURL prefix
      debug('serving: (prefix stripped) %s', req.url);

      // Serve file, if no file has been found, reset url for proxy stuff
      // that comes afterwards
      middleware(req, res, function(err) {
        req.url = oldURL;
        if (err) {
          debug('err', err);
        }
        next(err);
      });
    } else {
      debug('prefixes didn\'t match, passing control on: (actual:%s expected:%s)', actualPrefix, expectedPrefix);
      next();
    }
  });
};

module.exports = ServeFilesAddon;
<commit_msg>configure broccoliMiddleware to hook into our liveReload (dirty hack for now) <commit_after>'use strict';

var cleanBaseURL = require('../../../../utilities/clean-base-url');
var debug        = require('debug')('ember-cli:serve-files');

function ServeFilesAddon(project) {
  this.project = project;
  this.name = 'serve-files-middleware';
}

ServeFilesAddon.prototype.serverMiddleware = function(options) {
  var app = options.app;
  options = options.options;
  debug('serverMiddleware: %s',options.outputPath);

  var broccoliMiddleware = options.middleware || require('broccoli/lib/middleware');
  var middleware = broccoliMiddleware(options.watcher, {
    liveReloadPath: '/ember-cli-live-reload.js'
  });

  var baseURL = cleanBaseURL(options.baseURL);

  debug('serverMiddleware: output: %s baseURL: %s', options.outputPath, baseURL);

  app.use(function(req, res, next) {
    var oldURL = req.url;
    var url = req.serveUrl || req.url;
    debug('serving: %s', url);

    var actualPrefix   = req.url.slice(0, baseURL.length - 1); // Don't care
    var expectedPrefix = baseURL.slice(0, baseURL.length - 1); // about last slash

    if (actualPrefix === expectedPrefix) {
      req.url = url.slice(actualPrefix.length); // Remove baseURL prefix
      debug('serving: (prefix stripped) %s', req.url);

      // Serve file, if no file has been found, reset url for proxy stuff
      // that comes afterwards
      middleware(req, res, function(err) {
        req.url = oldURL;
        if (err) {
          debug('err', err);
        }
        next(err);
      });
    } else {
      debug('prefixes didn\'t match, passing control on: (actual:%s expected:%s)', actualPrefix, expectedPrefix);
      next();
    }
  });
};

module.exports = ServeFilesAddon;
<|endoftext|>


<commit_before>import { document, localStorage, window } from 'global';
import PropTypes from 'prop-types';
import React from 'react';
import SplitPane from 'react-split-pane';

import USplit from './usplit';
import Dimensions from './dimensions';

const rootStyle = {
  height: '100vh',
  backgroundColor: '#F7F7F7',
};

const leftPanelStyle = leftPanelOnTop => ({
  width: '100%',
  display: 'flex',
  flexDirection: leftPanelOnTop ? 'column' : 'row',
  alignItems: 'stretch',
  paddingRight: leftPanelOnTop ? 10 : 0,
});

const downPanelStyle = downPanelInRight => ({
  display: 'flex',
  flexDirection: downPanelInRight ? 'row' : 'column',
  alignItems: 'stretch',
  position: 'absolute',
  width: '100%',
  height: '100%',
  padding: downPanelInRight ? '5px 10px 10px 0' : '0px 10px 10px 0',
  boxSizing: 'border-box',
});

const resizerCursor = isVert => (isVert ? 'col-resize' : 'row-resize');

const storiesResizerStyle = (showLeftPanel, leftPanelOnTop) => ({
  cursor: showLeftPanel ? resizerCursor(!leftPanelOnTop) : undefined,
  height: leftPanelOnTop ? 10 : 'auto',
  width: leftPanelOnTop ? '100%' : 10,
  zIndex: 1,
});

const addonResizerStyle = (showDownPanel, downPanelInRight) => ({
  cursor: showDownPanel ? resizerCursor(downPanelInRight) : undefined,
  height: downPanelInRight ? '100%' : 10,
  width: downPanelInRight ? 10 : '100%',
  zIndex: 1,
});

const contentPanelStyle = (downPanelInRight, leftPanelOnTop) => ({
  position: 'absolute',
  boxSizing: 'border-box',
  width: '100%',
  height: '100%',
  padding: downPanelInRight ? '10px 2px 10px 0' : '10px 10px 2px 0',
  paddingTop: leftPanelOnTop ? 0 : 10,
});

const normalPreviewStyle = {
  width: '100%',
  height: '100%',
  backgroundColor: '#FFF',
  border: '1px solid #ECECEC',
  borderRadius: 4,
};

const fullScreenPreviewStyle = {
  position: 'fixed',
  left: '0px',
  right: '0px',
  top: '0px',
  zIndex: 1,
  backgroundColor: '#FFF',
  height: '100%',
  width: '100%',
  border: 0,
  margin: 0,
  padding: 0,
  overflow: 'hidden',
};

const defaultSizes = {
  addonPanel: {
    down: 200,
    right: 400,
  },
  storiesPanel: {
    left: 250,
    top: 400,
  },
};

const onDragStart = () => document.body.classList.add('dragging');

const onDragEnd = () => document.body.classList.remove('dragging');

const saveSizes = sizes => {
  try {
    localStorage.setItem('panelSizes', JSON.stringify(sizes));
    return true;
  } catch (e) {
    return false;
  }
};

const getSavedSizes = sizes => {
  try {
    const panelSizes = localStorage.getItem('panelSizes');
    if (panelSizes) {
      return JSON.parse(panelSizes);
    }
    saveSizes(sizes);
    return sizes;
  } catch (e) {
    saveSizes(sizes);
    return sizes;
  }
};

const conditionalRender = (condition, positive, negative) => (condition ? positive() : negative());

class Layout extends React.Component {
  constructor(props) {
    super(props);

    this.layerSizes = getSavedSizes(defaultSizes);

    this.state = {
      previewPanelDimensions: {
        height: 0,
        width: 0,
      },
    };

    this.onResize = this.onResize.bind(this);
  }

  componentDidMount() {
    window.addEventListener('resize', this.onResize);
  }

  componentWillUnmount() {
    window.removeEventListener('resize', this.onResize);
  }

  onResize(pane, mode) {
    return size => {
      this.layerSizes[pane][mode] = size;
      saveSizes(this.layerSizes);

      const { clientWidth, clientHeight } = this.previewPanelRef;

      this.setState({
        previewPanelDimensions: {
          width: clientWidth,
          height: clientHeight,
        },
      });
    };
  }

  render() {
    const {
      goFullScreen,
      showLeftPanel,
      showDownPanel,
      downPanelInRight,
      downPanel,
      leftPanel,
      preview,
    } = this.props;
    const { previewPanelDimensions } = this.state;

    const leftPanelOnTop = false;

    let previewStyle = normalPreviewStyle;

    if (goFullScreen) {
      previewStyle = fullScreenPreviewStyle;
    }

    const sizes = getSavedSizes(this.layerSizes);

    const leftPanelDefaultSize = !leftPanelOnTop ? sizes.storiesPanel.left : sizes.storiesPanel.top;
    const downPanelDefaultSize = !downPanelInRight ? sizes.addonPanel.down : sizes.addonPanel.right;

    const addonSplit = downPanelInRight ? 'vertical' : 'horizontal';
    const storiesSplit = leftPanelOnTop ? 'horizontal' : 'vertical';

    return (

        <SplitPane
          split={storiesSplit}
          allowResize={showLeftPanel}
          minSize={150}
          maxSize={-400}
          size={showLeftPanel ? leftPanelDefaultSize : 1}
          defaultSize={leftPanelDefaultSize}
          resizerStyle={storiesResizerStyle(showLeftPanel, leftPanelOnTop)}
          onDragStarted={onDragStart}
          onDragFinished={onDragEnd}
          onChange={this.onResize('storiesPanel', leftPanelOnTop ? 'top' : 'left')}
        >
          {conditionalRender(
            showLeftPanel,
            () => (

{leftPanel()}

                <USplit shift={5} split={storiesSplit} />

            ),
            () =>
          )}

          <SplitPane
            split={addonSplit}
            allowResize={showDownPanel}
            primary="second"
            minSize={downPanelInRight ? 200 : 100}
            maxSize={-200}
            size={showDownPanel ? downPanelDefaultSize : 1}
            defaultSize={downPanelDefaultSize}
            resizerStyle={addonResizerStyle(showDownPanel, downPanelInRight)}
            onDragStarted={onDragStart}
            onDragFinished={onDragEnd}
            onChange={this.onResize('addonPanel', downPanelInRight ? 'right' : 'down')}
          >

 {
                  this.previewPanelRef = ref;
                }}
              >
                {preview()}

              <Dimensions {...previewPanelDimensions} />

            {conditionalRender(
              showDownPanel,
              () => (

                  <USplit shift={-5} split={addonSplit} />
                  {downPanel()}

              ),
              () =>
            )}
          </SplitPane>
        </SplitPane>

    );
  }
}

Layout.propTypes = {
  showLeftPanel: PropTypes.bool.isRequired,
  showDownPanel: PropTypes.bool.isRequired,
  goFullScreen: PropTypes.bool.isRequired,
  leftPanel: PropTypes.func.isRequired,
  preview: PropTypes.func.isRequired,
  downPanel: PropTypes.func.isRequired,
  downPanelInRight: PropTypes.bool.isRequired,
};

export default Layout;
<commit_msg>Handle full screen scrolling on iOS<commit_after>import { document, localStorage, window } from 'global';
import PropTypes from 'prop-types';
import React from 'react';
import SplitPane from 'react-split-pane';

import USplit from './usplit';
import Dimensions from './dimensions';

const rootStyle = {
  height: '100vh',
  backgroundColor: '#F7F7F7',
};

const leftPanelStyle = leftPanelOnTop => ({
  width: '100%',
  display: 'flex',
  flexDirection: leftPanelOnTop ? 'column' : 'row',
  alignItems: 'stretch',
  paddingRight: leftPanelOnTop ? 10 : 0,
});

const downPanelStyle = downPanelInRight => ({
  display: 'flex',
  flexDirection: downPanelInRight ? 'row' : 'column',
  alignItems: 'stretch',
  position: 'absolute',
  width: '100%',
  height: '100%',
  padding: downPanelInRight ? '5px 10px 10px 0' : '0px 10px 10px 0',
  boxSizing: 'border-box',
});

const resizerCursor = isVert => (isVert ? 'col-resize' : 'row-resize');

const storiesResizerStyle = (showLeftPanel, leftPanelOnTop) => ({
  cursor: showLeftPanel ? resizerCursor(!leftPanelOnTop) : undefined,
  height: leftPanelOnTop ? 10 : 'auto',
  width: leftPanelOnTop ? '100%' : 10,
  zIndex: 1,
});

const addonResizerStyle = (showDownPanel, downPanelInRight) => ({
  cursor: showDownPanel ? resizerCursor(downPanelInRight) : undefined,
  height: downPanelInRight ? '100%' : 10,
  width: downPanelInRight ? 10 : '100%',
  zIndex: 1,
});

const contentPanelStyle = (downPanelInRight, leftPanelOnTop) => ({
  position: 'absolute',
  boxSizing: 'border-box',
  width: '100%',
  height: '100%',
  padding: downPanelInRight ? '10px 2px 10px 0' : '10px 10px 2px 0',
  paddingTop: leftPanelOnTop ? 0 : 10,
});

const normalPreviewStyle = {
  width: '100%',
  height: '100%',
  backgroundColor: '#FFF',
  border: '1px solid #ECECEC',
  borderRadius: 4,
};

const fullScreenPreviewStyle = {
  position: 'fixed',
  left: '0px',
  right: '0px',
  top: '0px',
  zIndex: 1,
  backgroundColor: '#FFF',
  height: '100%',
  width: '100%',
  border: 0,
  margin: 0,
  padding: 0,
  overflow: 'auto',
  webkitOverflowScrolling: 'touch',
};

const defaultSizes = {
  addonPanel: {
    down: 200,
    right: 400,
  },
  storiesPanel: {
    left: 250,
    top: 400,
  },
};

const onDragStart = () => document.body.classList.add('dragging');

const onDragEnd = () => document.body.classList.remove('dragging');

const saveSizes = sizes => {
  try {
    localStorage.setItem('panelSizes', JSON.stringify(sizes));
    return true;
  } catch (e) {
    return false;
  }
};

const getSavedSizes = sizes => {
  try {
    const panelSizes = localStorage.getItem('panelSizes');
    if (panelSizes) {
      return JSON.parse(panelSizes);
    }
    saveSizes(sizes);
    return sizes;
  } catch (e) {
    saveSizes(sizes);
    return sizes;
  }
};

const conditionalRender = (condition, positive, negative) => (condition ? positive() : negative());

class Layout extends React.Component {
  constructor(props) {
    super(props);

    this.layerSizes = getSavedSizes(defaultSizes);

    this.state = {
      previewPanelDimensions: {
        height: 0,
        width: 0,
      },
    };

    this.onResize = this.onResize.bind(this);
  }

  componentDidMount() {
    window.addEventListener('resize', this.onResize);
  }

  componentWillUnmount() {
    window.removeEventListener('resize', this.onResize);
  }

  onResize(pane, mode) {
    return size => {
      this.layerSizes[pane][mode] = size;
      saveSizes(this.layerSizes);

      const { clientWidth, clientHeight } = this.previewPanelRef;

      this.setState({
        previewPanelDimensions: {
          width: clientWidth,
          height: clientHeight,
        },
      });
    };
  }

  render() {
    const {
      goFullScreen,
      showLeftPanel,
      showDownPanel,
      downPanelInRight,
      downPanel,
      leftPanel,
      preview,
    } = this.props;
    const { previewPanelDimensions } = this.state;

    const leftPanelOnTop = false;

    let previewStyle = normalPreviewStyle;

    if (goFullScreen) {
      previewStyle = fullScreenPreviewStyle;
    }

    const sizes = getSavedSizes(this.layerSizes);

    const leftPanelDefaultSize = !leftPanelOnTop ? sizes.storiesPanel.left : sizes.storiesPanel.top;
    const downPanelDefaultSize = !downPanelInRight ? sizes.addonPanel.down : sizes.addonPanel.right;

    const addonSplit = downPanelInRight ? 'vertical' : 'horizontal';
    const storiesSplit = leftPanelOnTop ? 'horizontal' : 'vertical';

    return (

        <SplitPane
          split={storiesSplit}
          allowResize={showLeftPanel}
          minSize={150}
          maxSize={-400}
          size={showLeftPanel ? leftPanelDefaultSize : 1}
          defaultSize={leftPanelDefaultSize}
          resizerStyle={storiesResizerStyle(showLeftPanel, leftPanelOnTop)}
          onDragStarted={onDragStart}
          onDragFinished={onDragEnd}
          onChange={this.onResize('storiesPanel', leftPanelOnTop ? 'top' : 'left')}
        >
          {conditionalRender(
            showLeftPanel,
            () => (

{leftPanel()}

                <USplit shift={5} split={storiesSplit} />

            ),
            () =>
          )}

          <SplitPane
            split={addonSplit}
            allowResize={showDownPanel}
            primary="second"
            minSize={downPanelInRight ? 200 : 100}
            maxSize={-200}
            size={showDownPanel ? downPanelDefaultSize : 1}
            defaultSize={downPanelDefaultSize}
            resizerStyle={addonResizerStyle(showDownPanel, downPanelInRight)}
            onDragStarted={onDragStart}
            onDragFinished={onDragEnd}
            onChange={this.onResize('addonPanel', downPanelInRight ? 'right' : 'down')}
          >

 {
                  this.previewPanelRef = ref;
                }}
              >
                {preview()}

              <Dimensions {...previewPanelDimensions} />

            {conditionalRender(
              showDownPanel,
              () => (

                  <USplit shift={-5} split={addonSplit} />
                  {downPanel()}

              ),
              () =>
            )}
          </SplitPane>
        </SplitPane>

    );
  }
}

Layout.propTypes = {
  showLeftPanel: PropTypes.bool.isRequired,
  showDownPanel: PropTypes.bool.isRequired,
  goFullScreen: PropTypes.bool.isRequired,
  leftPanel: PropTypes.func.isRequired,
  preview: PropTypes.func.isRequired,
  downPanel: PropTypes.func.isRequired,
  downPanelInRight: PropTypes.bool.isRequired,
};

export default Layout;
<|endoftext|>


<commit_before>"use strict";
var fs = require("fs");
var watch = require("../process/watch");
var path = require("path");
var versionTree = {};
var loading_queue = [], loading_count = 0;
var runPromiseInQueue = function () {
    if (loading_count > 2) return;
    if (loading_queue.length) {
        loading_count++;
        loading_queue.shift()();
    }
};
var delPromiseInQueue = function (a) {
    loading_count--;
    runPromiseInQueue();
    return a;
};
var getPromiseInQueue = function (runner) {
    var promise = new Promise(function (ok) {
        loading_queue.push(ok);
    }).then(function () {
        return new Promise(runner);
    }).then(delPromiseInQueue, delPromiseInQueue);
    runPromiseInQueue();
    return promise;
};
/**
 * 取文件夹
 * @param {string} pathname
 */
var getdir = function (pathname) {
    var directory = {};
    fs.readdirSync(pathname).forEach(function (name) {
        directory[name] = false;
    });
    return directory;
};
var getdirAsync = function (pathname) {
    return getPromiseInQueue(function (ok, oh) {

        fs.readdir(pathname, function (error, files) {
            if (error) oh(error);
            else {
                var directory = {}
                files.forEach(function (name) {
                    directory[name] = false;
                });
                ok(directory);
            }
        });
    });
};
var getfileAsync = function (pathname, buffer_size) {
    if (isFinite(buffer_size) && getVersion(pathname).size > buffer_size)
        return getFileHeadAsync(pathname, buffer_size);
    return getPromiseInQueue(function (ok, oh) {
        fs.readFile(pathname, function (error, data) {
            if (error) oh(error);
            else ok(data);
        });
    });
};
var getFileHeadAsync = function (pathname, buffer_size) {
    return getPromiseInQueue(function (ok, oh) {
        fs.open(pathname, "r", function (err, h) {
            if (err) {
                return oh(err);
            }
            var chunk = Buffer.alloc(buffer_size);
            fs.read(h, chunk, 0, chunk.length, 0, function (err, length, chunk) {
                if (err) return fs.close(h), oh(err);
                if (length < chunk.length) {
                    chunk = chunk.slice(0, length);
                }
                chunk.location = pathname;
                fs.close(h, function (error) {
                    if (error) return oh(error);
                    ok(chunk);
                });
            });
        });
    });
};

var isdirAsync = function (pathname) {
    return getPromiseInQueue(function (ok, oh) {
        fs.stat(pathname, function (error, stats) {
            if (error) oh(error);
            else if (stats.isDirectory()) ok(true);
            else {
                versionTree[pathname] = stats;
                ok(false);
            }
        });
    });
};

var getVersion = function (pathname) {
    return versionTree[pathname];
};

var asyncLoader = function (curl, temp, key, rebuild) {
    var root = String(this);
    var buffer_size = this.buffer_size;
    var durl = path.resolve(root, curl);
    var durls = [durl];
    var _watch = function (_durls) {
        durls.forEach(durl => watch(durl));
        durls = _durls;
        _durls.forEach(curl => watch(curl, load));
    };
    var pathname = path.join(root, curl);
    var that = this;
    var load = function (loadType) {
        var durls = [durl];
        var is_change = loadType === "change";
        var saved = is_change && temp[key];
        saved = !(saved instanceof Promise || saved instanceof Error) && saved;

        temp[key] = isdirAsync(pathname).then(function (isdir) {
            if (temp[key]) {
                unwatch(pathname, temp[key]);
            }
            if (isdir) {
                temp[key] = getdirAsync(pathname).then(function (dirs) {
                    temp[key] = dirs;
                    if (_reload_handlers.length) {
                        is_change = saved && Object.keys(dirs).sort().join(",") !== Object.keys(saved).sort().join(",");
                        is_change && _reload_handlers.forEach(run => run());
                    }
                    _watch(durls);
                    console.info(root, curl, is_change ? "change" : "load");
                }).catch(function (error) {
                    temp[key] = error;
                    _watch([]);
                });
            } else {
                temp[key] = getfileAsync(pathname, buffer_size).then(function (data) {
                    try {
                        if (rebuild instanceof Function) {
                            data = rebuild(data, that.getURL(), durl, durls);
                        }
                    } catch (e) {
                        console.warn("Build Faild:", curl, e);
                        data = e;
                    };
                    return data;
                }).then(function (data) {
                    if (typeof data === "string") {
                        data = Buffer.from(data);
                    }
                    temp[key] = data;
                    if (_reload_handlers.length) {
                        is_change = saved && (saved instanceof Buffer && Buffer.compare(saved, data) || String(saved) !== String(data));
                        is_change && _reload_handlers.forEach(run => run());
                    }
                    console.info(root, curl, is_change ? "change" : "load");
                    _watch(durls);
                }).catch(function (error) {
                    temp[key] = error;
                    console.error(error);
                    _watch([]);
                });
            }
        }).catch(function (error) {
            temp[key] = error;
            console.error(error);
        });
    };
    load();
    return load;
};

var seekAsync = function (url, tree, rebuild) {
    var temp = tree;
    if (!temp) return new Error('file system error');
    var keeys = url.split(/[\\\/]+/);
    var curl = "";
    var that = this;
    var temps = [];

    var research = function () {
        return seekAsync.call(that, url, tree, rebuild);
    };
    search: for (var cx = 0, dx = keeys.length; cx < dx; cx++) {
        var key = keeys[cx];
        if (key === '' || key === '.') continue;
        if (!(key in temp)) {
            if (!that.buffer_size) {
                temp = undefined;
                break;
            }
            for (var cy = temps.length - 1; cy >= 0; cy--) {
                if (key in temps[cy]) {
                    let searched = seekAsync.call(that, keeys.slice(cx).join("/"), temps[cy], rebuild);
                    if (searched instanceof Promise) {
                        return searched.then(research);
                    }
                    if (searched !== undefined) {
                        temp = searched;
                        break search;
                    }
                }
            }
            continue;
        }
        curl = path.join(curl, key);
        if (temp[key] === false) {
            asyncLoader.call(that, curl, temp, key, rebuild);
        }
        temps.push(temp);
        temp = temp[key];
        if (temp instanceof Promise) {
            return temp.then(research);
        }
        if (!temp) break;
    }
    if (!(temp instanceof Object)) {
        return temp;
    }
    if (temp instanceof Function) {
        return temp;
    }
    if (temp instanceof Error) {
        return temp;
    }
    if ((temp instanceof Buffer) && !temp.stat) {
        temp.stat = getVersion(path.join(String(this), curl));
    }
    return temp;
};

/**
 * 取消路径监听
 * @param {string} curl
 * @param {object} temp
 */
var unwatch = function (curl, temp) {
    if (temp instanceof Object) {
        for (var k in temp) {
            var cvrl = path.join(curl, k);
            watch(cvrl);
            unwatch(cvrl, temp[k]);
        }
    }
};
var getExtts = function (extts) {
    if (extts instanceof Array && extts.length <= 1) extts = extts;
    return extts || "";
};
/**
 * 根椐filesroot路径设置文件缓冲
 * @param {string} filesroot
 */
var cache = function (filesroot, rebuild, buffer_size_limit) {
    var sk = function (url, extts) {
    };
    filesroot = filesroot.split(",");
    var tree = filesroot.map(froot => fs.existsSync(froot) && fs.statSync(froot).isDirectory() && getdir(froot) || Object.create(null));
    if (isFinite(buffer_size_limit) && buffer_size_limit >= 0) {
        sk.buffer_size = buffer_size_limit | 0;
    }
    var seekerAsync = function (url, extts = "") {
        extts = getExtts(extts);
        if (!Array.isArray(extts)) {
            extts = [extts];
        }
        var seeker = new Function();
        seeker.toString = function () {
            return this;
        }.bind(filesroot[0]);
        seeker.getURL = function () {
            return url;
        };
        seeker.buffer_size = sk.buffer_size;
        return new Promise(function (ok, oh) {
            var cy = 0;
            var cx = 0;
            var result, matchParent;
            var run = function () {
                if (cx >= extts.length) {
                    cy++;
                    if (cy >= tree.length) {
                        if (result === undefined) {
                            if (matchParent) {
                                url = url.replace(/\\/g, '/');
                                if (!/\/$/.test(url)) {
                                    url += '/';
                                    result = url;
                                }
                            }
                        }
                        return ok(result);
                    }
                    seeker.toString = function () {
                        return this;
                    }.bind(filesroot[cy]);
                    cx = 0;
                }
                var tree1 = tree[cy];
                var extt = extts[cx];
                var error;
                var promise = seekAsync.call(seeker, url + extt, tree1, rebuild);
                if (promise instanceof Promise) {
                    promise.catch(function (e) {
                        error = e;
                    }).then(function (result) {
                        if (result instanceof Buffer) return ok(result);
                        if (error) oh(error);
                        else run();
                    });
                } else {

                    if (promise instanceof Buffer || promise instanceof Function) return ok(promise);
                    if (promise instanceof Object && !result) {
                        var package_file = "package.json";
                        if (package_file in promise) {
                            if (promise[package_file] === false) {
                                seekAsync.call(seeker, url + extt + "/" + package_file, tree1, a => JSON.parse(String(a))).catch(oh).then(run);
                                return;
                            } else if (promise[package_file] instanceof Buffer) {
                                promise[package_file] = JSON.parse(promise[package_file].toString());
                            }
                            var package_data = promise[package_file];
                            if (package_data instanceof Object && package_data.main) {
                                var roots = package_data.main.split(/[\/\\]+/).filter(a => a && a !== '.' && !/\:$/.test(a));
                                if (roots[0] in promise) {
                                    result = path.join(url, roots.join('/')).replace(/\\/g, '/');
                                    return ok(result);
                                }
                            }
                        } else {
                            var seek_index = 'index';
                            var index = extts.findIndex(a => seek_index + a in promise);
                            if (~index) {
                                result = path.join(url, seek_index + extts[index]).replace(/\\/g, '/');
                            } else {
                                if (typeof promise === 'string') result = promise;
                            }
                        }
                        matchParent = true;
                    }
                    cx++;
                    run();
                }

            };
            run();
        });
    };
    sk.async = seekerAsync;
    sk.new = cache;
    return sk;
}
var _reload_handlers = [];
cache.onreload = function (handler) {
    if (handler instanceof Function) _reload_handlers.push(handler);
};
module.exports = cache;<commit_msg>支持转换大写字母<commit_after>"use strict";
var fs = require("fs");
var watch = require("../process/watch");
var path = require("path");
var versionTree = {};
var loading_queue = [], loading_count = 0;
var runPromiseInQueue = function () {
    if (loading_count > 2) return;
    if (loading_queue.length) {
        loading_count++;
        loading_queue.shift()();
    }
};
var delPromiseInQueue = function (a) {
    loading_count--;
    runPromiseInQueue();
    return a;
};
var getPromiseInQueue = function (runner) {
    var promise = new Promise(function (ok) {
        loading_queue.push(ok);
    }).then(function () {
        return new Promise(runner);
    }).then(delPromiseInQueue, delPromiseInQueue);
    runPromiseInQueue();
    return promise;
};
/**
 * 取文件夹
 * @param {string} pathname
 */
var getdir = function (pathname) {
    var directory = {};
    fs.readdirSync(pathname).forEach(function (name) {
        directory[name] = false;
    });
    return directory;
};
var getdirAsync = function (pathname) {
    return getPromiseInQueue(function (ok, oh) {

        fs.readdir(pathname, function (error, files) {
            if (error) oh(error);
            else {
                var directory = {}
                files.forEach(function (name) {
                    directory[name] = false;
                });
                ok(directory);
            }
        });
    });
};
var getfileAsync = function (pathname, buffer_size) {
    if (isFinite(buffer_size) && getVersion(pathname).size > buffer_size)
        return getFileHeadAsync(pathname, buffer_size);
    return getPromiseInQueue(function (ok, oh) {
        fs.readFile(pathname, function (error, data) {
            if (error) oh(error);
            else ok(data);
        });
    });
};
var getFileHeadAsync = function (pathname, buffer_size) {
    return getPromiseInQueue(function (ok, oh) {
        fs.open(pathname, "r", function (err, h) {
            if (err) {
                return oh(err);
            }
            var chunk = Buffer.alloc(buffer_size);
            fs.read(h, chunk, 0, chunk.length, 0, function (err, length, chunk) {
                if (err) return fs.close(h), oh(err);
                if (length < chunk.length) {
                    chunk = chunk.slice(0, length);
                }
                chunk.location = pathname;
                fs.close(h, function (error) {
                    if (error) return oh(error);
                    ok(chunk);
                });
            });
        });
    });
};

var isdirAsync = function (pathname) {
    return getPromiseInQueue(function (ok, oh) {
        fs.stat(pathname, function (error, stats) {
            if (error) oh(error);
            else if (stats.isDirectory()) ok(true);
            else {
                versionTree[pathname] = stats;
                ok(false);
            }
        });
    });
};

var getVersion = function (pathname) {
    return versionTree[pathname];
};

var asyncLoader = function (curl, temp, key, rebuild) {
    var root = String(this);
    var buffer_size = this.buffer_size;
    var durl = path.resolve(root, curl);
    var durls = [durl];
    var _watch = function (_durls) {
        durls.forEach(durl => watch(durl));
        durls = _durls;
        _durls.forEach(curl => watch(curl, load));
    };
    var pathname = path.join(root, curl);
    var that = this;
    var load = function (loadType) {
        var durls = [durl];
        var is_change = loadType === "change";
        var saved = is_change && temp[key];
        saved = !(saved instanceof Promise || saved instanceof Error) && saved;

        temp[key] = isdirAsync(pathname).then(function (isdir) {
            if (temp[key]) {
                unwatch(pathname, temp[key]);
            }
            if (isdir) {
                temp[key] = getdirAsync(pathname).then(function (dirs) {
                    temp[key] = dirs;
                    if (_reload_handlers.length) {
                        is_change = saved && Object.keys(dirs).sort().join(",") !== Object.keys(saved).sort().join(",");
                        is_change && _reload_handlers.forEach(run => run());
                    }
                    _watch(durls);
                    console.info(root, curl, is_change ? "change" : "load");
                }).catch(function (error) {
                    temp[key] = error;
                    _watch([]);
                });
            } else {
                temp[key] = getfileAsync(pathname, buffer_size).then(function (data) {
                    try {
                        if (rebuild instanceof Function) {
                            data = rebuild(data, that.getURL(), durl, durls);
                        }
                    } catch (e) {
                        console.warn("Build Faild:", curl, e);
                        data = e;
                    };
                    return data;
                }).then(function (data) {
                    if (typeof data === "string") {
                        data = Buffer.from(data);
                    }
                    temp[key] = data;
                    if (_reload_handlers.length) {
                        is_change = saved && (saved instanceof Buffer && Buffer.compare(saved, data) || String(saved) !== String(data));
                        is_change && _reload_handlers.forEach(run => run());
                    }
                    console.info(root, curl, is_change ? "change" : "load");
                    _watch(durls);
                }).catch(function (error) {
                    temp[key] = error;
                    console.error(error);
                    _watch([]);
                });
            }
        }).catch(function (error) {
            temp[key] = error;
            console.error(error);
        });
    };
    load();
    return load;
};
var matcherReg = /[A-Z]+/g;
var replaceCase = function (match, index, input) {
    if (match.length === 1) {
        return "-" + match.toLowerCase();
    }
    if (match.length === 2) {
        match = match.toLowerCase();
        return "-" + match[0] + "-" + match[1];
    }
    if (index + match.length === input.length) {
        return "-" + match;
    }
    return '-' + match.slice(0, match.length - 1) + "-" + match[match.length - 1].toLowerCase();
};

var seekAsync = function (url, tree, rebuild) {
    var temp = tree;
    if (!temp) return new Error('file system error');
    var keeys = url.split(/[\\\/]+/);
    var curl = "";
    var that = this;
    var temps = [];

    var research = function () {
        return seekAsync.call(that, url, tree, rebuild);
    };
    search: for (var cx = 0, dx = keeys.length; cx < dx; cx++) {
        var key = keeys[cx];
        if (key === '' || key === '.') continue;
        if (!(key in temp)) {
            let k = key.replace(matcherReg, replaceCase);
            if (k in temp) {
                key = k;
            }
        }
        if (!(key in temp)) {
            if (!that.buffer_size) {
                temp = undefined;
                break;
            }
            for (var cy = temps.length - 1; cy >= 0; cy--) {
                if (key in temps[cy]) {
                    let searched = seekAsync.call(that, keeys.slice(cx).join("/"), temps[cy], rebuild);
                    if (searched instanceof Promise) {
                        return searched.then(research);
                    }
                    if (searched !== undefined) {
                        temp = searched;
                        break search;
                    }
                }
            }
            continue;
        }
        curl = path.join(curl, key);
        if (temp[key] === false) {
            asyncLoader.call(that, curl, temp, key, rebuild);
        }
        temps.push(temp);
        temp = temp[key];
        if (temp instanceof Promise) {
            return temp.then(research);
        }
        if (!temp) break;
    }
    if (!(temp instanceof Object)) {
        return temp;
    }
    if (temp instanceof Function) {
        return temp;
    }
    if (temp instanceof Error) {
        return temp;
    }
    if ((temp instanceof Buffer) && !temp.stat) {
        temp.stat = getVersion(path.join(String(this), curl));
    }
    return temp;
};

/**
 * 取消路径监听
 * @param {string} curl
 * @param {object} temp
 */
var unwatch = function (curl, temp) {
    if (temp instanceof Object) {
        for (var k in temp) {
            var cvrl = path.join(curl, k);
            watch(cvrl);
            unwatch(cvrl, temp[k]);
        }
    }
};
var getExtts = function (extts) {
    if (extts instanceof Array && extts.length <= 1) extts = extts;
    return extts || "";
};
/**
 * 根椐filesroot路径设置文件缓冲
 * @param {string} filesroot
 */
var cache = function (filesroot, rebuild, buffer_size_limit) {
    var sk = function (url, extts) {
    };
    filesroot = filesroot.split(",");
    var tree = filesroot.map(froot => fs.existsSync(froot) && fs.statSync(froot).isDirectory() && getdir(froot) || Object.create(null));
    if (isFinite(buffer_size_limit) && buffer_size_limit >= 0) {
        sk.buffer_size = buffer_size_limit | 0;
    }
    var seekerAsync = function (url, extts = "") {
        extts = getExtts(extts);
        if (!Array.isArray(extts)) {
            extts = [extts];
        }
        var seeker = new Function();
        seeker.toString = function () {
            return this;
        }.bind(filesroot[0]);
        seeker.getURL = function () {
            return url;
        };
        seeker.buffer_size = sk.buffer_size;
        return new Promise(function (ok, oh) {
            var cy = 0;
            var cx = 0;
            var result, matchParent;
            var run = function () {
                if (cx >= extts.length) {
                    cy++;
                    if (cy >= tree.length) {
                        if (result === undefined) {
                            if (matchParent) {
                                url = url.replace(/\\/g, '/');
                                if (!/\/$/.test(url)) {
                                    url += '/';
                                    result = url;
                                }
                            }
                        }
                        return ok(result);
                    }
                    seeker.toString = function () {
                        return this;
                    }.bind(filesroot[cy]);
                    cx = 0;
                }
                var tree1 = tree[cy];
                var extt = extts[cx];
                var error;
                var promise = seekAsync.call(seeker, url + extt, tree1, rebuild);
                if (promise instanceof Promise) {
                    promise.catch(function (e) {
                        error = e;
                    }).then(function (result) {
                        if (result instanceof Buffer) return ok(result);
                        if (error) oh(error);
                        else run();
                    });
                } else {

                    if (promise instanceof Buffer || promise instanceof Function) return ok(promise);
                    if (promise instanceof Object && !result) {
                        var package_file = "package.json";
                        if (package_file in promise) {
                            if (promise[package_file] === false) {
                                seekAsync.call(seeker, url + extt + "/" + package_file, tree1, a => JSON.parse(String(a))).catch(oh).then(run);
                                return;
                            } else if (promise[package_file] instanceof Buffer) {
                                promise[package_file] = JSON.parse(promise[package_file].toString());
                            }
                            var package_data = promise[package_file];
                            if (package_data instanceof Object && package_data.main) {
                                var roots = package_data.main.split(/[\/\\]+/).filter(a => a && a !== '.' && !/\:$/.test(a));
                                if (roots[0] in promise) {
                                    result = path.join(url, roots.join('/')).replace(/\\/g, '/');
                                    return ok(result);
                                }
                            }
                        } else {
                            var seek_index = 'index';
                            var index = extts.findIndex(a => seek_index + a in promise);
                            if (~index) {
                                result = path.join(url, seek_index + extts[index]).replace(/\\/g, '/');
                            } else {
                                if (typeof promise === 'string') result = promise;
                            }
                        }
                        matchParent = true;
                    }
                    cx++;
                    run();
                }

            };
            run();
        });
    };
    sk.async = seekerAsync;
    sk.new = cache;
    return sk;
}
var _reload_handlers = [];
cache.onreload = function (handler) {
    if (handler instanceof Function) _reload_handlers.push(handler);
};
module.exports = cache;<|endoftext|>


<commit_before>var h = require('hyperscript')
var pull = require('pull-stream')
var cat = require('pull-cat')
var combobox = require('hypercombo')

exports.needs = {
  avatar_image_link: 'first',
  avatar_action: 'map',
  avatar_description: 'first',
  avatar_edit: 'first',
  avatar_pinned: 'first',
  follows: 'first',
  followers: 'first',
  sbot_get: 'first',
  sbot_query: 'first',
  message_confirm: 'first',
  message_compose: 'first'
}

exports.gives = 'avatar_profile'

exports.create = function (api) {

  function image_link (id) {
    return api.avatar_image_link(id, 'thumbnail')
  }

  return function (id) {

    var sk0rgs_el = h('ul')
    var follows_el = h('div.profile__follows.wrap')
    var friends_el = h('div.profile__friendss.wrap')
    var followers_el = h('div.profile__followers.wrap')
    var a, b

    function adoptSkillForm() {
      var adoptSelector_el;
      var sk0rgName_el;
      var form = h('form',
        h('strong', 'Adopt existing:'),
        adoptSelector_el = combobox({
          style: {'max-width': '26ex'},
          read: pull(
            api.sbot_query({query:
              [{"$filter": {"value": { "content":{ "type":"sk0rg" }}}}]
            }),
            // filter unadopted ones
            pull.map(function (sk) {
              var t = sk.value.content.text
              if (t.length > 70) t = t.substr(0, 70) + '…'
              return h('option', {value: sk.key},
                sk.value.content.name + ": " + t
              )
            }))
        }),
        h('button', {onclick: function (e) {
          e.preventDefault()
          api.message_confirm({
            "type": 'about', "about": id,
            "sk0rg":adoptSelector_el.value, "adopted": true,
          }, function (err, msg) {
            if(err) return alert(err)
            if(!msg) return
          })
        }}, "Adopt"),
        h('br'),
        h('strong', 'Add missing sk0rg:'),
        sk0rgName_el=h('input', {placeholder: "sk0rg name"}),
        api.message_compose(
          {type: 'sk0rg' },
          function (value) {
            value.name = sk0rgName_el.value
            return value
          },
          function (err, sk) {
            if(err) return alert(err)
            if(!sk) return
            var title = sk.value.content.text
            if(title.length > 70) title = title.substr(0, 70) + '…'
            sk0rgs_el.appendChild(h('li',
              h('a', {href: '#'+id}, sk.content.name),
              ": " + sk.content.text))
          }
        )
      )
      return form
    }

    // this counts skill adopted:true and :false messages to see which ones are still valid
    function countAdopts(ary) {
      var cntMap  = {}
      ary.forEach(function(msg) {
        var c = msg.value.content
        cntMap[c.sk0rg]={
          cnt:0,
          msg: msg,
        }
      })
      ary.forEach(function(msg) {
        var c = msg.value.content
        if (typeof c.adopted === "boolean") {
          cntMap[c.sk0rg].cnt = c.adopted ? cntMap[c.sk0rg].cnt + 1:cntMap[c.sk0rg].cnt - 1
        }
      })
      return cntMap
    }

    // fill sk0rgs list
    pull(
      api.sbot_query({query: [
        {"$filter": {
          "value":{
            "author": id,
            "content": {"type":"about", "sk0rg":{"$prefix":""}}
          }
        }}
      ]}),
      pull.collect(function(err, ary) {
        if(err) {throw err; return;}
        var adoptCnt = countAdopts(ary)
        Object.keys(adoptCnt).forEach(function(objkey) { // deduplicate the array
          var aboutMsg = adoptCnt[objkey].msg
          if (adoptCnt[objkey].cnt > 0) { // only add adopted themes
            var sk = aboutMsg.value.content.sk0rg
            api.sbot_get(sk, function(err, skMsg) {
              sk0rgs_el.appendChild(h('li',
                h('a', {href: '#'+id}, skMsg.content.name), ": " + skMsg.content.text,

                h('a', {href: '#', onclick: function(e) {
                  e.preventDefault()
                  // copy msg and invert it
                  var untrack = aboutMsg.value.content
                  untrack.adopted = false
                  api.message_confirm(untrack, function (err, msg) {
                    if(err) return alert(err)
                    if(!msg) return
                  })
                }}, "X")
              ))
            })
          }
        })
      })
    )

    pull(api.follows(id), pull.unique(), pull.collect(function (err, ary) {
      a = ary || []; next()
    }))
    pull(api.followers(id), pull.unique(), pull.collect(function (err, ary) {
      b = ary || {}; next()
    }))

    function next () {
      if(!(a && b)) return
      var _c = [], _a = [], _b = []

      a.forEach(function (id) {
        if(!~b.indexOf(id)) _a.push(id)
        else               _c.push(id)
      })
      b.forEach(function (id) {
        if(!~_c.indexOf(id)) _b.push(id)
      })
      function add (ary, el) {
        ary.forEach(function (id) { el.appendChild(image_link(id)) })
      }

      add(_a, follows_el)
      add(_c, friends_el)
      add(_b, followers_el)
    }

    return h('div.column.profile',
      api.avatar_edit(id),
      api.avatar_description(id),
      h("strong", "pinned post"),
      api.avatar_pinned(id),
      api.avatar_action(id),
      h('div.profile__relationships.column',
        h('strong', 'sk0rgs'),
        sk0rgs_el,
        h('div', h('a', {href: '#', onclick: function (e) {
          e.preventDefault()
          this.parentNode.replaceChild(adoptSkillForm(id), this)
        }}, 'Adopt Sk0rg…')),
        h('strong', 'follows'),
        follows_el,
        h('strong', 'friends'),
        friends_el,
        h('strong', 'followers'),
        followers_el
      )
    )
  }

}

<commit_msg>small fixes<commit_after>var h = require('hyperscript')
var pull = require('pull-stream')
var cat = require('pull-cat')
var combobox = require('hypercombo')

exports.needs = {
  avatar_image_link: 'first',
  avatar_action: 'map',
  avatar_description: 'first',
  avatar_edit: 'first',
  avatar_pinned: 'first',
  follows: 'first',
  followers: 'first',
  sbot_get: 'first',
  sbot_query: 'first',
  message_confirm: 'first',
  message_compose: 'first'
}

exports.gives = 'avatar_profile'

exports.create = function (api) {

  function image_link (id) {
    return api.avatar_image_link(id, 'thumbnail')
  }

  return function (id) {

    var sk0rgs_el = h('ul')
    var follows_el = h('div.profile__follows.wrap')
    var friends_el = h('div.profile__friendss.wrap')
    var followers_el = h('div.profile__followers.wrap')
    var a, b

    function adoptSkillForm() {
      var adoptSelector_el;
      var sk0rgName_el;
      var form = h('form',
        h('strong', 'Adopt existing:'),
        adoptSelector_el = combobox({
          style: {'max-width': '26ex'},
          read: pull(
            api.sbot_query({query:
              [{"$filter": {"value": { "content":{ "type":"sk0rg" }}}}]
            }),
            // filter unadopted ones
            pull.map(function (sk) {
              var t = sk.value.content.text
              if (t.length > 70) t = t.substr(0, 70) + '…'
              return h('option', {value: sk.key},
                sk.value.content.name + ": " + t
              )
            }))
        }),
        h('button', {onclick: function (e) {
          e.preventDefault()
          api.message_confirm({
            "type": 'about', "about": id,
            "sk0rg":adoptSelector_el.value, "adopted": true,
          }, function (err, msg) {
            if(err) return alert(err)
            if(!msg) return
          })
        }}, "Adopt"),
        h('br'),
        h('strong', 'Add missing sk0rg:'),
        sk0rgName_el=h('input', {placeholder: "sk0rg name"}),
        api.message_compose(
          {type: 'sk0rg' },
          function (value) {
            value.name = sk0rgName_el.value
            return value
          },
          function (err, sk) {
            if(err) return alert(err)
            if(!sk) return
            var title = sk.value.content.text
            if(title.length > 70) title = title.substr(0, 70) + '…'
            sk0rgs_el.appendChild(h('li',
              h('a', {href: '#'+id}, sk.content.name),
              ": " + sk.content.text))
          }
        )
      )
      return form
    }

    // this counts skill adopted:true and :false messages to see which ones are still valid
    function countAdopts(ary) {
      var cntMap  = {}
      ary.forEach(function(msg) {
        var c = msg.value.content
        cntMap[c.sk0rg]={
          cnt:0,
          msg: msg,
        }
      })
      ary.forEach(function(msg) {
        var c = msg.value.content
        if (typeof c.adopted === "boolean") {
          cntMap[c.sk0rg].cnt = c.adopted ? cntMap[c.sk0rg].cnt + 1:cntMap[c.sk0rg].cnt - 1
        }
      })
      return cntMap
    }

    // fill sk0rgs list
    pull(
      api.sbot_query({query: [
        {"$filter": {
          "value":{
            "author": id,
            "content": {"type":"about", "sk0rg":{"$prefix":""}}
          }
        }}
      ]}),
      pull.collect(function(err, ary) {
        if(err) {throw err; return;}
        var adoptCnt = countAdopts(ary)
        Object.keys(adoptCnt).forEach(function(msgKey) { // deduplicate the array
          var aboutMsg = adoptCnt[msgKey].msg
          if (adoptCnt[msgKey].cnt > 0) { // only add adopted themes
            var sk = aboutMsg.value.content.sk0rg
            api.sbot_get(sk, function(err, skMsg) {
              sk0rgs_el.appendChild(h('li',
                h('a', {href: '#'+msgKey}, skMsg.content.name), ": " + skMsg.content.text,

                h('a', {href: '#', onclick: function(e) {
                  e.preventDefault()
                  // copy msg and invert it
                  var untrack = aboutMsg.value.content
                  untrack.adopted = false
                  api.message_confirm(untrack, function (err, msg) {
                    if(err) return alert(err)
                    if(!msg) return
                  })
                }}, "(☢)")
              ))
            })
          }
        })
      })
    )

    pull(api.follows(id), pull.unique(), pull.collect(function (err, ary) {
      a = ary || []; next()
    }))
    pull(api.followers(id), pull.unique(), pull.collect(function (err, ary) {
      b = ary || {}; next()
    }))

    function next () {
      if(!(a && b)) return
      var _c = [], _a = [], _b = []

      a.forEach(function (id) {
        if(!~b.indexOf(id)) _a.push(id)
        else               _c.push(id)
      })
      b.forEach(function (id) {
        if(!~_c.indexOf(id)) _b.push(id)
      })
      function add (ary, el) {
        ary.forEach(function (id) { el.appendChild(image_link(id)) })
      }

      add(_a, follows_el)
      add(_c, friends_el)
      add(_b, followers_el)
    }

    return h('div.column.profile',
      api.avatar_edit(id),
      api.avatar_description(id),
      h("strong", "pinned post"),
      api.avatar_pinned(id),
      api.avatar_action(id),
      h('div.profile__relationships.column',
        h('strong', 'sk0rgs'),
        sk0rgs_el,
        h('div', h('a', {href: '#', onclick: function (e) {
          e.preventDefault()
          this.parentNode.replaceChild(adoptSkillForm(id), this)
        }}, 'Adopt Sk0rg…')),
        h('strong', 'follows'),
        follows_el,
        h('strong', 'friends'),
        friends_el,
        h('strong', 'followers'),
        followers_el
      )
    )
  }

}

<|endoftext|>


<commit_before>// Get all the "··· More" menus
var menus = getMenusArray();

for (var index in menus) {
    var newItem = $(document.createElement('li'));
    var innerButton = $(document.createElement('button'));
    var menu = $(menus[index]);
    var quoteButtonText = chrome.i18n.getMessage("quote");

    // Set up the button
    innerButton.attr('class', 'dropdown-link');
    innerButton.attr('role', 'menuitem');
    innerButton.attr('type', 'button');
    // Attach it the click listener
    innerButton.click(onClickQuoteButton);
    // Set the button's text
    innerButton.text(quoteButtonText);
    // Add the "Quote" button
    newItem.prepend(innerButton);
    // Add the item to the list
    menu.prepend(newItem);
}

/**
 ** FUNCTIONS
 **/

function getMenusArray() {
    return [].filter.call(
                document.querySelectorAll("li.more-tweet-actions .dropdown-menu > ul"),
                function(value, index, array) {
                    return true;
                },
                []);
}

function getTweetAuthor(element) {
    return $(element).closest('.tweet').find('.username').text();
}

function getTweetText(element) {
    return $(element).closest('.tweet').find('.tweet-text').text();
}

function onClickQuoteButton(event) {
    // Get the btn's parent tweet's author
    var author = getTweetAuthor(event.currentTarget);
    // Get the btn's parent tweet's text
    var text = getTweetText(event.currentTarget);
    // Set the focus on the text field first (so the tweet form will be expanded)
    $('#tweet-box-mini-home-profile').focus();
    // Set the tweet text as its value
    $('#tweet-box-mini-home-profile').first().text('RT ' + author + ': ' + text);
    // Set the scroll bar at the top of the page
    $(document).scrollTop(0);
    // Close the dropdown menu
    $(event.currentTarget).closest('.tweet').find('.dropdown-toggle').click();
}
<commit_msg>timeline> Added 'addQuoteButton()' function<commit_after>// Get all the "··· More" menus
var menus = getMenusArray();

addQuoteButton(menus);

/**
 ** FUNCTIONS
 **/

function addQuoteButton(menus) {
    for (var index in menus) {
        var newItem = $(document.createElement('li'));
        var innerButton = $(document.createElement('button'));
        var menu = $(menus[index]);
        var quoteButtonText = chrome.i18n.getMessage("quote");

        // Set up the button
        innerButton.attr('class', 'dropdown-link');
        innerButton.attr('role', 'menuitem');
        innerButton.attr('type', 'button');
        // Attach it the click listener
        innerButton.click(onClickQuoteButton);
        // Set the button's text
        innerButton.text(quoteButtonText);
        // Add the "Quote" button
        newItem.prepend(innerButton);
        // Add the item to the list
        menu.prepend(newItem);
    }
}

function getMenusArray() {
    return [].filter.call(
                document.querySelectorAll("li.more-tweet-actions .dropdown-menu > ul"),
                function(value, index, array) {
                    return true;
                },
                []);
}

function getTweetAuthor(element) {
    return $(element).closest('.tweet').find('.username').text();
}

function getTweetText(element) {
    return $(element).closest('.tweet').find('.tweet-text').text();
}

function onClickQuoteButton(event) {
    // Get the btn's parent tweet's author
    var author = getTweetAuthor(event.currentTarget);
    // Get the btn's parent tweet's text
    var text = getTweetText(event.currentTarget);
    // Set the focus on the text field first (so the tweet form will be expanded)
    $('#tweet-box-mini-home-profile').focus();
    // Set the tweet text as its value
    $('#tweet-box-mini-home-profile').first().text('RT ' + author + ': ' + text);
    // Set the scroll bar at the top of the page
    $(document).scrollTop(0);
    // Close the dropdown menu
    $(event.currentTarget).closest('.tweet').find('.dropdown-toggle').click();
}
<|endoftext|>


<commit_before>(function(a,b){function g(b,c){var d=this;d.container=a(b),d.options=a.extend({},f,c),d.init()}var d="shapeshift",f=(b.document,{centerGrid:!0,enableAnimation:!0,enableAutoHeight:!0,enableDrag:!0,enableDragAnimation:!0,enableRearrange:!0,enableResize:!0,animateSpeed:160,columns:null,dragClone:!1,dragRate:75,dragWhitelist:"*",dropCutoff:0,dropWhitelist:"*",gutterX:10,gutterY:10,minHeight:100,paddingY:0,paddingX:0,selector:""});g.prototype.init=function(){var a=this;a.eventSetup(),a.container.trigger("ss-event-arrange")},g.prototype.eventSetup=function(){var a=this,b=a.options;a.container.attr("data-ss-rearrangeable",b.enableRearrange),a.container.off("ss-event-arrange").on("ss-event-arrange",function(){a.arrange()}),a.container.off("ss-event-dragreset").on("ss-event-dragreset",function(){a.dragClear(),a.drag()}),jQuery.ui?jQuery.ui.draggable?b.enableDrag?a.container.trigger("ss-event-dragreset"):a.dragClear():b.enableDrag&&alert("jQuery.shapeshift is trying to enable drag and drop but jQuery UI Draggable/Droppable has not been included yet."):b.enableDrag&&alert("jQuery.shapeshift is trying to enable drag and drop but jQuery UI has not been included yet."),b.enableResize&&a.resize()},g.prototype.arrange=function(){var b=this,c=b.options,d=b.container.children(c.selector).filter(":visible"),e=b.getPositions(b.container,!1),f=!0;f=d.filter(".ss-moving")[0]?c.enableDragAnimation:c.enableAnimation;for(var g=0;e.length>g;g++){var h=a(d[g]);h.hasClass("ss-moving")||(f?h.stop(!0,!1).animate(e[g],c.animateSpeed):h.css(e[g]))}b.container.css("height",c.containerHeight)},g.prototype.drag=function(){function i(b){f=a(b.target),$curContainer=f.parent(),d.dragClone&&($clone=f.clone().insertBefore(f).addClass("ss-clone")),f.addClass("ss-moving"),$curContainer.trigger("ss-event-dragged",f)}function j(b){$clone=a(".ss-clone"),$clone[0]&&($cloneContainer=$clone.parent(),$cloneContainer[0]===f.parent()[0]?$clone.remove():($clone.removeClass("ss-clone"),$cloneContainer.trigger("ss-event-dragreset"),f.parent().trigger("ss-event-dragreset"))),a(b.target).removeClass("ss-moving").parent().trigger("ss-event-arrange")}function k(i,j){!h&&$curContainer.data("ss-rearrangeable")&&(h=!0,g=c.getIntendedPosition(i),e=$curContainer.children(":not(.ss-moving):visible"),g!=e.size()?$curContainer[0]!=f.parent()[0]?($target=e.last(),f.insertAfter($target)):($target=e.get(g),f.insertBefore($target)):($target=e.get(g-1),f.insertAfter($target)),$target||f.appendTo($curContainer),$curContainer.trigger("ss-event-arrange"),a(".ss-prev-container").trigger("ss-event-arrange"),b.setTimeout(function(){h=!1},d.dragRate)),j.position.left=i.pageX-a(i.target).parent().offset().left-d.childWidth/2,j.position.top=i.pageY-a(i.target).parent().offset().top-f.outerHeight()/2}function l(){f=a(".ss-moving").removeClass("ss-moving"),$selectedContainer=f.parent(),$selectedContainer.trigger("ss-event-arrange").trigger("ss-event-dropped",f)}function m(b){$curContainer.addClass("ss-prev-container"),$curContainer=a(b.target).removeClass("ss-prev-container")}var c=this,d=c.options;$curContainer=c.container;var e=c.container.children(d.selector),f=null,g=null,h=!1;e.filter(d.dragWhitelist).draggable({addClasses:!1,containment:"document",zIndex:9999,drag:function(a,b){k(a,b)},start:function(a,b){i(a,b)},stop:function(a){j(a)}}),c.container.droppable({accept:d.dropWhitelist,drop:function(a){l(a)},over:function(a){m(a)}})},g.prototype.dragClear=function(){this.container.droppable().droppable("destroy"),this.container.children().draggable().draggable("destroy")},g.prototype.getPositions=function(c,d){var e=this,f=e.options,g=c.children(f.selector).filter(":visible"),h=f.columns,i=[],j=null,k=f.paddingX,l=[];d&&(g=g.not(".ss-moving")),f.childWidth=g.first().outerWidth(!0),j=f.childWidth+f.gutterX,h||(h=Math.floor(c.innerWidth()/j),h>g.length&&(h=g.length)),f.centerGrid&&(k=Math.floor((c.innerWidth()-h*j)/2)+f.gutterX/2);for(var m=0;h>m;m++)i.push(f.paddingY);for(var n=0;g.length>n;n++){var o=a(g[n]),p=a.inArray(Math.min.apply(b,i),i),q=o.outerHeight(!0)+f.gutterY,r=j*p+k,s=i[p];attributes={left:r,top:s},l[n]=attributes,i[p]+=q}return f.enableAutoHeight&&(q=Math.max.apply(Math,i),f.minHeight>q&&(q=f.minHeight),f.containerHeight=q),l},g.prototype.getIntendedPosition=function(){for(var c=this,d=c.options,e=a(".ss-moving"),f=e.parent(),g=0,h=e.position().left+d.childWidth/2,i=e.position().top+e.outerHeight()/2,j=9999,k=c.getPositions(f,!0),l=k.length-d.dropCutoff,m=0;k.length>m;m++)if(l>m&&(attributes=k[m],h>attributes.left&&i>attributes.top)){var n=h-attributes.left,o=i-attributes.top;if(distance=Math.sqrt(n*n+o*o),j>distance&&(j=distance,g=m,m===k.length-1)){var p=f.children().not(".ss-moving").last();(o>.75*p.outerHeight()||n>.75*d.childWidth||0>n)&&g++}}return g},g.prototype.resize=function(){var c=this,d=!1;a(b).on("resize",function(){d||(d=!0,setTimeout(function(){d=!1,c.container.trigger("ss-event-arrange")},75))})},a.fn[d]=function(b){return this.each(function(){a.data(this,"plugin_"+d,new g(this,b))})}})(jQuery,window);<commit_msg>Updated minified<commit_after>(function(a,b){function g(b,c){var d=this;d.container=a(b),d.options=a.extend({},f,c),d.init()}var d="shapeshift",f=(b.document,{centerGrid:!0,enableAnimation:!0,enableAutoHeight:!0,enableDrag:!0,enableDragAnimation:!0,enableRearrange:!0,enableResize:!0,animateSpeed:160,columns:null,dragClone:!1,dragRate:75,dragWhitelist:"*",dropCutoff:0,dropWhitelist:"*",gutterX:10,gutterY:10,minHeight:200,paddingY:0,paddingX:0,selector:""});g.prototype.init=function(){var a=this;a.eventSetup(),a.container.trigger("ss-event-arrange")},g.prototype.eventSetup=function(){var a=this,b=a.options;a.container.attr("data-ss-rearrangeable",b.enableRearrange),a.container.off("ss-event-arrange").on("ss-event-arrange",function(){a.arrange()}),a.container.off("ss-event-dragreset").on("ss-event-dragreset",function(){a.dragClear(),a.drag()}),jQuery.ui?jQuery.ui.draggable?b.enableDrag?a.container.trigger("ss-event-dragreset"):a.dragClear():b.enableDrag&&alert("jQuery.shapeshift is trying to enable drag and drop but jQuery UI Draggable/Droppable has not been included yet."):b.enableDrag&&alert("jQuery.shapeshift is trying to enable drag and drop but jQuery UI has not been included yet."),b.enableResize&&a.resize()},g.prototype.arrange=function(){var b=this,c=b.options,d=b.container.children(c.selector).filter(":visible"),e=b.getPositions(b.container,!1),f=!0;f=d.filter(".ss-moving")[0]?c.enableDragAnimation:c.enableAnimation;for(var g=0;e.length>g;g++){var h=a(d[g]);h.hasClass("ss-moving")||(f?h.stop(!0,!1).animate(e[g],c.animateSpeed):h.css(e[g]))}b.container.css("height",c.containerHeight)},g.prototype.drag=function(){function k(b){f=a(b.target),g=f.outerHeight()/2,h=f.outerWidth()/2,$curContainer=f.parent(),d.dragClone&&($clone=f.clone().insertBefore(f).addClass("ss-clone")),f.addClass("ss-moving"),$curContainer.trigger("ss-event-dragged",f)}function l(b){$clone=a(".ss-clone"),$clone[0]&&($cloneContainer=$clone.parent(),$cloneContainer[0]===f.parent()[0]?$clone.remove():($clone.removeClass("ss-clone"),$cloneContainer.trigger("ss-event-dragreset"),f.parent().trigger("ss-event-dragreset"))),a(b.target).removeClass("ss-moving").parent().trigger("ss-event-arrange")}function m(k,l){!j&&$curContainer.data("ss-rearrangeable")&&(j=!0,i=c.getIntendedPosition(k),e=$curContainer.children(":not(.ss-moving):visible"),i!=e.size()?$curContainer[0]!=f.parent()[0]?($target=e.last(),$target[0]?f.insertAfter($target):$curContainer.prepend(f)):($target=e.get(i),f.insertBefore($target)):($target=e.get(i-1),f.insertAfter($target)),$target||f.appendTo($curContainer),$curContainer.trigger("ss-event-arrange"),a(".ss-prev-container").trigger("ss-event-arrange"),b.setTimeout(function(){j=!1},d.dragRate)),l.position.left=k.pageX-f.parent().offset().left-h,l.position.top=k.pageY-f.parent().offset().top-g}function n(){f=a(".ss-moving").removeClass("ss-moving"),$selectedContainer=f.parent(),$selectedContainer.trigger("ss-event-arrange").trigger("ss-event-dropped",f)}function o(b){$curContainer.addClass("ss-prev-container"),$curContainer=a(b.target).removeClass("ss-prev-container")}var c=this,d=c.options;$curContainer=c.container;var f,g,h,i,e=c.container.children(d.selector),j=!1;e.filter(d.dragWhitelist).draggable({addClasses:!1,containment:"document",zIndex:9999,drag:function(a,b){m(a,b)},start:function(a,b){k(a,b)},stop:function(a){l(a)}}),c.container.droppable({accept:d.dropWhitelist,tolerance:"intersect",drop:function(a){n(a)},over:function(a){o(a)}})},g.prototype.dragClear=function(){this.container.droppable().droppable("destroy"),this.container.children().draggable().draggable("destroy")},g.prototype.getPositions=function(c,d){var e=this,f=e.options,g=c.children(f.selector).filter(":visible"),h=f.columns,i=[],j=null,k=f.paddingX,l=[];d&&(g=g.not(".ss-moving")),f.childWidth=g.first().outerWidth(!0),j=f.childWidth+f.gutterX,h||(h=Math.floor(c.innerWidth()/j),h>g.length&&(h=g.length)),f.centerGrid&&(k=Math.floor((c.innerWidth()-h*j)/2)+f.gutterX/2);for(var m=0;h>m;m++)i.push(f.paddingY);for(var n=0;g.length>n;n++){var o=a(g[n]),p=a.inArray(Math.min.apply(b,i),i),q=o.outerHeight(!0)+f.gutterY,r=j*p+k,s=i[p];attributes={left:r,top:s},l[n]=attributes,i[p]+=q}return f.enableAutoHeight&&(q=Math.max.apply(Math,i),f.minHeight>q&&(q=f.minHeight),f.containerHeight=q),l},g.prototype.getIntendedPosition=function(){for(var c=this,d=c.options,e=a(".ss-moving"),f=e.parent(),g=0,h=e.position().left+d.childWidth/2,i=e.position().top+e.outerHeight()/2,j=9999,k=c.getPositions(f,!0),l=k.length-d.dropCutoff,m=0;k.length>m;m++)if(l>m&&(attributes=k[m],h>attributes.left&&i>attributes.top)){var n=h-attributes.left,o=i-attributes.top;if(distance=Math.sqrt(n*n+o*o),j>distance&&(j=distance,g=m,m===k.length-1)){var p=f.children().not(".ss-moving").last();(o>.75*p.outerHeight()||n>.75*d.childWidth||0>n)&&g++}}return g},g.prototype.resize=function(){var c=this,d=!1;a(b).on("resize",function(){d||(d=!0,setTimeout(function(){d=!1,c.container.trigger("ss-event-arrange")},75))})},a.fn[d]=function(b){return this.each(function(){a.data(this,"plugin_"+d,new g(this,b))})}})(jQuery,window);<|endoftext|>