<commit_before>var Promise = require('bluebird');

module.exports = function (config, log, redis) {

	var maxClearLevels      = 1000000;
	var clearLaterSet       = config.clearLaterSet;
	var clearNowSet         = config.clearNowSet;
	var clearsPerIteration  = config.clearNowAmountPerIteration;
	var prefix              = config.redis.prefix;
	var prefixData          = prefix + 'd:';
	var prefixContains      = prefix + 'c:';
	var prefixIn            = prefix + 'i:';

	/**
	 * Get a value from the cache with the given key.
	 *
	 * @param {string} options.key The key to get the value for.
	 *
	 * @returns {Promise} A promise that resolves to the value or `null` if none.
	 */
	function cacheGet(options) {

		if (!options.key) {
			return Promise.reject('must provide a "key" to get');
		}

		log.debug('cache: [get] ' + options.key);

		var key = prefixData + options.key;

		return redis('get', [key]).then(function (json) {

			var data = null;
			try {
				data = JSON.parse(json);
			} catch (e) {
				log.error('cache: decoding redis(get) ' + options.key, { json: json });
			}

			return data;
		});
	}

	/**
	 * Get many values from cache with the given keys. Maintains order.
	 *
	 * @param {string[]} options.keys The keys to get values for.
	 *
	 * @returns {Promise} A promise that resolves to an array of values or `null`
	 *   in the same order as the keys given.
	 */
	function cacheGetMany(options) {

		if (!options.keys || options.keys.length) {
			return Promise.reject('must provide "keys" to get');
		}

		var keys    = [];
		var keysLen = options.keys.length;
		for (var i = 0; i < keysLen; i++) {
			keys.push(prefixData + options.keys[i]);
		}

		log.debug('cache: [getMany] ' + keysLen + ' keys');

		return redis('mget', keys).then(function (jsonArray) {

			if (jsonArray && jsonArray.length !== keysLen) {
				var e = new Error('cache: redis(mget) returned ' + jsonArray.length + ' replies, expected ' + keys.length);
				log.error(e.message, { keys: keys });
				return reject(e);
			}

			var dataArray = [];
			for (var i = 0; i < keysLen; i++) {
				var json = jsonArray[i];
				try {
					dataArray.push(JSON.parse(json));
				} catch (e) {
					log.error('cache: decoding redis(mget) ' + options.keys[i], { json: json });
					dataArray.push(null);
				}
			}

			return dataArray;
		});
	}

	/**
	 * Set the given key to the given data with the given timeout in milliseconds.
	 *
	 * @param {string} options.key    The key for the set.
	 * @param {*}      options.data   The data to set.
	 * @param {number} options.millis The TTL for the set (in millis).
	 *
	 * @returns {Promise} A promise that resolves to an object containing details of the set operation:
	 *
	 * {
	 *   cacheSet            : // if the set was successful
	 *   clearAssocIn        : // if the associated key clear was successful
	 *   assocIn             : // how many associations were tied to the "in" set
	 *   assocContains       : // how many "contains" sets this key was added to
	 *   expireAssocIn       : // whether the "in" set TTL was set
	 *   expireAssocContains : // whether the "contains" sets TTLs were set
	 * }
	 *
	 */
	function cacheSet(options) {

		if (!options.key) {
			return Promise.reject(new Error('must provide a "key" to set'));
		}
		if ('undefined' === options.data) {
			return Promise.reject(new Error('must provide "data" to set'));
		}
		if (!options.millis) {
			return Promise.reject(new Error('must provide a "millis" TTL'));
		}

		var key      = options.key;
		var millis   = options.millis;
		var assoc    = options.associations;
		var assocLen = assoc ? assoc.length : 0;
		var keyData  = prefixData + key;
		var keyIn    = prefixIn   + key;

		log.debug('cache: [set] ' + key + ' for ' + millis + ' millis');

		// Set the data for the key (encoded as JSON).
		var json = JSON.stringify(options.data);
		var asyncCacheSet = redis('set', [keyData, json, 'PX', millis]);

		// Clear any existing associations.
		var asyncClearAssocIn = redis('del', [keyIn]);

		var asyncAssociateIn;
		var asyncExpireAssociateIn;
		var asyncAssociateContains;
		var asyncExpireAssociateContains;

		// Make associations.
		if (assocLen) {

			// The given associated keys are *in* this key.
			var argsAssocIn =[keyIn].concat(assoc);
			asyncAssociateIn = redis('sadd', argsAssocIn);
			asyncExpireAssociateIn = redis('pexpire', [keyIn, millis]);

			// This key *contains* the given associated keys.
			var waitingAssociateContains = [];
			var argsSetExpire = [];
			for (var i = 0; i < assocLen; i++) {
				var assocKey         = assoc[i];
				var assocKeyContains = prefixContains + assocKey;
				waitingAssociateContains.push(redis('sadd', [assocKeyContains, key]));
				argsSetExpire.push(assocKeyContains);
			}
			asyncAssociateContains = Promise.all(waitingAssociateContains);

			// Set the TTL for all contains sets based on their contained keys' TTLs.
			argsSetExpire.push(prefixData);
			asyncExpireAssociateContains = redis('pexpiresmax', argsSetExpire);

		} else {

			asyncAssociateIn             = Promise.resolve();
			asyncExpireAssociateIn       = Promise.resolve();
			asyncAssociateContains       = Promise.resolve();
			asyncExpireAssociateContains = Promise.resolve();

		}

		return Promise.props({
			cacheSet            : asyncCacheSet,
			clearAssocIn        : asyncClearAssocIn,
			assocIn             : asyncAssociateIn,
			assocContains       : asyncAssociateContains,
			expireAssocIn       : asyncExpireAssociateIn,
			expireAssocContains : asyncExpireAssociateContains
		});
	}

	/**
	 * Clear the given keys in cache and the given amount of association levels.
	 *
	 * @param {string[]}      options.keys   The keys to clear
	 * @param {number|string} options.levels The association levels to clear. Defaults to "all".
	 *   Can be a positive integer, or a string ("none" or "all).
	 *
	 * @returns {Promise} A promise that resolves to information about the clear.
	 *
	 * {
	 *   level               : // this level number (starts at 1)
	 *   keys                : // keys to be cleared at this level
	 *   keysCount           : // number of keys to be cleared at this level
	 *   cleared             : // number of data keys cleared
	 *   keysContains        : // keys ("contains" sets) modified
	 *   removedFromContains : // keys removed from "contains" sets
	 *   keysInDeleted       : // keys ("in" sets) deleted
	 *   keysNextLevel       : // the keys that will be cleared on the next level
	 *   nextLevel           : // an object similar to this one, containing information about the next level clear
	 * }
	 *
	 */
	function cacheClear(options) {

		if (!options.keys || options.keys.length) {
			return Promise.reject('must provide "keys" to clear');
		}

		var keysSeen = {};
		var maxLevel = maxClearLevels;

		if (options.levels === 'none') {
			maxLevel = 0;
		} else if ('number' === typeof options.levels) {
			maxLevel = Math.max(0, Math.floor(+options.levels));
		}

		// Clear the cache then attach a property of all cache keys cleared.
		return cacheClearLevel(options.keys, 1).then(function (info) {
			info.allKeysCleared = Object.keys(keysSeen);
			return info;
		});

		function cacheClearLevel(keys, level) {

			// Build a unique set of keys for "data", "in", and "contains" key sets.
			var keysStrings  = [];
			var keysData     = [];
			var keysIn       = [];
			var keysContains = [];
			for (var i = 0; i < keys.length; i++) {
				var key = keys[i];
				if (!keysSeen[key]) {
					keysSeen[key] = true;
					keysStrings.push('' + key);
					keysData.push(prefixData + key);
					keysIn.push(prefixIn + key);
					keysContains.push(prefixContains + key);
				}
			}

			log.debug('cache: [clear] (level ' + level + ') ' + keysData.length + ' keys');

			// Delete the data from cache.
			var asyncClear = redis('del', keysData);

			// Get all of the *contains* keys for the associations from the *in* keys.
			var asyncGetContainsKeys = redis('sunion', keysIn);

			// Delete all of the *in* keys for the associations.
			var asyncDeleteInSets = redis('del', keysIn);

			// Remove the keys that were cleared from the *contains* keys.
			var asyncRemoveContains = asyncGetContainsKeys.then(function (keysAllContains) {
				return Promise.all((keysAllContains || []).map(function (key) {
					return redis('srem', [prefixContains + key].concat(keys));
				})).then(function (results) {
					return (results && results.length) ? results.reduce(function (a, b) { return a + b; }) : 0;
				});
			});

			var asyncKeysNextLevel;
			var asyncClearNextLevel;

			if (level > maxLevel) {

				asyncKeysNextLevel  = Promise.resolve([]);
				asyncClearNextLevel = Promise.resolve();

			} else {

				// Get the next level of keys to clear.
				asyncKeysNextLevel = redis('sunion', keysContains);

				// Clear the next level of keys.
				asyncClearNextLevel = asyncKeysNextLevel.then(function (keysNextLevel) {
					return keysNextLevel.length ? cacheClearLevel(keysNextLevel, level + 1) : Promise.resolve();
				});

			}

			return Promise.props({
				level               : level,
				keys                : keys,
				keysCount           : keys.length,
				cleared             : asyncClear,
				keysContains        : asyncGetContainsKeys,
				removedFromContains : asyncRemoveContains,
				keysInDeleted       : asyncDeleteInSets,
				keysNextLevel       : asyncKeysNextLevel,
				nextLevel           : asyncClearNextLevel
			});
		}
	}

	/**
	 * Add the given keys to a de-dupe queue for clearing later.
	 * These keys will be fully cleared.
	 *
	 * @param {string[]} options.keys The keys to clear later.
	 *
	 * @returns {Promise} A promise that resolves to the number of keys added.
	 */
	function cacheClearLater(options) {

		if (!options.keys || options.keys.length) {
			return Promise.reject('must provide "keys" to clear later');
		}

		log.debug('cache: [clearLater] ' + options.keys.length + ' keys');

		// Add all of the given keys to a clear later set.
		var argsSetAdd = [clearLaterSet].concat(options.keys);
		return redis('sadd', argsSetAdd);
	}

	/**
	 * Clear all keys in the "clear now" set. This will clear in batches using the "smpop" script.
	 *
	 * @returns {Promise} A promise that resolves to an array of arrays containing the keys cleared per iteration.
	 */
	function cacheClearNow() {

		// Clear all of the keys from the given clear later set.
		var keysClearedByIteration = [];

		return cacheClearNowIteration();

		// Pop a few keys from the set and clear them.
		// Once they've been cleared, continue to pop keys and clear until the set has been emptied.
		function cacheClearNowIteration() {

			return redis('smpop', [clearNowSet, clearsPerIteration]).then(function (keysToClear) {
				if (keysToClear && keysToClear.length) {

					log.debug('cache: [clearNow] iteration ' + keysToClear.length + ' keys');

					return cacheClear({ keys: keysToClear }).then(function (clearInfo) {

						if (clearInfo && clearInfo.allKeysCleared) {
							keysClearedByIteration.push(clearInfo.allKeysCleared);
						}
						if (keysToClear.length !== clearsPerIteration) {
							return keysClearedByIteration;
						}

						return cacheClearNowIteration().then(function () {
							return keysClearedByIteration;
						});
					});
				}
			});
		}
	}

	return {
		get        : cacheGet,
		getMany    : cacheGetMany,
		set        : cacheSet,
		clear      : cacheClear,
		clearLater : cacheClearLater,
		clearNow   : cacheClearNow
	};
};
<commit_msg>fix for incorrect redis reply count during multi-get. throw error<commit_after>var Promise = require('bluebird');

module.exports = function (config, log, redis) {

	var maxClearLevels      = 1000000;
	var clearLaterSet       = config.clearLaterSet;
	var clearNowSet         = config.clearNowSet;
	var clearsPerIteration  = config.clearNowAmountPerIteration;
	var prefix              = config.redis.prefix;
	var prefixData          = prefix + 'd:';
	var prefixContains      = prefix + 'c:';
	var prefixIn            = prefix + 'i:';

	/**
	 * Get a value from the cache with the given key.
	 *
	 * @param {string} options.key The key to get the value for.
	 *
	 * @returns {Promise} A promise that resolves to the value or `null` if none.
	 */
	function cacheGet(options) {

		if (!options.key) {
			return Promise.reject('must provide a "key" to get');
		}

		log.debug('cache: [get] ' + options.key);

		var key = prefixData + options.key;

		return redis('get', [key]).then(function (json) {

			var data = null;
			try {
				data = JSON.parse(json);
			} catch (e) {
				log.error('cache: decoding redis(get) ' + options.key, { json: json });
			}

			return data;
		});
	}

	/**
	 * Get many values from cache with the given keys. Maintains order.
	 *
	 * @param {string[]} options.keys The keys to get values for.
	 *
	 * @returns {Promise} A promise that resolves to an array of values or `null`
	 *   in the same order as the keys given.
	 */
	function cacheGetMany(options) {

		if (!options.keys || options.keys.length) {
			return Promise.reject('must provide "keys" to get');
		}

		var keys    = [];
		var keysLen = options.keys.length;
		for (var i = 0; i < keysLen; i++) {
			keys.push(prefixData + options.keys[i]);
		}

		log.debug('cache: [getMany] ' + keysLen + ' keys');

		return redis('mget', keys).then(function (jsonArray) {

			if (jsonArray && jsonArray.length !== keysLen) {
				var e = new Error('cache: redis(mget) returned ' + jsonArray.length + ' replies, expected ' + keys.length);
				log.error(e.message, { keys: keys });
				throw e;
			}

			var dataArray = [];
			for (var i = 0; i < keysLen; i++) {
				var json = jsonArray[i];
				try {
					dataArray.push(JSON.parse(json));
				} catch (e) {
					log.error('cache: decoding redis(mget) ' + options.keys[i], { json: json });
					dataArray.push(null);
				}
			}

			return dataArray;
		});
	}

	/**
	 * Set the given key to the given data with the given timeout in milliseconds.
	 *
	 * @param {string} options.key    The key for the set.
	 * @param {*}      options.data   The data to set.
	 * @param {number} options.millis The TTL for the set (in millis).
	 *
	 * @returns {Promise} A promise that resolves to an object containing details of the set operation:
	 *
	 * {
	 *   cacheSet            : // if the set was successful
	 *   clearAssocIn        : // if the associated key clear was successful
	 *   assocIn             : // how many associations were tied to the "in" set
	 *   assocContains       : // how many "contains" sets this key was added to
	 *   expireAssocIn       : // whether the "in" set TTL was set
	 *   expireAssocContains : // whether the "contains" sets TTLs were set
	 * }
	 *
	 */
	function cacheSet(options) {

		if (!options.key) {
			return Promise.reject(new Error('must provide a "key" to set'));
		}
		if ('undefined' === options.data) {
			return Promise.reject(new Error('must provide "data" to set'));
		}
		if (!options.millis) {
			return Promise.reject(new Error('must provide a "millis" TTL'));
		}

		var key      = options.key;
		var millis   = options.millis;
		var assoc    = options.associations;
		var assocLen = assoc ? assoc.length : 0;
		var keyData  = prefixData + key;
		var keyIn    = prefixIn   + key;

		log.debug('cache: [set] ' + key + ' for ' + millis + ' millis');

		// Set the data for the key (encoded as JSON).
		var json = JSON.stringify(options.data);
		var asyncCacheSet = redis('set', [keyData, json, 'PX', millis]);

		// Clear any existing associations.
		var asyncClearAssocIn = redis('del', [keyIn]);

		var asyncAssociateIn;
		var asyncExpireAssociateIn;
		var asyncAssociateContains;
		var asyncExpireAssociateContains;

		// Make associations.
		if (assocLen) {

			// The given associated keys are *in* this key.
			var argsAssocIn =[keyIn].concat(assoc);
			asyncAssociateIn = redis('sadd', argsAssocIn);
			asyncExpireAssociateIn = redis('pexpire', [keyIn, millis]);

			// This key *contains* the given associated keys.
			var waitingAssociateContains = [];
			var argsSetExpire = [];
			for (var i = 0; i < assocLen; i++) {
				var assocKey         = assoc[i];
				var assocKeyContains = prefixContains + assocKey;
				waitingAssociateContains.push(redis('sadd', [assocKeyContains, key]));
				argsSetExpire.push(assocKeyContains);
			}
			asyncAssociateContains = Promise.all(waitingAssociateContains);

			// Set the TTL for all contains sets based on their contained keys' TTLs.
			argsSetExpire.push(prefixData);
			asyncExpireAssociateContains = redis('pexpiresmax', argsSetExpire);

		} else {

			asyncAssociateIn             = Promise.resolve();
			asyncExpireAssociateIn       = Promise.resolve();
			asyncAssociateContains       = Promise.resolve();
			asyncExpireAssociateContains = Promise.resolve();

		}

		return Promise.props({
			cacheSet            : asyncCacheSet,
			clearAssocIn        : asyncClearAssocIn,
			assocIn             : asyncAssociateIn,
			assocContains       : asyncAssociateContains,
			expireAssocIn       : asyncExpireAssociateIn,
			expireAssocContains : asyncExpireAssociateContains
		});
	}

	/**
	 * Clear the given keys in cache and the given amount of association levels.
	 *
	 * @param {string[]}      options.keys   The keys to clear
	 * @param {number|string} options.levels The association levels to clear. Defaults to "all".
	 *   Can be a positive integer, or a string ("none" or "all).
	 *
	 * @returns {Promise} A promise that resolves to information about the clear.
	 *
	 * {
	 *   level               : // this level number (starts at 1)
	 *   keys                : // keys to be cleared at this level
	 *   keysCount           : // number of keys to be cleared at this level
	 *   cleared             : // number of data keys cleared
	 *   keysContains        : // keys ("contains" sets) modified
	 *   removedFromContains : // keys removed from "contains" sets
	 *   keysInDeleted       : // keys ("in" sets) deleted
	 *   keysNextLevel       : // the keys that will be cleared on the next level
	 *   nextLevel           : // an object similar to this one, containing information about the next level clear
	 * }
	 *
	 */
	function cacheClear(options) {

		if (!options.keys || options.keys.length) {
			return Promise.reject('must provide "keys" to clear');
		}

		var keysSeen = {};
		var maxLevel = maxClearLevels;

		if (options.levels === 'none') {
			maxLevel = 0;
		} else if ('number' === typeof options.levels) {
			maxLevel = Math.max(0, Math.floor(+options.levels));
		}

		// Clear the cache then attach a property of all cache keys cleared.
		return cacheClearLevel(options.keys, 1).then(function (info) {
			info.allKeysCleared = Object.keys(keysSeen);
			return info;
		});

		function cacheClearLevel(keys, level) {

			// Build a unique set of keys for "data", "in", and "contains" key sets.
			var keysStrings  = [];
			var keysData     = [];
			var keysIn       = [];
			var keysContains = [];
			for (var i = 0; i < keys.length; i++) {
				var key = keys[i];
				if (!keysSeen[key]) {
					keysSeen[key] = true;
					keysStrings.push('' + key);
					keysData.push(prefixData + key);
					keysIn.push(prefixIn + key);
					keysContains.push(prefixContains + key);
				}
			}

			log.debug('cache: [clear] (level ' + level + ') ' + keysData.length + ' keys');

			// Delete the data from cache.
			var asyncClear = redis('del', keysData);

			// Get all of the *contains* keys for the associations from the *in* keys.
			var asyncGetContainsKeys = redis('sunion', keysIn);

			// Delete all of the *in* keys for the associations.
			var asyncDeleteInSets = redis('del', keysIn);

			// Remove the keys that were cleared from the *contains* keys.
			var asyncRemoveContains = asyncGetContainsKeys.then(function (keysAllContains) {
				return Promise.all((keysAllContains || []).map(function (key) {
					return redis('srem', [prefixContains + key].concat(keys));
				})).then(function (results) {
					return (results && results.length) ? results.reduce(function (a, b) { return a + b; }) : 0;
				});
			});

			var asyncKeysNextLevel;
			var asyncClearNextLevel;

			if (level > maxLevel) {

				asyncKeysNextLevel  = Promise.resolve([]);
				asyncClearNextLevel = Promise.resolve();

			} else {

				// Get the next level of keys to clear.
				asyncKeysNextLevel = redis('sunion', keysContains);

				// Clear the next level of keys.
				asyncClearNextLevel = asyncKeysNextLevel.then(function (keysNextLevel) {
					return keysNextLevel.length ? cacheClearLevel(keysNextLevel, level + 1) : Promise.resolve();
				});

			}

			return Promise.props({
				level               : level,
				keys                : keys,
				keysCount           : keys.length,
				cleared             : asyncClear,
				keysContains        : asyncGetContainsKeys,
				removedFromContains : asyncRemoveContains,
				keysInDeleted       : asyncDeleteInSets,
				keysNextLevel       : asyncKeysNextLevel,
				nextLevel           : asyncClearNextLevel
			});
		}
	}

	/**
	 * Add the given keys to a de-dupe queue for clearing later.
	 * These keys will be fully cleared.
	 *
	 * @param {string[]} options.keys The keys to clear later.
	 *
	 * @returns {Promise} A promise that resolves to the number of keys added.
	 */
	function cacheClearLater(options) {

		if (!options.keys || options.keys.length) {
			return Promise.reject('must provide "keys" to clear later');
		}

		log.debug('cache: [clearLater] ' + options.keys.length + ' keys');

		// Add all of the given keys to a clear later set.
		var argsSetAdd = [clearLaterSet].concat(options.keys);
		return redis('sadd', argsSetAdd);
	}

	/**
	 * Clear all keys in the "clear now" set. This will clear in batches using the "smpop" script.
	 *
	 * @returns {Promise} A promise that resolves to an array of arrays containing the keys cleared per iteration.
	 */
	function cacheClearNow() {

		// Clear all of the keys from the given clear later set.
		var keysClearedByIteration = [];

		return cacheClearNowIteration();

		// Pop a few keys from the set and clear them.
		// Once they've been cleared, continue to pop keys and clear until the set has been emptied.
		function cacheClearNowIteration() {

			return redis('smpop', [clearNowSet, clearsPerIteration]).then(function (keysToClear) {
				if (keysToClear && keysToClear.length) {

					log.debug('cache: [clearNow] iteration ' + keysToClear.length + ' keys');

					return cacheClear({ keys: keysToClear }).then(function (clearInfo) {

						if (clearInfo && clearInfo.allKeysCleared) {
							keysClearedByIteration.push(clearInfo.allKeysCleared);
						}
						if (keysToClear.length !== clearsPerIteration) {
							return keysClearedByIteration;
						}

						return cacheClearNowIteration().then(function () {
							return keysClearedByIteration;
						});
					});
				}
			});
		}
	}

	return {
		get        : cacheGet,
		getMany    : cacheGetMany,
		set        : cacheSet,
		clear      : cacheClear,
		clearLater : cacheClearLater,
		clearNow   : cacheClearNow
	};
};
<|endoftext|>


<commit_before>/*
   Cache
   Copyright (C) 2010 - 2013 Glenn Jones. All Rights Reserved.
   MIT License: https://raw.github.com/glennjones/microformat-node/master/license.txt

   */
'use strict';

var cacheItemLimit = 1000,
    cacheTimeLimit = 3600000,  // 3600000 = 1hr,
    logger = {},
    cache  = [];

function setCacheLimits (newCacheTimeLimit, newCacheItemLimit) {
  cacheTimeLimit = newCacheTimeLimit;
  cacheItemLimit = newCacheItemLimit;
}

function get (url) {
  var i = cache.length,
      x = 0;

  while (x < i) {
    if (url === cache[x].url) {
      return cache[x].data;
    }
    x++;
  }

  return undefined;
}

function has (url) {
  return (get(url) === undefined) ? false : true;
}

function fetch (url, callback) {
  callback(null, get(url));
}

function set (url, data) {
  if (cache.length >= cacheItemLimit) {
    cache.pop();
  }
  return cache.unshift( {
    url : url,
    time : new Date().getTime(),
    data : data
  });
}

function checkLimits () {
  var time = new Date().getTime(),
      i,
      x =0,
      out =[];

  i = cache.length;
  while (x < i) {
    if ((time - cache[x].time) < cacheTimeLimit) {
      out.unshift(cache[x]);
    }
    x++;
  }
  cache = out;

  setTimeout(checkLimits, 10000);
}

checkLimits();

exports.get = get;
exports.has = has;
exports.set = set;
exports.fetch = fetch;
exports.setCacheLimits = setCacheLimits;

<commit_msg>Fix for - Issue #8 - cache no longer starts on require<commit_after>/*
   Cache
   Copyright (C) 2010 - 2013 Glenn Jones. All Rights Reserved.
   MIT License: https://raw.github.com/glennjones/microformat-node/master/license.txt

   */
'use strict';

var cacheItemLimit = 1000,
    cacheTimeLimit = 3600000,  // 3600000 = 1hr,
    active = false,
    logger = {},
    cache  = [];

function setCacheLimits (newCacheTimeLimit, newCacheItemLimit) {
  cacheTimeLimit = newCacheTimeLimit;
  cacheItemLimit = newCacheItemLimit;
}

function get (url) {
  var i = cache.length,
      x = 0;

  while (x < i) {
    if (url === cache[x].url) {
      return cache[x].data;
    }
    x++;
  }

  return undefined;
}

function has (url) {
  return (get(url) === undefined) ? false : true;
}

function fetch (url, callback) {
  callback(null, get(url));
}

function set (url, data) {
  // only start cache checks if something is added
  if (!active) {
      active = true;
      checkLimits();
  }

  // check we have not gone over the number items limit
  if (cache.length >= cacheItemLimit) {
    cache.pop();
  }
  return cache.unshift( {
    url : url,
    time : new Date().getTime(),
    data : data
  });
}

function checkLimits () {
  var time = new Date().getTime(),
      i,
      x =0,
      out =[];

  i = cache.length;
  while (x < i) {
    if ((time - cache[x].time) < cacheTimeLimit) {
      out.unshift(cache[x]);
    }
    x++;
  }
  cache = out;

  // close down cache checks once it empty
  if(cache.length > 0){
    setTimeout(checkLimits, 10000);
  }else{
    active = false;
  }
}

exports.get = get;
exports.has = has;
exports.set = set;
exports.fetch = fetch;
exports.setCacheLimits = setCacheLimits;

<|endoftext|>


<commit_before>var conf = require('./config')
  , webapp = require('./webserver').webapp
  , webserver = require('./webserver').webserver
  , sockethandler = require('./socket')
  , io = require('socket.io')
  , Layout = require('./layout').Layout
  , Template = require('./layout').Template;

process.on('uncaughtException', function(err) {
  console.trace('Caught exception: ' + err);
});

var layout = new Layout('default');

var Ciana = function() {
  this.webapp = webapp;
  this.webserver = webserver;
}

Ciana.prototype.start = function start() {
  var self = this
    , connections = {}
    , providers = conf.get('providers');

  self.io = io.listen(webserver);
  self.io.sockets.on('connection', function(socket) {
    socket.emit('templates', layout.templates);
    sockethandler(socket, connections);
  });
  layout.watch(function(file) {
    if (file === layout.path_server + '/index.html' ||
        file.match(/.*\.(css|less)/) !== null) {
      self.io.sockets.emit('reload');
    } else {
      console.log(file);
      var template = file.match(/panels\/(.*?)\.html/);
      if (template !== null) {
        layout.templates[template[1]] = new Template(layout, template[1]);
        var emit = {};
        emit[template[1]] = layout.templates[template[1]];
        self.io.sockets.emit('templates', emit);
      }
    }
  });

  /*
  self.collectors = [];
  Object.keys(providers).forEach(function(provider_name) {
    var provider = providers[provider_name];
    switch (provider.type) {
      case 'collector':
        provider.next_run = provider.frequency;
        provider.name = provider_name;
        if (typeof provider.function === 'undefined') {
          provider.function = 'get';
        }
        var plugin = require('../plugins/collectors/' + provider.plugin);
        provider.plugin_module = new plugin();
        self.collectors.push(provider);
        break;

      default:
        console.log('Not sure what to do with provider type %s:\n', provider.type, provider);
    }
  });

  self.collector_timer = setInterval(function(ciana) {
    ciana.collectors.forEach(function(collector) {
      collector.next_run--;
      if (collector.next_run <= 0) {
        collector.next_run = collector.frequency;
        console.log('Running collector %s now.', collector.name);
        self.io.sockets.emit('provider_update', {
          name: collector.name,
          data: collector.plugin_module[collector.function].call()
        });
      }
      console.log('Running collector %s in another %ds', collector.name, collector.next_run);
    });
    console.log("\n\n");
  }, 1000, self);
  */

  console.log('Ciana started on port ' + conf.get('port'));
}

module.exports = Ciana;
<commit_msg>Reloading browser on public/(scripts|libraries) changes<commit_after>var conf = require('./config')
  , webapp = require('./webserver').webapp
  , webserver = require('./webserver').webserver
  , sockethandler = require('./socket')
  , io = require('socket.io')
  , Layout = require('./layout').Layout
  , Template = require('./layout').Template
  , chokidar = require('chokidar');

process.on('uncaughtException', function(err) {
  console.trace('Caught exception: ' + err);
});

var layout = new Layout('default');

var Ciana = function() {
  this.webapp = webapp;
  this.webserver = webserver;
}

Ciana.prototype.start = function start() {
  var self = this
    , connections = {}
    , providers = conf.get('providers');

  self.io = io.listen(webserver);
  self.io.sockets.on('connection', function(socket) {
    socket.emit('templates', layout.templates);
    sockethandler(socket, connections);
  });
  layout.watch(function(file) {
    if (file === layout.path_server + '/index.html' ||
        file.match(/.*\.(css|less)/) !== null) {
      self.io.sockets.emit('reload');
    } else {
      console.log(file);
      var template = file.match(/panels\/(.*?)\.html/);
      if (template !== null) {
        layout.templates[template[1]] = new Template(layout, template[1]);
        var emit = {};
        emit[template[1]] = layout.templates[template[1]];
        self.io.sockets.emit('templates', emit);
      }
    }
  });

  var script_reloader = function() {
    self.io.sockets.emit('reload');
  };
  var watcher = chokidar.watch(
    [conf.get('app_path') + '/public/scripts', conf.get('app_path') + '/public/libraries'],
    { ignoreInitial: true, ignored: /\/\./ });
  watcher.on('add', script_reloader).on('change', script_reloader).on('unlink', script_reloader);

  self.collectors = [];
  Object.keys(providers).forEach(function(provider_name) {
    var provider = providers[provider_name];
    switch (provider.type) {
      case 'collector':
        provider.next_run = provider.frequency;
        provider.name = provider_name;
        if (typeof provider.function === 'undefined') {
          provider.function = 'get';
        }
        var plugin = require('../plugins/collectors/' + provider.plugin);
        provider.plugin_module = new plugin();
        self.collectors.push(provider);
        break;

      default:
        console.log('Not sure what to do with provider type %s:\n', provider.type, provider);
    }
  });

  self.collector_timer = setInterval(function(ciana) {
    ciana.collectors.forEach(function(collector) {
      collector.next_run--;
      if (collector.next_run <= 0) {
        collector.next_run = collector.frequency;
        console.log('Running collector %s now.', collector.name);
        self.io.sockets.emit('provider_update', {
          name: collector.name,
          data: collector.plugin_module[collector.function].call()
        });
      }
      console.log('Running collector %s in another %ds', collector.name, collector.next_run);
    });
    console.log("\n\n");
  }, 1000, self);

  console.log('Ciana started on port ' + conf.get('port'));
}

module.exports = Ciana;
<|endoftext|>


<commit_before>var createKey = require('../../private-parts').createKey;

/**
 * Store the associations between constructors and their classes.
 */
var constructorToClassMap = new WeakMap();

/**
 * Store the associations between protected prototypes and their public
 * prototype counterparts.
 */
var protectToPrototypeMap = new WeakMap();

/**
 * A Class.
 * @constructor
 * @param {string} name An optional function name given to the produced
 *   constructor. This can be useful for debugging purposes.
 * @param {Function} definition An optional function used to define the new
 *   constructor.
 * @param {Class} parent An optional parent Class instance to extend from.
 *   This parameter is only used internally.
 */
function Class(name, definition, parent) {

  // Argument shifting.
  if (typeof name == 'function') {
    parent = definition;
    definition = name;
    name = null;
  }
  this.definition = definition;
  this.parent = parent;

  // Create the constructor, add some methods to it, and store the association.
  this.Ctor = createConstructor(name);
  this.Ctor.subclass = subclass;
  this.Ctor.final = final;
  constructorToClassMap.set(this.Ctor, this);

  this._setupInheritance();
  this._storeSecrets();
  this._makeAccessors();
}

/**
 * Setup the protected and private keys and method objects. Then invoke the
 * definition with those values. Finally, return the constructor publicly.
 * @return {Function} The generated constructor.
 */
Class.prototype.construct = function() {
  if (typeof this.definition == 'function') {
    this.definition.call(
      this.Ctor,
      this.Ctor.prototype,
      this.protectedKey,
      this.protectedMethods,
      this.privateKey,
      this.privateMethods
    );
  }
  return this.Ctor;
};

/**
 * Link this class to its parent and this class's constructor to its
 * parent class's constuctor.
 */
Class.prototype._setupInheritance = function() {
  if (this.parent) {
    // Add a node-style `super_` property. This is different from above
    // but used in case existing node code expects it to be there.
    this.Ctor.super_ = this.parent.Ctor;

    // Set up the prototype chain. Store a reference the the parent constructor
    // on the prototype for easier access than the node-style `Ctor.super_`.
    this.Ctor.prototype = Object.create(this.parent.Ctor.prototype, {
      constructor: { value: this.Ctor },
      super: { value: this.parent.Ctor.prototype }
    });
  }
};

/**
 * Create the private and protect keys and methods objects.
 */
Class.prototype._storeSecrets = function() {
  // Setup the protected key and method object.
  if (this.parent) {
    this.protectedKey = this.parent.protectedKey;
    this.protectedMethods = Object.create(this.parent.protectedMethods, {
      super: { value: this.parent.protectedMethods }
    });
  } else {
    this.protectedMethods = {};
    this.protectedKey = createKey(protectedFactory);
  }
  // Associate these class's protected methods with its public prototype.
  protectToPrototypeMap.set(this.Ctor.prototype, this.protectedMethods);

  // Set up the private key and method object.
  this.privateMethods = {};
  this.privateKey = createKey(this.privateMethods);
};

/**
 * Add getter, setter, and accessor functions to the constructor prototype.
 */
Class.prototype._makeAccessors = function() {
  var proto = this.Ctor.prototype;
  var _ = this.protectedKey;

  proto.addGetters = function(props) {
    if (!Array.isArray(props)) props = [].slice.call(arguments);
    props.forEach(function(prop) {
      proto['get' + capitalize(prop)] = function() {
        return _(this)[prop];
      };
    });
  };

  proto.addSetters = function(props) {
    if (!Array.isArray(props)) props = [].slice.call(arguments);
    props.forEach(function(prop) {
      proto['set' + capitalize(prop)] = function(value) {
        _(this)[prop] = value;
      };
    });
  };

  proto.addAccessors = function() {
    proto.addGetters.apply(proto, arguments);
    proto.addSetters.apply(proto, arguments);
  };
};

/**
 * Subclass a constructor.
 * Note: This function gets assigned to new constructor instances, so when
 * it is invoked, its `this` context will be the calling constructor. When
 * subclassing, that means `this` is the parent constructor.
 * @param {Function} definition Any function that could be passed to the Class
 *   constructor.
 * @return {Function} A new constuctor that is a subclass of the invoking
 *   consturctor.
 */
function subclass(name, definition) {
  // Argument shifting.
  if (typeof name == 'function') {
    definition = name;
    name = null;
  }
  var parent = constructorToClassMap.get(this);
  if (parent.final) {
    throw new Error('Cannot subclass constructors marked final.');
  }

  return new Class(name, definition, parent).construct();
}

/**
 * Prevent a constructor from being subclassed.
 */
function final() {
  var cls = constructorToClassMap.get(this);
  cls.final = true;
}

/**
 * A creator function that can be passed to the Private Parts `createKey`
 * method. If accepts an object and return a new object whose prototype is
 * the protected counterpart to the prototype of the passed object.
 * @param {Object} instance An instance of a constructor created with Class.
 * @return {Object} The protected methods object.
 */
function protectedFactory(instance) {
  var publicPrototype = Object.getPrototypeOf(instance);
  var protectedPrototype = protectToPrototypeMap.get(publicPrototype);
  if (!protectedPrototype) {
    throw new Error('The protected key function only accepts instances '
      + 'of objects created using Mozart constructors.'
    );
  }
  return Object.create(protectedPrototype);
}

/**
 * Create a constructor function with an optional name (the name makes it
 * easier to debug in the developer tools). The returned constructor, when
 * called, will attempt to invoke an `init` function (if it exists) with any
 * arguments passed to it.
 * @param {string} name The optional name of the constructor.
 * @return {Function} The newly created constructor function.
 */
function createConstructor(name) {
  if (!name) name = '';
  var factory = new Function('return function ' + name + '() {\n'
    + 'if (typeof this.init == \'function\')'
    + 'this.init.apply(this, arguments) }');
  return factory();
}

function capitalize(string) {
  return string.substr(0, 1).toUpperCase() + string.substr(1);
}

module.exports = Class;
<commit_msg>Convert the dynamic accessor functions to be non-enumerable.<commit_after>var createKey = require('../../private-parts').createKey;

/**
 * Store the associations between constructors and their classes.
 */
var constructorToClassMap = new WeakMap();

/**
 * Store the associations between protected prototypes and their public
 * prototype counterparts.
 */
var protectToPrototypeMap = new WeakMap();

/**
 * A Class.
 * @constructor
 * @param {string} name An optional function name given to the produced
 *   constructor. This can be useful for debugging purposes.
 * @param {Function} definition An optional function used to define the new
 *   constructor.
 * @param {Class} parent An optional parent Class instance to extend from.
 *   This parameter is only used internally.
 */
function Class(name, definition, parent) {

  // Argument shifting.
  if (typeof name == 'function') {
    parent = definition;
    definition = name;
    name = null;
  }
  this.definition = definition;
  this.parent = parent;

  // Create the constructor, add some methods to it, and store the association.
  this.Ctor = createConstructor(name);
  this.Ctor.subclass = subclass;
  this.Ctor.final = final;
  constructorToClassMap.set(this.Ctor, this);

  this._setupInheritance();
  this._storeSecrets();
  this._makeAccessors();
}

/**
 * Setup the protected and private keys and method objects. Then invoke the
 * definition with those values. Finally, return the constructor publicly.
 * @return {Function} The generated constructor.
 */
Class.prototype.construct = function() {
  if (typeof this.definition == 'function') {
    this.definition.call(
      this.Ctor,
      this.Ctor.prototype,
      this.protectedKey,
      this.protectedMethods,
      this.privateKey,
      this.privateMethods
    );
  }
  return this.Ctor;
};

/**
 * Link this class to its parent and this class's constructor to its
 * parent class's constuctor.
 */
Class.prototype._setupInheritance = function() {
  if (this.parent) {
    // Add a node-style `super_` property. This is different from above
    // but used in case existing node code expects it to be there.
    this.Ctor.super_ = this.parent.Ctor;

    // Set up the prototype chain. Store a reference the the parent constructor
    // on the prototype for easier access than the node-style `Ctor.super_`.
    this.Ctor.prototype = Object.create(this.parent.Ctor.prototype, {
      constructor: { value: this.Ctor },
      super: { value: this.parent.Ctor.prototype }
    });
  }
};

/**
 * Create the private and protect keys and methods objects.
 */
Class.prototype._storeSecrets = function() {
  // Setup the protected key and method object.
  if (this.parent) {
    this.protectedKey = this.parent.protectedKey;
    this.protectedMethods = Object.create(this.parent.protectedMethods, {
      super: { value: this.parent.protectedMethods }
    });
  } else {
    this.protectedMethods = {};
    this.protectedKey = createKey(protectedFactory);
  }
  // Associate these class's protected methods with its public prototype.
  protectToPrototypeMap.set(this.Ctor.prototype, this.protectedMethods);

  // Set up the private key and method object.
  this.privateMethods = {};
  this.privateKey = createKey(this.privateMethods);
};

/**
 * Add getter, setter, and accessor functions to the constructor prototype.
 */
Class.prototype._makeAccessors = function() {
  var proto = this.Ctor.prototype;
  var _ = this.protectedKey;

  Object.defineProperties(proto, {
    addGetters: {
      value: function(props) {
        if (!Array.isArray(props)) props = [].slice.call(arguments);
        props.forEach(function(prop) {
          proto['get' + capitalize(prop)] = function() {
            return _(this)[prop];
          };
        });
      }
    },
    addSetters: {
      value: function(props) {
        if (!Array.isArray(props)) props = [].slice.call(arguments);
        props.forEach(function(prop) {
          proto['set' + capitalize(prop)] = function(value) {
            _(this)[prop] = value;
          };
        });
      }
    },
    addAccessors: {
      value: function() {
        proto.addGetters.apply(proto, arguments);
        proto.addSetters.apply(proto, arguments);
      }
    }
  });

};

/**
 * Subclass a constructor.
 * Note: This function gets assigned to new constructor instances, so when
 * it is invoked, its `this` context will be the calling constructor. When
 * subclassing, that means `this` is the parent constructor.
 * @param {Function} definition Any function that could be passed to the Class
 *   constructor.
 * @return {Function} A new constuctor that is a subclass of the invoking
 *   consturctor.
 */
function subclass(name, definition) {
  // Argument shifting.
  if (typeof name == 'function') {
    definition = name;
    name = null;
  }
  var parent = constructorToClassMap.get(this);
  if (parent.final) {
    throw new Error('Cannot subclass constructors marked final.');
  }

  return new Class(name, definition, parent).construct();
}

/**
 * Prevent a constructor from being subclassed.
 */
function final() {
  var cls = constructorToClassMap.get(this);
  cls.final = true;
}

/**
 * A creator function that can be passed to the Private Parts `createKey`
 * method. If accepts an object and return a new object whose prototype is
 * the protected counterpart to the prototype of the passed object.
 * @param {Object} instance An instance of a constructor created with Class.
 * @return {Object} The protected methods object.
 */
function protectedFactory(instance) {
  var publicPrototype = Object.getPrototypeOf(instance);
  var protectedPrototype = protectToPrototypeMap.get(publicPrototype);
  if (!protectedPrototype) {
    throw new Error('The protected key function only accepts instances '
      + 'of objects created using Mozart constructors.'
    );
  }
  return Object.create(protectedPrototype);
}

/**
 * Create a constructor function with an optional name (the name makes it
 * easier to debug in the developer tools). The returned constructor, when
 * called, will attempt to invoke an `init` function (if it exists) with any
 * arguments passed to it.
 * @param {string} name The optional name of the constructor.
 * @return {Function} The newly created constructor function.
 */
function createConstructor(name) {
  if (!name) name = '';
  var factory = new Function('return function ' + name + '() {\n'
    + 'if (typeof this.init == \'function\')'
    + 'this.init.apply(this, arguments) }');
  return factory();
}

function capitalize(string) {
  return string.substr(0, 1).toUpperCase() + string.substr(1);
}

module.exports = Class;
<|endoftext|>


<commit_before>(function(){
  var initializing = false;

  Function.mashin = function(props) {
    var prop;
    // add passed in class attributes
    for(prop in props) {
      // don't copy intrinsic properties
      if ( prop.match(/prototype|arguments|__proto__/) ) { return; }

      if ( this[prop] && (typeof props[prop] == 'function' ) &&
           /\b_super\b/.test(props[prop]) ) {
        this[prop] = (function(name, func, _super){
          return function() {
            // store this in case we are calling through multiple methods
            // that use super, that way each is restored after super is used
            var exSuper = this._super;
            this._super = _super;
            var returned = func.apply(this, arguments);
            // and the restoration here!
            this._super = exSuper;
            return returned;
          };
        })(prop, props[prop], this[prop]);
      } else {
        this[prop] = props[prop];
      }
    }
    return this;
  };

  Function.mixin = function(props) {
    var proto = this.prototype;
    // add passed in instance attributes to prototype
    for(var prop in props) {
      if ( proto[prop] && (typeof props[prop] == 'function' ) &&
            /\b_super\b/.test(props[prop]) ) {
        // there is already an attr with that name the superclass
        // and the method in question is looking for _super ...
        // so, add _super reference
        proto[prop] = (function(name, func, _super) {
          return function() {
            // store this in case we are calling through multiple methods
            // that use super, that way each is restored after super is used
            var exSuper = this._super;
            this._super = _super;
            var returned = func.apply(this, arguments);
            // and the restoration here!
            this._super = exSuper;
            return returned;
          }
        })(prop, props[prop], proto[prop]);
      } else {
        proto[prop] = props[prop];
      }
    }
    return this;
  };

  Function.subclass = function(iProps, cProps) {
    var proto;

    initializing = true;
    proto = new this();
    initializing = false;

    // add existing class method from old class
    for( prop in this ) {
      var prop;
      if ( prop == 'prototype' ) { return; }
      Subclass[prop] = this[prop];
    }

    if ( iProps ) {
      // add passed in instance attributes to prototype
      for(var iProp in iProps) {
        if ( proto[iProp] && (typeof iProps[iProp] == 'function' ) &&
              /\b_super\b/.test(iProps[iProp]) ) {
          // there is already an attr with that name the superclass
          // and the method in question is looking for _super ...
          // so, add _super reference
          proto[iProp] = (function(name, func, _super) {
            return function() {
              // store this in case we are calling through multiple methods
              // that use super, that way each is restored after super is used
              var exSuper = this._super;
              this._super = _super;
              var returned = func.apply(this, arguments);
              // and the restoration here!
              this._super = exSuper;
              return returned;
            }
          })(iProp, iProps[iProp], proto[iProp]);
        } else {
          proto[iProp] = iProps[iProp];
        }
      }
    }

    if ( cProps ) {
      var cProp;
      // add passed in class attributes
      for(cProp in cProps) {
        // don't copy intrinsic properties
        if ( cProp.match(/prototype|arguments|__proto__/) ) { return; }

        if ( this[cProp] && (typeof cProps[cProp] == 'function' ) &&
             /\b_super\b/.test(cProps[cProp]) ) {
          Subclass[cProp] = (function(name, func, _super){
            return function() {
              // store this in case we are calling through multiple methods
              // that use super, that way each is restored after super is used
              var exSuper = this._super;
              this._super = _super;
              var returned = func.apply(this, arguments);
              // and the restoration here!
              this._super = exSuper;
              return returned;
            };
          })(cProp, cProps[cProp], this[cProp]);
        } else {
          Subclass[cProp] = cProps[cProp];
        }
      }
    }

    // high level constructor
    function Subclass() {
      if (!initializing && this.initialize) {
        this.initialize.apply(this, arguments);
      }
    }

    Subclass.prototype = proto;
    Subclass.superclass = this;
    Subclass.prototype.constructor = Subclass;
    return Subclass
  };

  // export the Class variable
  this.Class = Function.subclass();
})();

<commit_msg>getting the subclass to use mixin/mashin methods<commit_after>(function(){
  var initializing = false;

  Function.mashin = function(props) {
    var prop;
    // add passed in class attributes
    for(prop in props) {
      // don't copy intrinsic properties
      if ( prop.match(/prototype|arguments|__proto__/) ) { return; }

      if ( this[prop] && (typeof props[prop] == 'function' ) &&
           /\b_super\b/.test(props[prop]) ) {
        this[prop] = (function(name, func, _super){
          return function() {
            // store this in case we are calling through multiple methods
            // that use super, that way each is restored after super is used
            var exSuper = this._super;
            this._super = _super;
            var returned = func.apply(this, arguments);
            // and the restoration here!
            this._super = exSuper;
            return returned;
          };
        })(prop, props[prop], this[prop]);
      } else {
        this[prop] = props[prop];
      }
    }
  };

  Function.mixin = function(props, proto) {
    proto = proto || this.prototype;
    // add passed in instance attributes to prototype
    for(var prop in props) {
      if ( proto[prop] && (typeof props[prop] == 'function' ) &&
            /\b_super\b/.test(props[prop]) ) {
        // there is already an attr with that name the superclass
        // and the method in question is looking for _super ...
        // so, add _super reference
        proto[prop] = (function(name, func, _super) {
          return function() {
            // store this in case we are calling through multiple methods
            // that use super, that way each is restored after super is used
            var exSuper = this._super;
            this._super = _super;
            var returned = func.apply(this, arguments);
            // and the restoration here!
            this._super = exSuper;
            return returned;
          }
        })(prop, props[prop], proto[prop]);
      } else {
        proto[prop] = props[prop];
      }
    }
    return proto;
  };

  Function.subclass = function(iProps, cProps) {
    var proto;

    initializing = true;
    proto = new this();
    initializing = false;

    // high level constructor
    function Subclass() {
      if (!initializing && this.initialize) {
        this.initialize.apply(this, arguments);
      }
    }

    // add existing class method from old class
    for( prop in this ) {
      var prop;
      if ( prop == 'prototype' ) { return; }
      Subclass[prop] = this[prop];
    }

    Subclass.mashin(cProps);
    Subclass.prototype = Subclass.mixin(iProps, proto);
    Subclass.superclass = this;
    Subclass.prototype.constructor = Subclass;
    return Subclass
  };

  // export the Class variable
  this.Class = Function.subclass();
})();

<|endoftext|>


<commit_before>"use strict";

var connect = require("connect"),
    httpProxy = require("http-proxy"),
    configuration = require("./configuration"),
    NoProviderFound = require("./errors/no-provider-found.js");

/**
 * Clyde constructor
 *
 * @constructor
 * @param {Object} options Configuration options
 */
function Clyde(options) {
  // Options
  this.options = options;

  // Initialize log with options
  this.log = require("./log")(this.options);

  // Proxy instance to be used to route request to providers.
  this.proxy = httpProxy.createProxyServer();

  // Create root middleware
  this.root = connect();
}

/**
 * Builds and returns a middleware that runs the specified configuration.
 *
 * @public
 * @returns {Function} Middleware function
 */
Clyde.prototype.middleware = function() {

  // Initialize log with options
  var log = this.log;

  // Load configuration middlewares (filters and providers)
  var middlewares = configuration.load(this.options);

  //
  // Add prefilters middlewares
  //
  middlewares.prefilters.forEach(function(filter) {
    // Log before
    this.root.use(function(req, res, next) {
      log.debug({filter: filter.id}, "Before apply prefilter...");
      next();
    });
    // Filter middleware
    this.root.use(filter.middleware);
    // Log after
    this.root.use(function(req, res, next) {
      log.debug({filter: filter.id}, "After apply prefilter...");
      next();
    });
  }, this);

  //
  // Add providers and its filters middlewares
  //
  var index,
      num = middlewares.providers.length,
      providers = middlewares.providers;
  for (index = 0; index < num; index++) {
    this.registerProviderMiddleware(providers[index]);
  }

  //
  // Check if request has been processed by a provider and throw 404 error
  //
  this.root.use(function(req, res, next) {
    if (!req.provider) {
      next(new NoProviderFound());
    }
    next();
  });

  //
  // Add postfilters middlewares
  //
  middlewares.postfilters.forEach(function(filter) {
    // Log before
    this.root.use(function(req, res, next) {
      log.debug({filter: filter.id}, "Before apply postfilter...");
      next();
    });
    // Filter middleware
    this.root.use(filter.middleware);
    // Log after
    this.root.use(function(req, res, next) {
      log.debug({filter: filter.id}, "After apply postfilter...");
      next();
    });
  }, this);

  // Attach error handler
  this.root.use(this.errorHandler());

  return this.root;
};

/**
 * Add provider to the chain of application middlewares.
 *
 * @private
 * @param  {object} provider Provider configuration
 * @returns {void}
 */
Clyde.prototype.registerProviderMiddleware = function(provider) {

  var log = this.log,
      proxy = this.proxy;

  //
  // Store provider's information in the request. This allow to know the
  // request has entered in the "provider's zone" and also passes information
  // to filters that needs providers data.
  //
  this.root.use(provider.context, function(req, res, next) {
    req.provider = {
      providerId: provider.id,
      context: provider.context,
      target: provider.target
    };
    next();
  });

  //
  // Add provider prefilters
  //
  provider.prefilters.forEach(function(filter) {
    // Log before
    this.root.use(provider.context, function(req, res, next) {
      log.debug({filter: filter.id, provider: provider.id}, "Before apply prefilter on provider...");
      next();
    });
    // Filter middleware
    this.root.use(provider.context, filter.middleware);
    // Log after
    this.root.use(provider.context, function(req, res, next) {
      log.debug({filter: filter.id, provider: provider.id}, "After apply prefilter on provider...");
      next();
    });
  }, this);

  //
  // Add resources prefilters
  //
  provider.resources.forEach(function(resource) {
    if (resource.prefilters) {
      resource.prefilters.forEach(function(filter) {
        // Log before
        this.root.use(provider.context + resource.context, function(req, res, next) {
          log.debug({filter: filter.id, provider: provider.id, resource: resource.id}, "Before apply prefilter on provider's resource...");
          next();
        });
        // Filter middleware
        this.root.use(resource.context + resource.context, filter.middleware);
        // Log after
        this.root.use(provider.context + resource.context, function(req, res, next) {
          log.debug({filter: filter.id, provider: provider.id, resource: resource.id}, "After apply prefilter on provider's resource...");
          next();
        });
      }, this);
    }
  }, this);

  //
  // Add provider middleware
  //
  this.root.use(provider.context, function(req, res, next) {

    log.debug({req: req, provider: provider.id, target: provider.target}, "Sending request to provider...");

    // Proxy request to the provider
    proxy.web(req, res, {
      target: provider.target
    }, function(err) {
      // If there is any proxy error return the error.
      next(err);
    });

    // Log proxy response
    proxy.on("proxyRes", function(proxyRes) {
      log.debug({res: proxyRes, provider: provider.id, target: provider.target}, "Received response from provider...");
    });

    // Wait response finished and continue middleware chain.
    res.on("finish", function() {
      next();
    });
  });

  //
  // Add resources postfilters
  //
  provider.resources.forEach(function(resource) {
    if (resource.postfilters) {
      resource.postfilters.forEach(function(filter) {
        // Log before
        this.root.use(provider.context + resource.context, function(req, res, next) {
          log.debug({filter: filter.id, provider: provider.id, resource: resource.id}, "Before apply postfilter on provider's resource...");
          next();
        });
        // Filter middleware
        this.root.use(resource.context + resource.context, filter.middleware);
        // Log after
        this.root.use(provider.context + resource.context, function(req, res, next) {
          log.debug({filter: filter.id, provider: provider.id, resource: resource.id}, "After apply postfilter on provider's resource...");
          next();
        });
      }, this);
    }
  }, this);

  //
  // Add provider postfilters
  //
  provider.postfilters.forEach(function(filter) {
    // Log before
    this.root.use(provider.context, function(req, res, next) {
      log.debug({filter: filter.id, provider: provider.id}, "Before apply postfilter on provider...");
      next();
    });
    // Filter middleware
    this.root.use(provider.context, filter.middleware);
    // Log after
    this.root.use(provider.context, function(req, res, next) {
      log.debug({filter: filter.id, provider: provider.id}, "After apply postfilter on provider...");
      next();
    });
  }, this);

};

/**
 * Returns an error middleware handler.
 *
 * @private
 * @returns {Function} A middleware error handler.
 */
Clyde.prototype.errorHandler = function() {
  var log = this.log;

  return function(err, req, res, next) {  // eslint-disable-line no-unused-vars
    // NOTE: Some modules use 'statusCode' and others (like passport) uses 'status'.
    var statusCode = err.statusCode || err.status || 500,
        message = err.message;

    res.writeHead(statusCode, {
      "Content-Length": message.length,
      "Content-Type": "text/plain"
    });
    res.end(message);

    var obj = {req: req, res: res, err: err};

    if (statusCode >= 500) {
      log.warn(obj, message);
    } else {
      log.debug(obj, message);
    }
  };
};

/**
 * Creates a Clyde instance and returns its corresponding middleware responsible
 * to publish the providers specified by options.
 *
 * @param  {object} options JavaScript object with the configuration.
 * @returns {middleware} Application instance, a request-response handler
 * following middleware pattern.
 */
module.exports.createMiddleware = function(options) {
  // Apply default values to log properties if not present
  options.logfile = options.logfile || "clyde.log";
  options.loglevel = options.loglevel || "info";

  var clyde = new Clyde(options);
  return clyde.middleware();
};
<commit_msg>Fixed a bug attaching filters on resources<commit_after>"use strict";

var connect = require("connect"),
    httpProxy = require("http-proxy"),
    configuration = require("./configuration"),
    NoProviderFound = require("./errors/no-provider-found.js");

/**
 * Clyde constructor
 *
 * @constructor
 * @param {Object} options Configuration options
 */
function Clyde(options) {
  // Options
  this.options = options;

  // Initialize log with options
  this.log = require("./log")(this.options);

  // Proxy instance to be used to route request to providers.
  this.proxy = httpProxy.createProxyServer();

  // Create root middleware
  this.root = connect();
}

/**
 * Builds and returns a middleware that runs the specified configuration.
 *
 * @public
 * @returns {Function} Middleware function
 */
Clyde.prototype.middleware = function() {

  // Initialize log with options
  var log = this.log;

  // Load configuration middlewares (filters and providers)
  var middlewares = configuration.load(this.options);

  //
  // Add prefilters middlewares
  //
  middlewares.prefilters.forEach(function(filter) {
    // Log before
    this.root.use(function(req, res, next) {
      log.debug({filter: filter.id}, "Before apply prefilter...");
      next();
    });
    // Filter middleware
    this.root.use(filter.middleware);
    // Log after
    this.root.use(function(req, res, next) {
      log.debug({filter: filter.id}, "After apply prefilter...");
      next();
    });
  }, this);

  //
  // Add providers and its filters middlewares
  //
  var index,
      num = middlewares.providers.length,
      providers = middlewares.providers;
  for (index = 0; index < num; index++) {
    this.registerProviderMiddleware(providers[index]);
  }

  //
  // Check if request has been processed by a provider and throw 404 error
  //
  this.root.use(function(req, res, next) {
    if (!req.provider) {
      next(new NoProviderFound());
    }
    next();
  });

  //
  // Add postfilters middlewares
  //
  middlewares.postfilters.forEach(function(filter) {
    // Log before
    this.root.use(function(req, res, next) {
      log.debug({filter: filter.id}, "Before apply postfilter...");
      next();
    });
    // Filter middleware
    this.root.use(filter.middleware);
    // Log after
    this.root.use(function(req, res, next) {
      log.debug({filter: filter.id}, "After apply postfilter...");
      next();
    });
  }, this);

  // Attach error handler
  this.root.use(this.errorHandler());

  return this.root;
};

/**
 * Add provider to the chain of application middlewares.
 *
 * @private
 * @param  {object} provider Provider configuration
 * @returns {void}
 */
Clyde.prototype.registerProviderMiddleware = function(provider) {

  var log = this.log,
      proxy = this.proxy;

  //
  // Store provider's information in the request. This allow to know the
  // request has entered in the "provider's zone" and also passes information
  // to filters that needs providers data.
  //
  this.root.use(provider.context, function(req, res, next) {
    req.provider = {
      providerId: provider.id,
      context: provider.context,
      target: provider.target
    };
    next();
  });

  //
  // Add provider prefilters
  //
  provider.prefilters.forEach(function(filter) {
    // Log before
    this.root.use(provider.context, function(req, res, next) {
      log.debug({filter: filter.id, provider: provider.id}, "Before apply prefilter on provider...");
      next();
    });
    // Filter middleware
    this.root.use(provider.context, filter.middleware);
    // Log after
    this.root.use(provider.context, function(req, res, next) {
      log.debug({filter: filter.id, provider: provider.id}, "After apply prefilter on provider...");
      next();
    });
  }, this);

  //
  // Add resources prefilters
  //
  provider.resources.forEach(function(resource) {
    if (resource.prefilters) {
      resource.prefilters.forEach(function(filter) {
        var ctx = provider.context + resource.context;
        // Log before
        this.root.use(ctx, function(req, res, next) {
          log.debug({filter: filter.id, provider: provider.id, resource: resource.id}, "Before apply prefilter on provider's resource...");
          next();
        });
        // Filter middleware
        this.root.use(ctx, filter.middleware);
        // Log after
        this.root.use(ctx, function(req, res, next) {
          log.debug({filter: filter.id, provider: provider.id, resource: resource.id}, "After apply prefilter on provider's resource...");
          next();
        });
      }, this);
    }
  }, this);

  //
  // Add provider middleware
  //
  this.root.use(provider.context, function(req, res, next) {

    log.debug({req: req, provider: provider.id, target: provider.target}, "Sending request to provider...");

    // Proxy request to the provider
    proxy.web(req, res, {
      target: provider.target
    }, function(err) {
      // If there is any proxy error return the error.
      next(err);
    });

    // Log proxy response
    proxy.on("proxyRes", function(proxyRes) {
      log.debug({res: proxyRes, provider: provider.id, target: provider.target}, "Received response from provider...");
    });

    // Wait response finished and continue middleware chain.
    res.on("finish", function() {
      next();
    });
  });

  //
  // Add resources postfilters
  //
  provider.resources.forEach(function(resource) {
    if (resource.postfilters) {
      resource.postfilters.forEach(function(filter) {
        var ctx = provider.context + resource.context;
        // Log before
        this.root.use(ctx, function(req, res, next) {
          log.debug({filter: filter.id, provider: provider.id, resource: resource.id}, "Before apply postfilter on provider's resource...");
          next();
        });
        // Filter middleware
        this.root.use(ctx, filter.middleware);
        // Log after
        this.root.use(ctx, function(req, res, next) {
          log.debug({filter: filter.id, provider: provider.id, resource: resource.id}, "After apply postfilter on provider's resource...");
          next();
        });
      }, this);
    }
  }, this);

  //
  // Add provider postfilters
  //
  provider.postfilters.forEach(function(filter) {
    // Log before
    this.root.use(provider.context, function(req, res, next) {
      log.debug({filter: filter.id, provider: provider.id}, "Before apply postfilter on provider...");
      next();
    });
    // Filter middleware
    this.root.use(provider.context, filter.middleware);
    // Log after
    this.root.use(provider.context, function(req, res, next) {
      log.debug({filter: filter.id, provider: provider.id}, "After apply postfilter on provider...");
      next();
    });
  }, this);

};

/**
 * Returns an error middleware handler.
 *
 * @private
 * @returns {Function} A middleware error handler.
 */
Clyde.prototype.errorHandler = function() {
  var log = this.log;

  return function(err, req, res, next) {  // eslint-disable-line no-unused-vars
    // NOTE: Some modules use 'statusCode' and others (like passport) uses 'status'.
    var statusCode = err.statusCode || err.status || 500,
        message = err.message;

    res.writeHead(statusCode, {
      "Content-Length": message.length,
      "Content-Type": "text/plain"
    });
    res.end(message);

    var obj = {req: req, res: res, err: err};

    if (statusCode >= 500) {
      log.warn(obj, message);
    } else {
      log.debug(obj, message);
    }
  };
};

/**
 * Creates a Clyde instance and returns its corresponding middleware responsible
 * to publish the providers specified by options.
 *
 * @param  {object} options JavaScript object with the configuration.
 * @returns {middleware} Application instance, a request-response handler
 * following middleware pattern.
 */
module.exports.createMiddleware = function(options) {
  // Apply default values to log properties if not present
  options.logfile = options.logfile || "clyde.log";
  options.loglevel = options.loglevel || "info";

  var clyde = new Clyde(options);
  return clyde.middleware();
};
<|endoftext|>


<commit_before>var rdbg = require('rdbg');
var url = require('url');
var path = require('path');
var fs = require('fs');

function debug(port, host, options) {
  var bugger = rdbg.connect(port, host);

  var reattach = function() {
    bugger.targets(function(targets) {
      var target = targets.filter(function(target) {
        return target.url.search(options.url) > -1 && target.webSocketDebuggerUrl;
      })[0];

      if (target !== undefined) {
        bugger.attach(target);
      } else {
        setTimeout(reattach, 250);
      }
    });
  };

  var scripts = options.scripts || {};
  var resolve = function(filename) {
    if (scripts[filename]) {
      return scripts[filename];
    }

    return path.relative(cwd, filename);
  };

  var emit = function(event, filename) {
    var source = 'if (typeof(window) !== \'undefined\') {'
               + '  var e = new CustomEvent(\'' + event + '\','
               + '    { detail: \'' + filename + '\' });'
               + ''
               + '  window.dispatchEvent(e);'
               + '}';
    +''
    + 'if (typeof(process) !== \'undefined\') {'
    + '  process.emit(\'' + event + '\', \'' + filename + '\');'
    + '}';

    bugger.evaluate(source, function(error, result) {
      if (error) {
        bugger.emit('error', error);
      }
    });
  };

  var watcher = options.watcher;
  watcher.on('add', function(filename) {
    var pathname = resolve(filename);
    emit('add', pathname);
  });

  watcher.on('remove', function(filename) {
    var pathname = resolve(filename);
    emit('remove', pathname);
  });

  watcher.on('change', function(filename) {
    var pathname = resolve(filename);
    emit('change', pathname);

    bugger.scripts(function(scripts) {
      var script = scripts.filter(function(script) {
        var location = url.parse(script.url);
        return path.relative('/', location.path || '') === pathname;
      })[0];

      if (script === undefined) {
        return;
      }

      fs.readFile(filename, 'utf-8', function(error, contents) {
        if (error) {
          return bugger.emit('error', error);
        }

        bugger.source(script, contents);
      });
    });
  });

  bugger.on('source', function(script) {
    var location = url.parse(script.url);
    var pathname = path.relative('/', location.pathname || '');
    emit('source', pathname);
  });

  bugger.on('detatch', reattach);
  process.nextTick(reattach);

  return bugger;
}

module.exports = debug;
<commit_msg>Add default values for debug options<commit_after>var rdbg = require('rdbg');
var url = require('url');
var path = require('path');
var fs = require('fs');
var extend = require('extend');

function debug(port, host) {
  var options = extend({
    cwd: process.cwd(),
    scripts: {},
    watcher: undefined,
  }, arguments[2] === undefined ? {} : arguments[2]);

  var bugger = rdbg.connect(port, host);

  var reattach = function() {
    bugger.targets(function(targets) {
      var target = targets.filter(function(target) {
        return target.url.search(options.url) > -1 && target.webSocketDebuggerUrl;
      })[0];

      if (target !== undefined) {
        bugger.attach(target);
      } else {
        setTimeout(reattach, 250);
      }
    });
  };

  var resolve = function(filename) {
    if (options.scripts[filename]) {
      return options.scripts[filename];
    }

    return path.relative(options.cwd, filename);
  };

  var emit = function(event, filename) {
    var source = 'if (typeof(window) !== \'undefined\') {'
               + '  var e = new CustomEvent(\'' + event + '\','
               + '    { detail: \'' + filename + '\' });'
               + ''
               + '  window.dispatchEvent(e);'
               + '}';
    +''
    + 'if (typeof(process) !== \'undefined\') {'
    + '  process.emit(\'' + event + '\', \'' + filename + '\');'
    + '}';

    bugger.evaluate(source, function(error, result) {
      if (error) {
        bugger.emit('error', error);
      }
    });
  };

  if (options.watcher !== undefined) {
    options.watcher.on('add', function(filename) {
      var pathname = resolve(filename);
      emit('add', pathname);
    });

    options.watcher.on('remove', function(filename) {
      var pathname = resolve(filename);
      emit('remove', pathname);
    });

    options.watcher.on('change', function(filename) {
      var pathname = resolve(filename);
      emit('change', pathname);

      setTimeout(function() {
        bugger.scripts(function(scripts) {
          var script = scripts.filter(function(script) {
            var location = url.parse(script.url);
            return path.relative('/', location.path || '') === pathname;
          })[0];

          if (script === undefined) {
            return;
          }

          fs.readFile(filename, 'utf-8', function(error, contents) {
            if (error) {
              return bugger.emit('error', error);
            }

            bugger.source(script, contents);
          });
        });
      }, 100);
    });

    bugger.on('source', function(script) {
      var location = url.parse(script.url);
      var pathname = path.relative('/', location.pathname || '');
      emit('source', pathname);
    });
  }

  bugger.on('detatch', reattach);
  process.nextTick(reattach);

  return bugger;
}

module.exports = debug;
<|endoftext|>


<commit_before>/**
 *  CheckBox.js
 *
 *  Created by Julia Radzhabova on 1/24/14
 *  Copyright (c) 2014 Ascensio System SIA. All rights reserved.
 *
 */

/**
 *  Single checkbox field. Can be used as a direct replacement for traditional checkbox fields.
 *  Checkboxes may be given an optional {@link #labelText} which will be displayed immediately after checkbox.
 *
 *  Example usage:
 *      new Common.UI.CheckBox({
 *          el          : $('#id'),
 *          labelText   : 'someText',
 *          value       : true
 *      });
 *
 *  # Values
 *
 *  The main value of a checkbox is a boolean, indicating whether or not the checkbox is checked.
 *  To check the checkbox use setValue(...) function with parameters:
 *
 *  - `true`
 *  - `'true'`
 *  - `'1'`
 *  - `1`
 *  - 'checked'
 *
 *  Checkbox can be in indeterminate state. Use setValue('indeterminate').
 *
 *  Any other value will uncheck the checkbox.
 *
 *  To get the checkbox state use getValue() function. It can return 'checked' / 'unchecked' / 'indeterminate'.
 *
 *  @property {Boolean} disabled
 *  True if this checkbox is disabled.
 *
 *  disabled: false,
 *
 * **/

if (Common === undefined)
    var Common = {};

define([
    'common/main/lib/component/BaseView',
    'underscore'
], function (base, _) {
    'use strict';

    Common.UI.CheckBox = Common.UI.BaseView.extend({

        options : {
            labelText: ''
        },

        disabled    : false,
        rendered    : false,
        indeterminate: false,
        checked     : false,
        value       : 'unchecked',

        template    : _.template('<input type="button" class="img-commonctrl"><%= labelText %>'),

        initialize : function(options) {
            Common.UI.BaseView.prototype.initialize.call(this, options);

            var me = this,
                el = $(this.el);

            this.render();

            if (this.options.disabled)
                this.setDisabled(this.options.disabled);

            if (this.options.value!==undefined)
                this.setValue(this.options.value, true);

            // handle events
            this.$chk.on('click', _.bind(this.onItemCheck, this));
        },

        render: function () {
            var el = $(this.el);
            el.html(this.template({
                labelText: this.options.labelText
            }));

            this.$chk = el.find('input[type=button]');
            this.$label = el.find('label');

            this.rendered = true;

            return this;
        },

        setDisabled: function(disabled) {
            if (disabled !== this.disabled) {
                this.$label.toggleClass('disabled', disabled);
                (disabled) ? this.$chk.attr({disabled: disabled}) : this.$chk.removeAttr('disabled');
            }

            this.disabled = disabled;
        },

        isDisabled: function() {
            return this.disabled;
        },

        onItemCheck: function (e) {
            if (!this.disabled) {
                if (this.indeterminate){
                    this.indeterminate = false;
                    this.setValue(false);
                } else {
                    this.setValue(!this.checked);
                }
            }
        },

        setRawValue: function(value) {
            this.checked = (value === true || value === 'true' || value === '1' || value === 1 || value === 'checked');
            this.indeterminate = (value === 'indeterminate');

            this.$chk.toggleClass('checked', this.checked);
            this.$chk.toggleClass('indeterminate', this.indeterminate);

            this.value = this.indeterminate ? 'indeterminate' : (this.checked ? 'checked' : 'unchecked');
        },

        setValue: function(value, suspendchange) {
            if (this.rendered) {
                this.lastValue = this.value;
                this.setRawValue(value);
                if (suspendchange !== true && this.lastValue !== value)
                    this.trigger('change', this, this.value, this.lastValue);
            } else {
                this.options.value = value;
            }
        },

        getValue: function() {
            return this.value;
        },

        isChecked: function () {
            return this.checked;
        },

        setCaption: function(text) {
            this.$label.find('span').text(text);
        }
    });
});<commit_msg>[Common] Отладка CheckBox.<commit_after>/**
 *  CheckBox.js
 *
 *  Created by Julia Radzhabova on 1/24/14
 *  Copyright (c) 2014 Ascensio System SIA. All rights reserved.
 *
 */

/**
 *  Single checkbox field. Can be used as a direct replacement for traditional checkbox fields.
 *  Checkboxes may be given an optional {@link #labelText} which will be displayed immediately after checkbox.
 *
 *  Example usage:
 *      new Common.UI.CheckBox({
 *          el          : $('#id'),
 *          labelText   : 'someText',
 *          value       : true
 *      });
 *
 *  # Values
 *
 *  The main value of a checkbox is a boolean, indicating whether or not the checkbox is checked.
 *  To check the checkbox use setValue(...) function with parameters:
 *
 *  - `true`
 *  - `'true'`
 *  - `'1'`
 *  - `1`
 *  - 'checked'
 *
 *  Checkbox can be in indeterminate state. Use setValue('indeterminate').
 *
 *  Any other value will uncheck the checkbox.
 *
 *  To get the checkbox state use getValue() function. It can return 'checked' / 'unchecked' / 'indeterminate'.
 *
 *  @property {Boolean} disabled
 *  True if this checkbox is disabled.
 *
 *  disabled: false,
 *
 * **/

if (Common === undefined)
    var Common = {};

define([
    'common/main/lib/component/BaseView',
    'underscore'
], function (base, _) {
    'use strict';

    Common.UI.CheckBox = Common.UI.BaseView.extend({

        options : {
            labelText: ''
        },

        disabled    : false,
        rendered    : false,
        indeterminate: false,
        checked     : false,
        value       : 'unchecked',

        template    : _.template('<input type="button" class="img-commonctrl"><%= labelText %>'),

        initialize : function(options) {
            Common.UI.BaseView.prototype.initialize.call(this, options);

            var me = this,
                el = $(this.el);

            this.render();

            if (this.options.disabled)
                this.setDisabled(this.options.disabled);

            if (this.options.value!==undefined)
                this.setValue(this.options.value, true);

            // handle events
            this.$chk.on('click', _.bind(this.onItemCheck, this));
        },

        render: function () {
            var el = $(this.el);
            el.html(this.template({
                labelText: this.options.labelText
            }));

            this.$chk = el.find('input[type=button]');
            this.$label = el.find('label');

            this.rendered = true;

            return this;
        },

        setDisabled: function(disabled) {
            disabled = (disabled===true);
            if (disabled !== this.disabled) {
                this.$label.toggleClass('disabled', disabled);
                (disabled) ? this.$chk.attr({disabled: disabled}) : this.$chk.removeAttr('disabled');
            }

            this.disabled = disabled;
        },

        isDisabled: function() {
            return this.disabled;
        },

        onItemCheck: function (e) {
            if (!this.disabled) {
                if (this.indeterminate){
                    this.indeterminate = false;
                    this.setValue(false);
                } else {
                    this.setValue(!this.checked);
                }
            }
        },

        setRawValue: function(value) {
            this.checked = (value === true || value === 'true' || value === '1' || value === 1 || value === 'checked');
            this.indeterminate = (value === 'indeterminate');

            this.$chk.toggleClass('checked', this.checked);
            this.$chk.toggleClass('indeterminate', this.indeterminate);

            this.value = this.indeterminate ? 'indeterminate' : (this.checked ? 'checked' : 'unchecked');
        },

        setValue: function(value, suspendchange) {
            if (this.rendered) {
                this.lastValue = this.value;
                this.setRawValue(value);
                if (suspendchange !== true && this.lastValue !== value)
                    this.trigger('change', this, this.value, this.lastValue);
            } else {
                this.options.value = value;
            }
        },

        getValue: function() {
            return this.value;
        },

        isChecked: function () {
            return this.checked;
        },

        setCaption: function(text) {
            this.$label.find('span').text(text);
        }
    });
});<|endoftext|>