<commit_before>'use strict';
var core = {};
var fs = require('fs');
var mkdirp = require('mkdirp');
var config = require('nconf');
var winston = require('winston');
var CronJob = require('cron').CronJob;
var getLoggerSettings = function(consoleLog) {
  var settings = {
    transports: [
      new winston.transports.File({ filename: process.env.HOME + "/.evac/evac.log", json: false})
    ],
    exceptionHandlers: [
      new winston.transports.File({ filename: process.env.HOME + "/.evac/evac.log", json: false})
    ]
  };

  if (consoleLog) {
    settings.transports.push(new winston.transports.Console({colorize: true, timestamp: true}));
    settings.exceptionHandlers.push(new winston.transports.Console({colorize: true, timestamp: true}));
  }

  return settings;
};

var logger = new (winston.Logger)(getLoggerSettings(false));
var pluginPaths = [__dirname + '/plugin'];

var parseRecipeFile = function(path) {
  try {
    config.use('file', {file: path});
  } catch(e) {
    logger.error("Error parsing your evac recipe file.");
    process.exit(1);
  }

  config.load(function (err, conf) {
    if (err) {
      logger.error('Failed to load configuration from ' + path);
      process.exit(1);
    }

    core.parseRecipeJson(conf, function(err, message){
      if (err) {
        logger.error(message + " plugin - raise error.");
        logger.error(err);
        process.exit(1);
      }
    });
  });
};

var parseCronFile = function(path) {
  try {
    config.use('file', {file: path});
  } catch(e) {
    logger.error("Error parsing your cron configuration file.");
    process.exit(1);
  }

  config.load(function (err, cronConfig) {
    if (err) {
      logger.error('Failed to load configuration from ' + path);
      process.exit(1);
    }

    var jobs = [];

    for(var config in cronConfig) {
      var oneConfig = cronConfig[config];

      for (var schedule in oneConfig) {
        var job = new CronJob({
          cronTime: schedule,
          onTick: function() {
            parseRecipeFile(oneConfig[schedule]);
          },
          start: true
        });
      }
    }
  });
};

core.parseRecipeJson = function(recipe, callback) {
  if (!recipe.filter) {
    recipe.filter = {
      "through": {}
    };
  }

  var pluginType = ['out', 'filter', 'in'];
  var plugins = {};
  var args = {};

  pluginType.forEach(function(type) {
    for (var plugin in recipe[type]) {
      pluginPaths.forEach(function(path) {
        var pluginFileName = path + '/' + type + '/' + plugin.toLowerCase() + '.js';
        logger.debug("Search plugin : " + pluginFileName);

        if (fs.existsSync(pluginFileName)) {
          plugins[type] = require(pluginFileName);
          args[type] = recipe[type][plugin];
        }
      });
    }

    if (!plugins[type]) {
      callback("Plugin not found.", "Not defined.");
      return;
    }
  });

  try {
    plugins['in'].load(args['in'], function(err, outputs){
      if (err) {
        callback(err, "input");
        return;
      }

      logger.info("Input plugin called.");

      for (var i=0; i<outputs.length; i++) {
        plugins['filter'].filter(args['filter'], outputs[i], function(err, output){
          if (err) {
            callback(err, "filter");
            return;
          }

          logger.info("Filter plugin called.");

          if (output == "") {
            logger.info("Filter plugin no output.");
          } else {
            plugins['out'].output(args['out'], output, function(err, output){
              if (err) {
                callback(err, "output");
                return;
              }

              logger.info("Output plugin received: " + output);
              logger.info("Output finished.");

              callback(false, null);
              return;
            });
          }

        });
      }
    });
  } catch(e) {
    logger.error(e.message);
    callback(e, e.message);
    return;
  }
};

core.start = function(program, args) {
  mkdirp.sync(process.env.HOME + "/.evac");

  logger = new (winston.Logger)(getLoggerSettings(program.verbose));

  if (program.path) {
    pluginPaths.push(program.path);
  }

  if (program.cron) {
    // cron mode
    parseCronFile(args[0]);
  } else {
    // normal mode
    logger.info('Settings are loaded from a file :' + args[0]);
    parseRecipeFile(args[0]);
  }
}

module.exports = core;
<commit_msg>レシピファイルの読み込みを外出しした<commit_after>'use strict';
var core = {};
var util = require('./util.js');
var fs = require('fs');
var mkdirp = require('mkdirp');
var config = require('nconf');
var winston = require('winston');
var CronJob = require('cron').CronJob;
var getLoggerSettings = function(consoleLog) {
  var settings = {
    transports: [
      new winston.transports.File({ filename: process.env.HOME + "/.evac/evac.log", json: false})
    ],
    exceptionHandlers: [
      new winston.transports.File({ filename: process.env.HOME + "/.evac/evac.log", json: false})
    ]
  };

  if (consoleLog) {
    settings.transports.push(new winston.transports.Console({colorize: true, timestamp: true}));
    settings.exceptionHandlers.push(new winston.transports.Console({colorize: true, timestamp: true}));
  }

  return settings;
};

var logger = new (winston.Logger)(getLoggerSettings(false));
var pluginPaths = [__dirname + '/plugin'];

var parseRecipeFile = function(path) {
  util.parseSettingFile(path, function(err, config){
    if (err) {
      logger.error("Error parsing your evac recipe file.");
      process.exit(1);
    }

    config.load(function (err, conf) {
      if (err) {
        logger.error('Failed to load configuration from ' + path);
        process.exit(1);
      }

      core.parseRecipeJson(conf, function(err, message){
        if (err) {
          logger.error(message + " plugin - raise error.");
          logger.error(err);
          process.exit(1);
        }
      });
    });
  });
};

var parseCronFile = function(path) {
  util.parseSettingFile(path, function(err, config){
    if (err) {
      logger.error("Error parsing your cron configuration file.");
      process.exit(1);
    }

    config.load(function (err, cronConfig) {
      if (err) {
        logger.error('Failed to load configuration from ' + path);
        process.exit(1);
      }

      var jobs = [];

      for(var config in cronConfig) {
        var oneConfig = cronConfig[config];

        for (var schedule in oneConfig) {
          var job = new CronJob({
            cronTime: schedule,
            onTick: function() {
              parseRecipeFile(oneConfig[schedule]);
            },
            start: true
          });
        }
      }
    });
  });
};

core.parseRecipeJson = function(recipe, callback) {
  if (!recipe.filter) {
    recipe.filter = {
      "through": {}
    };
  }

  var pluginType = ['out', 'filter', 'in'];
  var plugins = {};
  var args = {};

  pluginType.forEach(function(type) {
    for (var plugin in recipe[type]) {
      pluginPaths.forEach(function(path) {
        var pluginFileName = path + '/' + type + '/' + plugin.toLowerCase() + '.js';
        logger.debug("Search plugin : " + pluginFileName);

        if (fs.existsSync(pluginFileName)) {
          plugins[type] = require(pluginFileName);
          args[type] = recipe[type][plugin];
        }
      });
    }

    if (!plugins[type]) {
      callback("Plugin not found.", "Not defined.");
      return;
    }
  });

  try {
    plugins['in'].load(args['in'], function(err, outputs){
      if (err) {
        callback(err, "input");
        return;
      }

      logger.info("Input plugin called.");

      for (var i=0; i<outputs.length; i++) {
        plugins['filter'].filter(args['filter'], outputs[i], function(err, output){
          if (err) {
            callback(err, "filter");
            return;
          }

          logger.info("Filter plugin called.");

          if (output == "") {
            logger.info("Filter plugin no output.");
          } else {
            plugins['out'].output(args['out'], output, function(err, output){
              if (err) {
                callback(err, "output");
                return;
              }

              logger.info("Output plugin received: " + output);
              logger.info("Output finished.");

              callback(false, null);
              return;
            });
          }

        });
      }
    });
  } catch(e) {
    logger.error(e.message);
    callback(e, e.message);
    return;
  }
};

core.start = function(program, args) {
  mkdirp.sync(process.env.HOME + "/.evac");

  logger = new (winston.Logger)(getLoggerSettings(program.verbose));

  if (program.path) {
    pluginPaths.push(program.path);
  }

  if (program.cron) {
    // cron mode
    parseCronFile(args[0]);
  } else {
    // normal mode
    logger.info('Settings are loaded from a file :' + args[0]);
    parseRecipeFile(args[0]);
  }
}

module.exports = core;
<|endoftext|>


<commit_before>/**
 * Transition Core
 */
(function($)
{
  var global = this;

  /**
   *
   * @type Object
   */
  var Transition = global.Transition =
  {
    START: 'transitionStart',
    END: 'transitionEnd',

    prefixes: ['', 'webkit', 'moz', 'o', 'ms'],
    supportPrefix: '',
    supportEvent: '',
    support: {},
    handlers: {},
    generators: {},
    defaultOptions:
    {
      duration: 400,      // ms
      easing: 'ease'     // CSS3 transition easing
    },

    /**
     *
     * @returns {undefined}
     */
    init: function()
    {
      this.detectSupport();
    },

    /**
     *
     * @param {jQuery} el
     * @param {Object} properties
     * @param {Object} options
     * @param {Function} callback
     */
    core: function(el, properties, options, callback)
    {
      var me = this;

      if ($.type(options) === 'function')
      {
        callback = options;
        options = defaultOptions;
      }
      else if ($.type('options') === 'object')
      {
        callback = callback || $.noop;
        options = $.extend({}, defaultOptions, options);
      }
      else
      {
        callback = $.noop;
        options = defaultOptions;
      }

      var transitionNames = new Array();
      for (var property in properties)
      {
        // When a property isn't supported
        if (!(property in me.handlers))
        {
          continue;
        }

        this.handlers[property].call(el, properties[property], options);
        transitionNames.push(property);
      }

      // Bind to end event and queue
      el.one(this.supportEvent, function()
        {
          // Clear transition
          el.css(me.build(
          {
            'transition-property': '',
            'transition-timing-function': '',
            'transition-duration': ''
          }));

          callback.call(el);
          el.trigger(me.END).dequeue();
        })
        .queue(function()
        {
          // Add transition
          el.css(me.build(
          {
            'transition-property': $.unique(transitionNames).join(', '),
            'transition-timing-function': options.easing || 'ease',
            'transition-duration': options.duration + 'ms'
          }));

          el.trigger(me.START);
        });
    },

    /**
      * Adds the support prefix to a set of css properties
      *
      * @param {Object} css A css attr object to convert
      * @returns {Object}
      */
    build: function(css)
    {
      var out = $.extend({}, css);

      for (var attr in css)
      {
        out['-' + this.supportPrefix + '-' + attr] = css[attr];
      }

      return out;
    },

    /**
     *
     * @returns {String}
     */
    detectSupport: function()
    {
      // Test prefix
      var el = global.document.createElement('div'),
          test = 'transition';

      for (var i = 0; i < this.prefixes.length; i++)
      {
        var style = this.getStyleString(test, this.prefixes[i]);

        if (el.style[style] !== undefined)
        {
          this.supportPrefix = this.prefixes[i];
          break;
        }
      }

      // Test handler support
      var support = {};

      for (var name in this.handlers)
      {
        if ($.type(this.handlers[name]['testSupport']) === 'function')
        {
          $.extend(support, this.handlers[name].testSupport());
        }
      }

      this.supportEvent = (this.supportPrefix === '')
        ? 'transitionend'
        : this.supportPrefix + 'TransitionEnd';
      return this.supportPrefix;
    },

    /**
     *
     * @param {String} property
     * @param {String} prefix
     * @returns {String}
     */
    getStyleString: function(property, prefix)
    {
      prefix = prefix || this.supportPrefix;

      if (prefix.trim() === '')
      {
        return property;
      }

      return [prefix, property].map(function(s)
      {
        return s.charAt(0).toUpperCase() + s.slice(1);
      }).join('');
    },

    /**
     * To add a transition property generator
     *
     * @param {Object} obj
     */
    addGenerator: function(obj)
    {
      $.extend(this.generators, obj);
    },

    /**
     * To add a transition handler
     *
     * @param {Object} obj
     */
    addHandler: function(obj)
    {
      $.extend(this.handlers, obj);
    }
  };

  // Detect support on load
  $(function()
  {
    Transition.init();
  });

  // Add to jQuery
  $.fn.extend(
  {
    transition: function(properties, options, callback)
    {
      Transition.core($(this), properties, options, callback);
      return $(this);
    }
  });

})(jQuery);
<commit_msg>Fixed invalid reference to object variable<commit_after>/**
 * Transition Core
 */
(function($)
{
  var global = this;

  /**
   *
   * @type Object
   */
  var Transition = global.Transition =
  {
    START: 'transitionStart',
    END: 'transitionEnd',

    prefixes: ['', 'webkit', 'moz', 'o', 'ms'],
    supportPrefix: '',
    supportEvent: '',
    support: {},
    handlers: {},
    generators: {},
    defaultOptions:
    {
      duration: 400,      // ms
      easing: 'ease'     // CSS3 transition easing
    },

    /**
     *
     * @returns {undefined}
     */
    init: function()
    {
      this.detectSupport();
    },

    /**
     *
     * @param {jQuery} el
     * @param {Object} properties
     * @param {Object} options
     * @param {Function} callback
     */
    core: function(el, properties, options, callback)
    {
      var me = this;

      if ($.type(options) === 'function')
      {
        callback = options;
        options = defaultOptions;
      }
      else if ($.type('options') === 'object')
      {
        callback = callback || $.noop;
        options = $.extend({}, this.defaultOptions, options);
      }
      else
      {
        callback = $.noop;
        options = defaultOptions;
      }

      var transitionNames = new Array();
      for (var property in properties)
      {
        // When a property isn't supported
        if (!(property in me.handlers))
        {
          continue;
        }

        this.handlers[property].call(el, properties[property], options);
        transitionNames.push(property);
      }

      // Bind to end event and queue
      el.one(this.supportEvent, function()
        {
          // Clear transition
          el.css(me.build(
          {
            'transition-property': '',
            'transition-timing-function': '',
            'transition-duration': ''
          }));

          callback.call(el);
          el.trigger(me.END).dequeue();
        })
        .queue(function()
        {
          // Add transition
          el.css(me.build(
          {
            'transition-property': $.unique(transitionNames).join(', '),
            'transition-timing-function': options.easing || 'ease',
            'transition-duration': options.duration + 'ms'
          }));

          el.trigger(me.START);
        });
    },

    /**
      * Adds the support prefix to a set of css properties
      *
      * @param {Object} css A css attr object to convert
      * @returns {Object}
      */
    build: function(css)
    {
      var out = $.extend({}, css);

      for (var attr in css)
      {
        out['-' + this.supportPrefix + '-' + attr] = css[attr];
      }

      return out;
    },

    /**
     *
     * @returns {String}
     */
    detectSupport: function()
    {
      // Test prefix
      var el = global.document.createElement('div'),
          test = 'transition';

      for (var i = 0; i < this.prefixes.length; i++)
      {
        var style = this.getStyleString(test, this.prefixes[i]);

        if (el.style[style] !== undefined)
        {
          this.supportPrefix = this.prefixes[i];
          break;
        }
      }

      // Test handler support
      var support = {};

      for (var name in this.handlers)
      {
        if ($.type(this.handlers[name]['testSupport']) === 'function')
        {
          $.extend(support, this.handlers[name].testSupport());
        }
      }

      this.supportEvent = (this.supportPrefix === '')
        ? 'transitionend'
        : this.supportPrefix + 'TransitionEnd';
      return this.supportPrefix;
    },

    /**
     *
     * @param {String} property
     * @param {String} prefix
     * @returns {String}
     */
    getStyleString: function(property, prefix)
    {
      prefix = prefix || this.supportPrefix;

      if (prefix.trim() === '')
      {
        return property;
      }

      return [prefix, property].map(function(s)
      {
        return s.charAt(0).toUpperCase() + s.slice(1);
      }).join('');
    },

    /**
     * To add a transition property generator
     *
     * @param {Object} obj
     */
    addGenerator: function(obj)
    {
      $.extend(this.generators, obj);
    },

    /**
     * To add a transition handler
     *
     * @param {Object} obj
     */
    addHandler: function(obj)
    {
      $.extend(this.handlers, obj);
    }
  };

  // Detect support on load
  $(function()
  {
    Transition.init();
  });

  // Add to jQuery
  $.fn.extend(
  {
    transition: function(properties, options, callback)
    {
      Transition.core($(this), properties, options, callback);
      return $(this);
    }
  });

})(jQuery);
<|endoftext|>


<commit_before>var queue = require('queue-async');
var events = require('events');
var util = require('util');
var URL = require('url');
var qs = require('querystring');
var typeis = require('type-is').is;
var bufferEqual = require('buffer-equal');
var agent = require('./utils/agent');
var Comb = require('js-combinatorics').Combinatorics;

module.exports = function(opts, cb) {
	var raja = new Raja(opts);
	raja.error = Raja.prototype.error.bind(raja);
	raja.agent = agent;

	raja.proxies = {};

	raja.proxies.local = require('./proxies/local')(raja, opts);
	raja.proxies.remote = require('./proxies/remote')(raja, opts);
	raja.proxies.express = require('./proxies/express')(raja, opts);
	raja.proxies.statics = opts.statics && require('./proxies/statics')(raja, opts);
	raja.proxies.dom = opts.dom && require('./proxies/dom')(raja, opts);

	raja.init(function(err) {
		cb(err, raja);
	});
	return raja;
};

module.exports.agent = agent;

function Raja(opts) {
	this.opts = opts || {};
	if (!this.opts.namespace) {
		this.opts.namespace = "";
		console.info("Raja expects a namespace, using empty string");
	}
	this.unsaved = {};
}
util.inherits(Raja, events.EventEmitter);

Raja.prototype.init = function(cb) {
	queue(2)
	.defer(require('./io'), this, this.opts)
	.defer(require('./store'), this, this.opts)
	.await(function(err, io, store) {
		if (err) return cb(err);
		if (this.opts.client && !io) return cb(new Error("raja failed to init io"));
		if (!store) return cb(new Error("raja failed to init store"));
		this.io = io;
		this.store = store;
		this.session = require('./store/session').bind(null, raja);
		this.emit('ready');
		cb(null, this);
	}.bind(this));
};

// internal function
Raja.prototype.receive = function(msg) {
	if (!msg.key && !msg.url) return;
	if (!msg.parents) msg.parents = [];
	if (msg.mtime) msg.mtime = new Date(msg.mtime);
	this.emit('message', msg);
	this.store.invalidateParents(msg, function(err) {
		if (err) console.error(err);
	});
};

Raja.prototype.send = function(msg) {
	if (!msg.key && !msg.url) {
		return this.error(new Error("Missing key in " + JSON.stringify(msg)));
	}
	if (!msg.mtime) msg.mtime = Date.now();
	else if (msg.mtime instanceof Date) msg.mtime = msg.mtime.getTime();
	this.io.send(msg);
};

Raja.prototype.error = function(err) {
	// called when an error happened while using raja
	if (err) console.error(err);
};

Raja.prototype.shallow = shallow;

Raja.prototype.dataEqual = dataEqual;

Raja.prototype.create = function(obj, havingHeaders) {
	if (typeof obj == "string") obj = {url: obj};
	if (havingHeaders) obj.headers = this.headers(havingHeaders);
	if (!obj.url) {
		if (obj.key) {
			obj.url = keyObj(obj.key).url;
		} else {
			throw new Error("Missing key in " + JSON.stringify(obj));
		}
	}
	obj.key = this.key(obj.url, this.vary(obj.headers));
	var inst = this.unsaved[obj.key];
	if (!inst) {
		var props = {};
		for (var key in obj) if (key != 'id' && key != 'children') props[key] = {
			value: obj[key],
			enumerable: true,
			writable: true
		};
		inst = this.unsaved[obj.key] = Object.create(Resource.prototype, props);
		Object.defineProperty(inst, 'raja', {
			enumerable: false,
			value: this
		});
	} else {
		this.shallow(obj, inst);
	}
	return inst;
};

Raja.prototype.retrieve = function(url, havingHeaders, cb) {
	if (!cb && typeof havingHeaders == "function") {
		cb = havingHeaders;
		havingHeaders = null;
	}
	var keys = havingHeaders ? this.keys(url, this.variant(this.headers(havingHeaders))) : [url];
	var store = this.store;
	(function tryGet() {
		var key = keys.shift();
		if (!key) return cb();
		store.get(key, function(err, obj) {
			if (err || obj) return cb(err, obj);
			tryGet();
		});
	})();
};

Raja.prototype.upsert = function(obj, cb) {
	this.retrieve(obj.url, obj, function(err, resource) {
		if (err) return cb(err);
		if (!resource) {
			resource = this.create(obj);
		} else {
			raja.shallow(obj, resource);
			resource.headers = this.headers(resource);
		}
		cb(null, resource);
	}.bind(this));
};

function Resource() { /* initialization code will not be called by Object.create */ }

Resource.prototype.depend = function(resource) {
	if (!this.resources) this.resources = {};
	if (!(resource instanceof Resource)) resource = this.raja.create(resource);
	this.resources[resource.key] = resource;
};

Resource.prototype.undepend = function(resource) {
	if (!this.resources || !resource) return;
	var key;
	if (typeof resource == "string") key = resource;
	else key = this.raja.key(resource.url, this.raja.vary(resource.headers));
	delete this.resources[key];
};

Resource.prototype.save = function(cb) {
	var raja = this.raja;
	cb = cb || raja.error;
	if (!this.url) {
		if (this.key) {
			this.url = keyObj(this.key).url;
		} else {
			throw new Error("Missing key in " + JSON.stringify(this));
		}
	}
	var key = raja.key(this.url, raja.vary(this.headers));
	if (this.key == key) {
		raja.store.set(this.key, this, function(err) {
			delete raja.unsaved[key];
			cb(err);
		});
	} else {
		var copy = raja.create(this);
		copy.key = key;
		copy.save(cb);
	}
};

Resource.prototype.load = function(remote, query, opts, cb) {
	if (!this.resources) this.resources = {};
	if (!cb && !opts && typeof query == "function") {
		cb = query;
		opts = null;
		query = null;
	} else if (!cb && typeof opts == "function") {
		cb = opts;
		opts = null;
	}
	remote = agent.resolve(this.url, remote);
	this.raja.proxies.remote.get(remote, query, opts, function(err, resource) {
		if (err >= 400 && err < 500) {
			this.undepend(resource);
		} else if (!err) {
			this.depend(resource);
		}
		cb(err, resource && resource.data);
	}.bind(this));
};

Resource.prototype.invalidate = function(cb) {
	this.raja.store.invalidate(this.key, cb);
};

Raja.prototype.headers = function(havingHeaders) {
	if (!havingHeaders) return {};
	var obj = {};
	var headers = havingHeaders.headers;
	var hasGet = !!havingHeaders.get;
	if (!headers && !hasGet) headers = havingHeaders;
	['Content-Type', 'Vary', 'ETag', 'Accept', 'X-Grant', 'X-Right'].forEach(function(name) {
		var iname = name.toLowerCase();
		var val = headers && (headers[iname] || headers[name]) || hasGet && havingHeaders.get(iname);
		if (val) obj[name] = val;
	});
	return obj;
};

Raja.prototype.vary = function(headers) {
	if (!headers) return;
	var vary = {};
	if (!headers.Vary) return vary;
	headers.Vary.split(',').forEach(function(header) {
		header = header.trim();
		var val = headers[header];
		if (header == 'Content-Type') {
			var type = typeis(val, ['json', 'html']);
			if (type == 'json') vary.type = type; // store html as default
		} else if (header == 'X-Grant') {
			vary.right = val;
		}
	});
	return vary;
};

Raja.prototype.variant = function(headers) {
	if (!headers) return;
	var obj = {};
	var accept = headers['Accept'];
	if (accept) {
		var type = typeis(accept, ['json', 'html']);
		if (type == 'json') obj.type = type; // no type means default type
	}
	var right = headers['X-Right'];
	if (right) {
		obj.right = right.split(',').map(function(str) {return str.trim();}).sort();
	}
	return obj;
};

Raja.prototype.key = function(url, obj) {
	if (!obj) return url;
	for (var k in obj) if (obj[k] == null) delete obj[k];
	var str = obj && qs.stringify(obj);
	if (str) url = str + ' ' + url;
	return url;
};

Raja.prototype.keyObj = keyObj;

Raja.prototype.keys = function(url, obj) {
	var names = Object.keys(obj);
	if (!names.length) return [url];
	var vec = [];
	names.forEach(function(name) {
		var val = obj[name];
		if (!Array.isArray(val)) val = [val];
		vec.push(orderedPower(val));
	});
	var cp = Comb.cartesianProduct.apply(Comb, vec).toArray();
	var keys = [];
	var self = this;
	cp.forEach(function(vec) {
		var query = {};
		names.forEach(function(name, index) {
			if (vec[index].length) query[name] = vec[index].sort().join(',')
		});
		keys.push(self.key(url, query));
	});
	return keys;
};

function shallow(src, dst) {
	dst = dst || {};
	Object.keys(src).forEach(function(key) {
		var val = src[key];
		if (val === undefined) return;
		if (key == "resources" || key == "parents" || key == "headers") {
			val = shallow(val);
		}
		if ((key == 'resources' || key == 'parents') && val && Object.keys(val).length == 0 && dst[key] && Object.keys(dst[key]).length > 0) {
			console.info("not overwriting", key, dst[key], "with empty object");
			return;
		}
		dst[key] = val;
	});
	return dst;
}

function keyObj(key) {
	if (!key) return {};
	var obj;
	var find = /^(.*\s)?(https?:\/\/.+)$/.exec(key);
	if (find != null && find.length == 3) {
		var query = find[1];
		if (query) {
			obj = qs.parse(query);
		} else {
			obj = {};
		}
		obj.url = find[2];
	} else {
		obj = {url: key};
	}
	return obj;
}

function dataEqual(a, b) {
	if (a == null || b == null) return a === b;
	var bufa = Buffer.isBuffer(a);
	var bufb = Buffer.isBuffer(b);
	if (!bufa && !bufb) return a === b;
	if (!bufa) a = new Buffer(a);
	if (!bufb) b = new Buffer(b);
	return bufferEqual(a, b);
}

function orderedPower(list) {
	var sets = [], iter, cur;
	for (var i=list.length; i > 0; i--) {
		iter = Comb.combination(list, i);
		while (cur = iter.next()) sets.push(cur);
	}
	sets.push([]);
	return sets;
}

<commit_msg>API change for resource.load<commit_after>var queue = require('queue-async');
var events = require('events');
var util = require('util');
var URL = require('url');
var qs = require('querystring');
var typeis = require('type-is').is;
var bufferEqual = require('buffer-equal');
var agent = require('./utils/agent');
var Comb = require('js-combinatorics').Combinatorics;

module.exports = function(opts, cb) {
	var raja = new Raja(opts);
	raja.error = Raja.prototype.error.bind(raja);
	raja.agent = agent;

	raja.proxies = {};

	raja.proxies.local = require('./proxies/local')(raja, opts);
	raja.proxies.remote = require('./proxies/remote')(raja, opts);
	raja.proxies.express = require('./proxies/express')(raja, opts);
	raja.proxies.statics = opts.statics && require('./proxies/statics')(raja, opts);
	raja.proxies.dom = opts.dom && require('./proxies/dom')(raja, opts);

	raja.init(function(err) {
		cb(err, raja);
	});
	return raja;
};

module.exports.agent = agent;

function Raja(opts) {
	this.opts = opts || {};
	if (!this.opts.namespace) {
		this.opts.namespace = "";
		console.info("Raja expects a namespace, using empty string");
	}
	this.unsaved = {};
}
util.inherits(Raja, events.EventEmitter);

Raja.prototype.init = function(cb) {
	queue(2)
	.defer(require('./io'), this, this.opts)
	.defer(require('./store'), this, this.opts)
	.await(function(err, io, store) {
		if (err) return cb(err);
		if (this.opts.client && !io) return cb(new Error("raja failed to init io"));
		if (!store) return cb(new Error("raja failed to init store"));
		this.io = io;
		this.store = store;
		this.session = require('./store/session').bind(null, raja);
		this.emit('ready');
		cb(null, this);
	}.bind(this));
};

// internal function
Raja.prototype.receive = function(msg) {
	if (!msg.key && !msg.url) return;
	if (!msg.parents) msg.parents = [];
	if (msg.mtime) msg.mtime = new Date(msg.mtime);
	this.emit('message', msg);
	this.store.invalidateParents(msg, function(err) {
		if (err) console.error(err);
	});
};

Raja.prototype.send = function(msg) {
	if (!msg.key && !msg.url) {
		return this.error(new Error("Missing key in " + JSON.stringify(msg)));
	}
	if (!msg.mtime) msg.mtime = Date.now();
	else if (msg.mtime instanceof Date) msg.mtime = msg.mtime.getTime();
	this.io.send(msg);
};

Raja.prototype.error = function(err) {
	// called when an error happened while using raja
	if (err) console.error(err);
};

Raja.prototype.shallow = shallow;

Raja.prototype.dataEqual = dataEqual;

Raja.prototype.create = function(obj, havingHeaders) {
	if (typeof obj == "string") obj = {url: obj};
	if (havingHeaders) obj.headers = this.headers(havingHeaders);
	if (!obj.url) {
		if (obj.key) {
			obj.url = keyObj(obj.key).url;
		} else {
			throw new Error("Missing key in " + JSON.stringify(obj));
		}
	}
	obj.key = this.key(obj.url, this.vary(obj.headers));
	var inst = this.unsaved[obj.key];
	if (!inst) {
		var props = {};
		for (var key in obj) if (key != 'id' && key != 'children') props[key] = {
			value: obj[key],
			enumerable: true,
			writable: true
		};
		inst = this.unsaved[obj.key] = Object.create(Resource.prototype, props);
		Object.defineProperty(inst, 'raja', {
			enumerable: false,
			value: this
		});
	} else {
		this.shallow(obj, inst);
	}
	return inst;
};

Raja.prototype.retrieve = function(url, havingHeaders, cb) {
	if (!cb && typeof havingHeaders == "function") {
		cb = havingHeaders;
		havingHeaders = null;
	}
	var keys = havingHeaders ? this.keys(url, this.variant(this.headers(havingHeaders))) : [url];
	var store = this.store;
	(function tryGet() {
		var key = keys.shift();
		if (!key) return cb();
		store.get(key, function(err, obj) {
			if (err || obj) return cb(err, obj);
			tryGet();
		});
	})();
};

Raja.prototype.upsert = function(obj, cb) {
	this.retrieve(obj.url, obj, function(err, resource) {
		if (err) return cb(err);
		if (!resource) {
			resource = this.create(obj);
		} else {
			raja.shallow(obj, resource);
			resource.headers = this.headers(resource);
		}
		cb(null, resource);
	}.bind(this));
};

function Resource() { /* initialization code will not be called by Object.create */ }

Resource.prototype.depend = function(resource) {
	if (!this.resources) this.resources = {};
	if (!(resource instanceof Resource)) resource = this.raja.create(resource);
	this.resources[resource.key] = resource;
};

Resource.prototype.undepend = function(resource) {
	if (!this.resources || !resource) return;
	var key;
	if (typeof resource == "string") key = resource;
	else key = this.raja.key(resource.url, this.raja.vary(resource.headers));
	delete this.resources[key];
};

Resource.prototype.save = function(cb) {
	var raja = this.raja;
	cb = cb || raja.error;
	if (!this.url) {
		if (this.key) {
			this.url = keyObj(this.key).url;
		} else {
			throw new Error("Missing key in " + JSON.stringify(this));
		}
	}
	var key = raja.key(this.url, raja.vary(this.headers));
	if (this.key == key) {
		raja.store.set(this.key, this, function(err) {
			delete raja.unsaved[key];
			cb(err);
		});
	} else {
		var copy = raja.create(this);
		copy.key = key;
		copy.save(cb);
	}
};

Resource.prototype.load = function(remote, query, opts, cb) {
	if (!this.resources) this.resources = {};
	if (!cb && !opts && typeof query == "function") {
		cb = query;
		opts = null;
		query = null;
	} else if (!cb && typeof opts == "function") {
		cb = opts;
		opts = null;
	}
	remote = agent.resolve(this.url, remote);
	this.raja.proxies.remote.get(remote, query, opts, function(err, resource) {
		if (err >= 400 && err < 500) {
			this.undepend(resource);
		} else if (!err) {
			this.depend(resource);
		}
		cb(err, resource);
	}.bind(this));
};

Resource.prototype.invalidate = function(cb) {
	this.raja.store.invalidate(this.key, cb);
};

Raja.prototype.headers = function(havingHeaders) {
	if (!havingHeaders) return {};
	var obj = {};
	var headers = havingHeaders.headers;
	var hasGet = !!havingHeaders.get;
	if (!headers && !hasGet) headers = havingHeaders;
	['Content-Type', 'Vary', 'ETag', 'Accept', 'X-Grant', 'X-Right'].forEach(function(name) {
		var iname = name.toLowerCase();
		var val = headers && (headers[iname] || headers[name]) || hasGet && havingHeaders.get(iname);
		if (val) obj[name] = val;
	});
	return obj;
};

Raja.prototype.vary = function(headers) {
	if (!headers) return;
	var vary = {};
	if (!headers.Vary) return vary;
	headers.Vary.split(',').forEach(function(header) {
		header = header.trim();
		var val = headers[header];
		if (header == 'Content-Type') {
			var type = typeis(val, ['json', 'html']);
			if (type == 'json') vary.type = type; // store html as default
		} else if (header == 'X-Grant') {
			vary.right = val;
		}
	});
	return vary;
};

Raja.prototype.variant = function(headers) {
	if (!headers) return;
	var obj = {};
	var accept = headers['Accept'];
	if (accept) {
		var type = typeis(accept, ['json', 'html']);
		if (type == 'json') obj.type = type; // no type means default type
	}
	var right = headers['X-Right'];
	if (right) {
		obj.right = right.split(',').map(function(str) {return str.trim();}).sort();
	}
	return obj;
};

Raja.prototype.key = function(url, obj) {
	if (!obj) return url;
	for (var k in obj) if (obj[k] == null) delete obj[k];
	var str = obj && qs.stringify(obj);
	if (str) url = str + ' ' + url;
	return url;
};

Raja.prototype.keyObj = keyObj;

Raja.prototype.keys = function(url, obj) {
	var names = Object.keys(obj);
	if (!names.length) return [url];
	var vec = [];
	names.forEach(function(name) {
		var val = obj[name];
		if (!Array.isArray(val)) val = [val];
		vec.push(orderedPower(val));
	});
	var cp = Comb.cartesianProduct.apply(Comb, vec).toArray();
	var keys = [];
	var self = this;
	cp.forEach(function(vec) {
		var query = {};
		names.forEach(function(name, index) {
			if (vec[index].length) query[name] = vec[index].sort().join(',')
		});
		keys.push(self.key(url, query));
	});
	return keys;
};

function shallow(src, dst) {
	dst = dst || {};
	Object.keys(src).forEach(function(key) {
		var val = src[key];
		if (val === undefined) return;
		if (key == "resources" || key == "parents" || key == "headers") {
			val = shallow(val);
		}
		if ((key == 'resources' || key == 'parents') && val && Object.keys(val).length == 0 && dst[key] && Object.keys(dst[key]).length > 0) {
			console.info("not overwriting", key, dst[key], "with empty object");
			return;
		}
		dst[key] = val;
	});
	return dst;
}

function keyObj(key) {
	if (!key) return {};
	var obj;
	var find = /^(.*\s)?(https?:\/\/.+)$/.exec(key);
	if (find != null && find.length == 3) {
		var query = find[1];
		if (query) {
			obj = qs.parse(query);
		} else {
			obj = {};
		}
		obj.url = find[2];
	} else {
		obj = {url: key};
	}
	return obj;
}

function dataEqual(a, b) {
	if (a == null || b == null) return a === b;
	var bufa = Buffer.isBuffer(a);
	var bufb = Buffer.isBuffer(b);
	if (!bufa && !bufb) return a === b;
	if (!bufa) a = new Buffer(a);
	if (!bufb) b = new Buffer(b);
	return bufferEqual(a, b);
}

function orderedPower(list) {
	var sets = [], iter, cur;
	for (var i=list.length; i > 0; i--) {
		iter = Comb.combination(list, i);
		while (cur = iter.next()) sets.push(cur);
	}
	sets.push([]);
	return sets;
}

<|endoftext|>


<commit_before>/**
 * helptip - Displays a helptip on hovering an element
 * Usage:
 * el.helptip('show this on hover', { 'static': true }); // create a helptip (or modifies an existing one)
 * el.helptip('');                                       // hides a helptip
 * el.helptip(false);                                    // completely removes any existing helptip from the element
 **/
(function ($) {
  $.helptip = function (tip, options) {
    var el  = $(this);
    options = $.extend({
      container:       'body',
      scrollContainer: $(window),
      eventIn:         'mouseenter',
      eventOut:        'mouseleave',
      delay:           100,
      callbacks:       {}
    }, options);

    var data = { coords: false };

    if (typeof tip === 'undefined' || tip === '') {
      tip = this.title;
    }

    var methods = {
      init: function () {
        var elData = el.data('helptip');

        if (elData) {
          data = $.extend(elData, data);
        }

        el.data('helptip', data);

        if (data.popup) {
          data.disabled = false; // if helptip already created
        } else {
          data.popup = $('

').appendTo(options.container);
        }

        methods.setOption(); // set options

        tip = tip.replace(/\n/g, '
');

        if (el[0].title) {
          el[0].title = '';
        }

        if (tip) {
          methods.setTip(tip); // set tip

          if (options.show) {
            data.popup.show();
          }
        } else {
          methods.disable(el);
        }

        if (!data.initiated) {
          methods.setEventHandlers(); // set event handlers
        }

        if (typeof options.callbacks.init === 'function') {
          options.callbacks.init.call(this, el, data, options);
        }
      },

      setOption: function () {
        data.options = options;

        if (data['class']) { // previously added class
          data.popup.removeClass(data['class']);
        }

        if (options['class']) {
          data.popup.addClass(options['class']);
          data['class'] = options['class'];
        }

        data.popup.toggleClass('helptip-static', options['static']);
      },

      setTip: function (html) {
        data.popup.children().html(html).end().width(function (i, w) { return w; });
      },

      setEventHandlers: function (args) {
        args = $.extend({
          namespace: 'helptip',
          mouseIn:   methods.mouseIn,
          mouseOut:  methods.mouseOut
        }, args);

        var handlers = {};
        var func;

        if (options.delay) {
          handlers = { over: args.mouseIn, out: args.mouseOut, interval: options.delay, namespace: args.namespace };
          func     = 'hoverIntent';

          el.on('mousemove.helptip-track', args.mouseMove || function (e) {
            if (!data.mousemove) {
              data.coords = { x: e.pageX, y: e.pageY };
            }
          });
        } else {
          handlers[options.eventIn  + '.' + args.namespace] = args.mouseIn;
          handlers[options.eventOut + '.' + args.namespace] = args.mouseOut;

          func = 'on';
        }

        el[func](handlers);

        if (data.popup) {
          data.popup.on(options.eventOut + '.' + args.namespace, args.mouseOut);
        }

        if (args.namespace === 'helptip') {
          data.initiated = true;
        }
      },

      mouseIn: function (e) {
        var pos;

        if (options['static']) {
          var offset = options.container === 'body' ? el.offset() : el.position();
          pos = { x: offset.left + el.outerWidth() / 2, y: offset.top + el.outerHeight() / 2 };
        } else {
          pos = data.coords || { x: e.pageX, y: e.pageY };
          data.coords = false;

          if (!data.mousemove) {
            el.on('mousemove.helptip', methods.mouseMove);
            data.mousemove = true;
          }
        }

        var originalPos = $.extend(true, pos);

        if (options.fixed) {
          pos.x += options.scrollContainer.scrollLeft();
        } else {
          data.popup.removeClass('helptip-left helptip-top');

          if (options.scrollContainer.height() < ((pos.y - options.scrollContainer.scrollTop()) * 2)) {
            data.popup.addClass('helptip-top');
            pos.y -= data.popup.height();
          }

          if (options.scrollContainer.width() < ((pos.x - options.scrollContainer.scrollLeft()) * 2)) {
            data.popup.addClass('helptip-left');
            pos.x -= (data.offset || {}).width || data.popup.width();
          }
        }

        if (!data.disabled) {
          data.popup.css({ left: pos.x, top: pos.y }).show();
        }

        data.offset = { x: pos.x - originalPos.x, y: pos.y - originalPos.y, width: data.popup.width() };
      },

      mouseOut: function (e) {
        if (!(e.currentTarget === el[0] ? data.popup : el).find('*').andSelf().is(e.relatedTarget)) {
          data.popup.hide();
          el.off('mousemove.helptip');
          data.mousemove = false;
        }
      },

      mouseMove: function (e) {
        if (!data.disabled) {
          var offset = data.offset;
          data.popup.css({ left: offset.x + e.pageX, top: offset.y + e.pageY });
        }
      },

      disable: function () {
        data.disabled = true;
        data.popup.hide();
      },

      destroy: function () {
        el.off('.helptip .helptip-track');

        if (data.popup) {
          data.popup.remove();
          data.popup = null;
        }

        el.removeData('helptip');
        el = null;
      }
    };

    if (options.deferred) {
      if (options.delay) {
        el.on('mouseover.helptip-clearTitle', function () { $(this).off('mouseover.helptip-clearTitle')[0].title = ''; });
      }

      return methods.setEventHandlers({
        namespace: 'helptip-deferred',
        mouseMove: options['static'] ? $.noop : function (e) { data.coords = { x: e.pageX, y: e.pageY }; },
        mouseOut:  function ()  { el.off('.helptip-deferred'); },
        mouseIn:   function (e) {
          if (!data.coords) {
            data.coords = { x: e.pageX, y: e.pageY };
          }

          methods.init();
          methods.mouseIn();
        }
      });
    }

    // destroy any existing helptip instance if first argument is set false, else initialise
    methods[tip === false ? 'destroy' : 'init']();
  };

  $.fn.helptip = function (
    tip,    // text to be displayed on hover
    options // options - static, width (TODO)
  ) {
    if (typeof tip === 'object') {
      options = tip;
      tip     = '';
    }

    options = $.extend({ 'static': $('body').hasClass('ie6') }, options || {});
    return this.each(function () { $.helptip.call(this, tip, options); });
  };
})(jQuery);<commit_msg>rolling back one version<commit_after>/**
 * helptip - Displays a helptip on hovering an element
 * Usage:
 * el.helptip('show this on hover', { 'static': true }); // create a helptip (or modifies an existing one)
 * el.helptip('');                                       // hides a helptip
 * el.helptip(false);                                    // completely removes any existing helptip from the element
 **/
(function ($) {
  $.helptip = function (tip, options) {
    var el  = $(this);
    options = $.extend({
      container: 'body',
      eventIn:   'mouseenter',
      eventOut:  'mouseleave',
      delay:     100
    }, options);

    var data = { coords: false };

    if (typeof tip === 'undefined' || tip === '') {
      tip = this.title;
    }

    var methods = {
      init: function () {
        if (data.popup) {
          data.disabled = false; // if helptip already created
        } else {
          data.popup = $('

').appendTo(options.container);
        }

        tip = tip.replace(/\n/g, '
');

        if (el[0].title) {
          el[0].title = '';
        }

        if (tip) {
          methods.setTip(tip); // set tip

          if (options.show) {
            data.popup.show();
          }
        } else {
          methods.disable(el);
        }

        methods.setOption(); // set options

        if (!data.initiated) {
          methods.setEventHandlers(); // set event handlers
        }
      },

      setTip: function (html) {
        data.popup.children().html(html);
      },

      setOption: function () {
        data.options = options;

        if (data['class']) { // previously added class
          data.popup.removeClass(data['class']);
        }

        if (options['class']) {
          data.popup.addClass(options['class']);
          data['class'] = options['class'];
        }

        data.popup.toggleClass('helptip-static', options['static']);
      },

      setEventHandlers: function (args) {
        var handlers = {};
        var func;

        args = $.extend({
          namespace: 'helptip',
          mouseIn:   methods.mouseIn,
          mouseOut:  methods.mouseOut
        }, args);

        if (options.delay) {
          handlers = { over: args.mouseIn, out: args.mouseOut, interval: options.delay, namespace: args.namespace };
          func     = 'hoverIntent';

          el.on('mousemove.helptip-track', args.mouseMove || function (e) {
            if (!data.mousemove) {
              data.coords = { x: e.pageX, y: e.pageY };
            }
          });
        } else {
          handlers[options.eventIn  + '.' + args.namespace] = args.mouseIn;
          handlers[options.eventOut + '.' + args.namespace] = args.mouseOut;
          func = 'on';
        }

        el[func](handlers);

        if (args.namespace === 'helptip') {
          data.initiated = true;
        }
      },

      mouseIn: function (e) {
        var $window = $(window);
        var pos;

        if (options['static']) {
          var offset = options.container === 'body' ? el.offset() : el.position();
          pos = { x: offset.left + el.outerWidth() / 2, y: offset.top + el.outerHeight() / 2 };
        } else {
          pos = data.coords || { x: e.pageX, y: e.pageY };

          data.coords = false;

          if (!data.mousemove) {
            el.on('mousemove.helptip', methods.mouseMove);
            data.mousemove = true;
          }
        }

        var originalPos = $.extend(true, pos);

        data.popup.removeClass('helptip-left helptip-top');

        if ($window.height() < ((pos.y - $window.scrollTop()) * 2)) {
          data.popup.addClass('helptip-top');
          pos.y -= data.popup.height();
        }

        if ($window.width() < ((pos.x - $window.scrollLeft()) * 2)) {
          data.popup.addClass('helptip-left');
          pos.x -= (data.offset || {}).width || data.popup.width();
        }

        if (!data.disabled) {
          data.popup.css({ left: pos.x, top: pos.y }).show();
        }

        data.offset = { x: pos.x - originalPos.x, y: pos.y - originalPos.y, width: data.popup.width() };

        $window = null;
      },

      mouseOut: function () {
        data.popup.hide();
        el.off('mousemove.helptip');
        data.mousemove = false;
      },

      mouseMove: function (e) {
        if (!data.disabled) {
          var offset = data.offset;
          data.popup.css({ left: offset.x + e.pageX, top: offset.y + e.pageY });
        }
      },

      disable: function () {
        data.disabled = true;
        data.popup.hide();
      },

      destroy: function () {
        el.off('.helptip .helptip-track');

        if (data.popup) {
          data.popup.remove();
          data.popup = null;
        }

        el.removeData('helptip');
        el = null;
      }
    };

    if (options.deferred) {
      if (options.delay) {
        el.on('mouseover.helptip-clearTitle', function () { $(this).off('mouseover.helptip-clearTitle')[0].title = ''; });
      }

      return methods.setEventHandlers({
        namespace: 'helptip-deferred',
        mouseMove: function (e) { data.coords = { x: e.pageX, y: e.pageY }; },
        mouseOut:  function ()  { el.off('.helptip-deferred'); },
        mouseIn:   function (e) {
          if (!data.coords) {
            data.coords = { x: e.pageX, y: e.pageY };
          }

          methods.init();
          methods.mouseIn();
        }
      });
    }

    var elData = el.data('helptip');

    if (elData) {
      data = $.extend(elData, data);
    }

    el.data('helptip', data);

    // destroy any existing helptip instance if first argument is set false, else initialise
    methods[tip === false ? 'destroy' : 'init']();
  };

  $.fn.helptip = function (
    tip,    // text to be displayed on hover
    options // options - static, width (TODO)
  ) {
    if (typeof tip === 'object') {
      options = tip;
      tip     = '';
    }

    options = $.extend({ 'static': $('body').hasClass('ie6') }, options || {});
    return this.each(function () { $.helptip.call(this, tip, options); });
  };
})(jQuery);<|endoftext|>


<commit_before>var utils = require('../utils')

module.exports = function ($provide, traceBuffer) {
  return {
    instrumentAll: function (modules) {
      modules.forEach(function (name) {
        $provide.decorator(name, ['$delegate', '$injector', function ($delegate, $injector) {
          utils.instrumentObject($delegate, $injector, {
            type: 'angular.factory',
          prefix: 'app.factory.' + name
          })
          return $delegate
        }])
      })
    }
  }
}
<commit_msg>Fix wrong type and prefix for Angular Factory traces<commit_after>var utils = require('../utils')

module.exports = function ($provide, traceBuffer) {
  return {
    instrumentAll: function (modules) {
      modules.forEach(function (name) {
        $provide.decorator(name, ['$delegate', '$injector', function ($delegate, $injector) {
          utils.instrumentObject($delegate, $injector, {
            type: 'app.angular.factory',
            prefix: name
          })
          return $delegate
        }])
      })
    }
  }
}
<|endoftext|>


<commit_before>'use strict'

const path            = require('path')
const requireUncached = require('require-uncached')

/**
 * Loads and parses data for Nunjucks.
 * @public
 * @param {String} dataPath - Path to the data JSON.
 * @param {?Object} opts - Options.
 * @returns {Promise} Returns the following properties if resolved: {Object}.
 */
module.exports = function(dataPath, opts) {

	return new Promise((resolve, reject) => {

		const environment = (opts!=null && opts.optimize===true) ? 'prod' : 'dev'

		const globalData = (() => {

			const hasData = opts!=null && opts.data!=null
			if (hasData===false) return {}

			const mustRequire = typeof opts.data==='string'
			if (mustRequire===true) return requireUncached(opts.data)

			return opts.data

		})()

		const localData = (() => {

			const hasData = dataPath!=null
			if (hasData===false) return {}

			return requireUncached(dataPath)

		})()

		resolve(Object.assign({}, globalData, localData, {
			environment
		}))

	})

}<commit_msg>Fixed loading for relative data<commit_after>'use strict'

const path            = require('path')
const requireUncached = require('require-uncached')

/**
 * Loads and parses data for Nunjucks.
 * @public
 * @param {String} dataPath - Path to the data JSON.
 * @param {?Object} opts - Options.
 * @returns {Promise} Returns the following properties if resolved: {Object}.
 */
module.exports = function(dataPath, opts) {

	return new Promise((resolve, reject) => {

		const environment = (opts!=null && opts.optimize===true) ? 'prod' : 'dev'

		const globalData = (() => {

			const hasData = opts!=null && opts.data!=null
			if (hasData===false) return {}

			const mustRequire = typeof opts.data==='string'
			if (mustRequire===true) return requireUncached(path.resolve(opts.data))

			return opts.data

		})()

		const localData = (() => {

			const hasData = dataPath!=null
			if (hasData===false) return {}

			return requireUncached(dataPath)

		})()

		resolve(Object.assign({}, globalData, localData, {
			environment
		}))

	})

}<|endoftext|>


<commit_before>function StyleView(state) {

    var style = this;

    this.init = function() {

        this.model = new Style(state);

        this.template = Templates.Style;
        this.css = Templates.CSS;

        this.element = this.render();
        this.$ = this.element.querySelectorAll.bind(this.element)
        this.$$ = this.element.querySelector.bind(this.element);
        this.selector = this.$$('.selector');
        this.weights = this.$$('.select-weight');
        this.checkboxes = {
            active: this.$$('.toggle-active'),
            weight: this.$$('.toggle-weight'),
            color: this.$$('.toggle-color'),
            fontSize: this.$$('.toggle-font-size'),
            lineHeight: this.$$('.toggle-line-height')
        };

        this.rule = Stylesheet.newRule();
        this.fontMenu = new FontMenu(this);

        this.model.on('toggle', this.setCheckbox.bind(this));
        this.model.on('change:family', this.renderWeights.bind(this));
        this.model.on('change:selector change:active', this.setActive.bind(this));

        this.attachEvents();
        this.setActive();
    };

    this.events = {

        'click': {
            '.selector': function(event) {
                Picker.select(style);
            },

            '.font-menu': function(event) {
                this.fontMenu.click(event);
                event.stopPropagation();
            }
        },

        'mousedown': {
            '.selector': function(event) {
                Picker.preventFocus(event, style);
            }
        },

        'mouseover': {
            '.selector': function(event) {
                this.model.set({ highlight: true });
            },

            '.select-weight': function(event) {
                if (event.target.dataset.weight) {
                    this.model.set({ tempWeight: event.target.dataset.weight.toString() });
                }
            },

            '.font-menu': function(event) {

                if (this.fontMenu.open && tagName(event.target) === 'b') {
                    this.model.set({ tempWeight: event.target.textContent.toString() + '00' });
                }

                var family = getFamily(event.target, this.fontMenu.element);

                if (family !== this.model.state.tempFamily) {
                    this.model.set({ tempFamily: family });
                }
            }
        },

        'mouseout': {
            '.selector': function(event) {
                this.model.set({ highlight: false });
            },

            '.select-weight': function(event) {
                if (event.target.dataset.weight) {
                    this.model.set({ tempWeight: null });
                }
            },

            '.font-menu': function(event) {
                if (tagName(event.target) === 'b') {
                    this.model.set({ tempWeight: null });
                }
                if (!this.fontMenu.element.contains(event.relatedTarget)) {
                    this.model.set({ tempFamily: null });
                }
            }
        },

        'change': {
            '.toggle-active': function(event) {
                this.model.set({ active: !this.model.state.active });
            },

            '.toggle-weight': function(event) {
                this.model.toggle('weight');
            },

            '.toggle-color': function(event) {
                this.model.toggle('color');
            },

            '.toggle-font-size': function(event) {
                this.model.toggle('fontSize');
            },

            '.toggle-line-height': function(event) {
                this.model.toggle('lineHeight');
            },

            '.select-weight': function(event) {
                this.model.set({ weight: event.target.dataset.weight.toString() });
                this.model.enable('weight');
            }
        },

        'input': {
            '.selector': function(event) {
                this.model.set({ selector: event.target.value.toString().trim() });
            },

            '.search': function(event) {
                this.fontMenu.searchFonts(event.target.value);
            },

            '.change-color': function(event) {
                this.model.set({ color: event.target.value.toString() });
                this.model.enable('color');
            },

            '.change-font-size': function(event) {
                this.model.set({ fontSize: event.target.value.toString() + 'px' });
                this.model.enable('fontSize');
            },

            '.change-line-height': function(event) {
                this.model.set({ lineHeight: event.target.value.toString() });
                this.model.enable('lineHeight');
            },
        }
    };

    this.init();
}

_.extend(StyleView.prototype, {

    render: function() {
        var html = this.template(this.model.state);
        return elementFromHTML(html);
    },

    attachEvents: function() {
        if (!this.events) return;

        for (var event in this.events) {
            for (var selector in this.events[event]) {
                var elements = this.element.querySelectorAll(selector);
                for (var i = 0; i < elements.length; i++) {
                    elements[i].addEventListener(event, this.events[event][selector].bind(this), false);
                }
            }
        }
    },

    setActive: function(param) {
        if (param && this.model.state.active && this.model.state.selector) {
            this.element.classList.remove('inactive');
        } else {
            this.element.classList.add('inactive');
        }
    },

    setCheckbox: function(change) {
        changed = _.keys(change.enabled)[0];
        this.checkboxes[changed].checked = change.enabled[changed];
    },

    renderWeights: function() {
        var variants = Fonts.list[this.model.state.family].variants;
        var weights = getWeights(variants);
        this.weights.innerHTML = Templates.Weights({ weights: weights });
    }
});
<commit_msg>Whoops, searchFonts not renamed everywhere<commit_after>function StyleView(state) {

    var style = this;

    this.init = function() {

        this.model = new Style(state);

        this.template = Templates.Style;
        this.css = Templates.CSS;

        this.element = this.render();
        this.$ = this.element.querySelectorAll.bind(this.element)
        this.$$ = this.element.querySelector.bind(this.element);
        this.selector = this.$$('.selector');
        this.weights = this.$$('.select-weight');
        this.checkboxes = {
            active: this.$$('.toggle-active'),
            weight: this.$$('.toggle-weight'),
            color: this.$$('.toggle-color'),
            fontSize: this.$$('.toggle-font-size'),
            lineHeight: this.$$('.toggle-line-height')
        };

        this.rule = Stylesheet.newRule();
        this.fontMenu = new FontMenu(this);

        this.model.on('toggle', this.setCheckbox.bind(this));
        this.model.on('change:family', this.renderWeights.bind(this));
        this.model.on('change:selector change:active', this.setActive.bind(this));

        this.attachEvents();
        this.setActive();
    };

    this.events = {

        'click': {
            '.selector': function(event) {
                Picker.select(style);
            },

            '.font-menu': function(event) {
                this.fontMenu.click(event);
                event.stopPropagation();
            }
        },

        'mousedown': {
            '.selector': function(event) {
                Picker.preventFocus(event, style);
            }
        },

        'mouseover': {
            '.selector': function(event) {
                this.model.set({ highlight: true });
            },

            '.select-weight': function(event) {
                if (event.target.dataset.weight) {
                    this.model.set({ tempWeight: event.target.dataset.weight.toString() });
                }
            },

            '.font-menu': function(event) {

                if (this.fontMenu.open && tagName(event.target) === 'b') {
                    this.model.set({ tempWeight: event.target.textContent.toString() + '00' });
                }

                var family = getFamily(event.target, this.fontMenu.element);

                if (family !== this.model.state.tempFamily) {
                    this.model.set({ tempFamily: family });
                }
            }
        },

        'mouseout': {
            '.selector': function(event) {
                this.model.set({ highlight: false });
            },

            '.select-weight': function(event) {
                if (event.target.dataset.weight) {
                    this.model.set({ tempWeight: null });
                }
            },

            '.font-menu': function(event) {
                if (tagName(event.target) === 'b') {
                    this.model.set({ tempWeight: null });
                }
                if (!this.fontMenu.element.contains(event.relatedTarget)) {
                    this.model.set({ tempFamily: null });
                }
            }
        },

        'change': {
            '.toggle-active': function(event) {
                this.model.set({ active: !this.model.state.active });
            },

            '.toggle-weight': function(event) {
                this.model.toggle('weight');
            },

            '.toggle-color': function(event) {
                this.model.toggle('color');
            },

            '.toggle-font-size': function(event) {
                this.model.toggle('fontSize');
            },

            '.toggle-line-height': function(event) {
                this.model.toggle('lineHeight');
            },

            '.select-weight': function(event) {
                this.model.set({ weight: event.target.dataset.weight.toString() });
                this.model.enable('weight');
            }
        },

        'input': {
            '.selector': function(event) {
                this.model.set({ selector: event.target.value.toString().trim() });
            },

            '.search': function(event) {
                this.fontMenu.search(event.target.value);
            },

            '.change-color': function(event) {
                this.model.set({ color: event.target.value.toString() });
                this.model.enable('color');
            },

            '.change-font-size': function(event) {
                this.model.set({ fontSize: event.target.value.toString() + 'px' });
                this.model.enable('fontSize');
            },

            '.change-line-height': function(event) {
                this.model.set({ lineHeight: event.target.value.toString() });
                this.model.enable('lineHeight');
            },
        }
    };

    this.init();
}

_.extend(StyleView.prototype, {

    render: function() {
        var html = this.template(this.model.state);
        return elementFromHTML(html);
    },

    attachEvents: function() {
        if (!this.events) return;

        for (var event in this.events) {
            for (var selector in this.events[event]) {
                var elements = this.element.querySelectorAll(selector);
                for (var i = 0; i < elements.length; i++) {
                    elements[i].addEventListener(event, this.events[event][selector].bind(this), false);
                }
            }
        }
    },

    setActive: function(param) {
        if (param && this.model.state.active && this.model.state.selector) {
            this.element.classList.remove('inactive');
        } else {
            this.element.classList.add('inactive');
        }
    },

    setCheckbox: function(change) {
        changed = _.keys(change.enabled)[0];
        this.checkboxes[changed].checked = change.enabled[changed];
    },

    renderWeights: function() {
        var variants = Fonts.list[this.model.state.family].variants;
        var weights = getWeights(variants);
        this.weights.innerHTML = Templates.Weights({ weights: weights });
    }
});
<|endoftext|>


<commit_before>(function(){
    "use strict";

    var root = this,
        Chart = root.Chart,
        //Cache a local reference to Chart.helpers
        helpers = Chart.helpers;

    var defaultConfig = {
        //Number - Funnel top width as fraction of the chart conainer width
        widthTop: 0.75,
        //Number - Funnel bottom width as fraction of the chart conainer width
        widthBottom: 0.25,

        //Nunber - Height as fraction of chart container height
        height: 0.9,

        //String - Standard stroke colour for the funnel segments
        strokeColor: '#FFFFFF',

        //String - Standard fill colour for funnel segments
        fillColor: '#000000',

        //String - Standard fill colour for funnel segment labels
        labelColor: '#888888',

        //Number - Segment/Section stroke size
        strokeWidth: 1,

        //Boolean - Whether we should show each funnel segment as equal height
        equalHeight: true,

        //Number - Funnel type does not support animation
        animationSteps : 0,
    };

    Chart.Trapezoid = Chart.Element.extend({
        initialize: function() {
            // adjusted width when there are angles. These widths are euqal to
            // the base of the trapezoid
            this.xl = this.leftT > 0 ? this.height * Math.tan(this.leftT) : 0;
            this.xr = this.rightT > 0 ? this.height * Math.tan(this.rightT) : 0;
        },

        inRange: function(chartX, chartY) {
            return (chartX >= this.x + this.xl && chartX <= this.x + this.xl + this.width) && (chartY >= this.y && chartY <= this.y + this.height);
        },

        tooltipPosition : function(){
            return {
                x : this.x + ((this.xl + this.xr + this.width)/2),
                y : this.y + (this.height/2)
            };
        },
        update: function(newProps) {
            newProps.xl = this.leftT > 0 ? newProps.height * Math.tan(this.leftT) : 0;
            newProps.xr = this.rightT > 0 ? newProps.height * Math.tan(this.rightT) : 0;
            Chart.Element.prototype.update.call(this, newProps);
        },

        draw: function() {
            var halfStroke  = this.strokeWidth / 2,
                ctx         = this.ctx;

            ctx.save();

            ctx.fillStyle   = this.fillColor;
            ctx.strokeStyle = this.strokeColor;
            ctx.lineWidth   = halfStroke;

            ctx.translate(this.x, this.y + halfStroke);
            ctx.beginPath();
            ctx.moveTo(0 + halfStroke, 0)
            ctx.lineTo(this.xl + halfStroke, this.height - halfStroke);
            ctx.lineTo(this.xl + this.width, this.height - halfStroke);
            ctx.lineTo(this.xl + this.xr + this.width, 0);
            ctx.closePath();
            ctx.fill();
            ctx.stroke();
            if (this.segmentLabel) {
                ctx.fillStyle = this.labelColor;
                ctx.font = "10px sans-serif";
                ctx.textBaseline = "middle";
                ctx.textAlign = "left";
                ctx.fillText(this.segmentLabel, this.xl + this.xr + this.width, this.height/2);
            }
            ctx.restore();
        }
    });

    Chart.Type.extend({
        //Passing in a name registers this chart in the Chart namespace
        name: "Funnel",
        //Providing a defaults will also register the deafults in the chart namespace
        defaults : defaultConfig,
        //Initialize is fired when the chart is initialized - Data is passed in as a parameter
        //Config is automatically merged by the core of Chart.js, and is available at this.options
        initialize:  function(data){
            this.widthTop    = this.chart.width * this.options.widthTop;
            this.widthBottom = this.chart.width * this.options.widthBottom;
            this.height      = this.chart.height * this.options.height;
            this.originX     = 0;
            this.originY     = (this.chart.height - this.height) / 2;
            this.t           = Math.atan((this.widthTop - this.widthBottom)/(2 * this.height));
            this.tt          = Math.tan(this.t);

            this.segments    = [];

            if (this.options.showTooltips){
                helpers.bindEvents(this, this.options.tooltipEvents, function(evt) {
                    var activeSegments = (evt.type !== 'mouseout') ? this.getSegmentsAtEvent(evt) : [];
                    // var activeBars = this.segme;
                    this.showTooltip(activeSegments);
                });
            }

            this.wedgeClass = Chart.Trapezoid.extend({
                ctx: this.chart.ctx,
                fillColor: this.options.fillColor,
                strokeColor: this.options.strokeColor,
                strokeWidth: this.options.strokeWidth,
                labelColor: this.options.labelColor
            });

            // height for each segment when using equal height segments
            this.segmentHeight = this.height / data.length;

            // collated segment heights
            var segmentHeightTotal = 0;

            helpers.each(data, function(segment, segmentIndex) {
                var segmentWidth = this.getSegmentWidth(this.widthTop, this.tt, segmentHeightTotal, this.segmentHeight);

                if (segment.hasOwnProperty('sections')) {
                    var sectionValueTotal = 0;
                    var sectionWidthTotal = 0;
                    var sections          = [];

                    // individual section width will be a percent so we need to calculate
                    // the total value for all sections within this segment first
                    helpers.each(segment.sections, function(section) {
                        sectionValueTotal += section.value;
                    }, this);

                    helpers.each(segment.sections, function(section, sectionIndex) {
                        var sectionConfig = {
                            x: this.originX + sectionWidthTotal + this.getSegmentOffset(this.tt, segmentHeightTotal),
                            y: this.originY + segmentHeightTotal,
                            height: this.segmentHeight,
                            width: (segmentWidth / sectionValueTotal) * section.value,
                            leftT: 0,
                            rightT: 0,
                            fillColor: section.color,
                            segmentLabel: null,
                            label : section.label || null,
                            value : section.value,
                        };

                        if (sectionIndex === 0) {
                            sectionConfig.leftT = this.t;
                            // as there is an angle on left side of the trapezoid the renderer
                            // will add a wedge, we need to adjust for the wedge width as well
                            sectionWidthTotal += this.segmentHeight * this.tt;
                        }

                        if ((sectionIndex + 1) === segment.sections.length) {
                            sectionConfig.rightT = this.t;
                            sectionConfig.segmentLabel = segment.label;
                        }

                        sectionWidthTotal += sectionConfig.width;
                        sections.push(new this.wedgeClass(sectionConfig));
                    }, this);

                    this.segments.push(sections);
                } else {
                    this.segments.push(new this.wedgeClass({
                            x: this.originX + this.getSegmentOffset(this.tt, segmentHeightTotal),
                            y: this.originY + segmentHeightTotal,
                            height: this.segmentHeight,
                            width: this.getSegmentWidth(this.widthTop, this.tt, segmentHeightTotal, this.segmentHeight),
                            leftT: this.t,
                            rightT: this.t,
                            fillColor: segment.color,
                            segmentLabel: segment.label || null,
                            label : segment.label || null,
                            value : segment.value
                        })
                    )
                }

                segmentHeightTotal += this.segmentHeight;
            },this);
            this.render();
        },

        // calculates the total width of segment
        getSegmentWidth: function(chartWidth, tt, y, segmentHeight) {
            var totalHeight = y + segmentHeight;

            return chartWidth - ((totalHeight * tt) * 2);
        },

        getSegmentOffset: function(tt, y) {
            return y * tt;
        },

        getSegmentsAtEvent : function(e){
            var segmentsArray = [];
            var location = helpers.getRelativePosition(e);
            helpers.each(this.segments, function(segment) {
                if (Object.prototype.toString.call(segment) === '[object Array]') {
                    helpers.each(segment, function(section) {
                        if (section.inRange(location.x,location.y)) segmentsArray.push(section);
                    })
                } else {
                    if (segment.inRange(location.x,location.y)) segmentsArray.push(segment);
                }
            },this);

            return segmentsArray;
        },

        update : function(){

        },

        removeData: function(atIndex){

        },

        reflow : function(){
            this.widthTop    = this.chart.width * this.options.widthTop;
            this.widthBottom = this.chart.width * this.options.widthBottom;
            this.height      = this.chart.height * this.options.height;
            this.originX     = 0;
            this.originY     = (this.chart.height - this.height) / 2;

            // height for each segment when using equal height segments
            this.segmentHeight = this.height / this.segments.length;

            // collated segment heights
            var segmentHeightTotal = 0;

            helpers.each(this.segments, function(segment) {
                var segmentWidth = this.getSegmentWidth(this.widthTop, this.tt, segmentHeightTotal, this.segmentHeight);

                if (Object.prototype.toString.call(segment) === '[object Array]') {
                    var sectionValueTotal = 0;
                    var sectionWidthTotal = 0;

                    // individual section width will be a percent so we need to calculate
                    // the total value for all sections within this segment first
                    helpers.each(segment, function(section) {
                        sectionValueTotal += section.value;
                    });

                    helpers.each(segment, function(section) {
                        var sectionWidth = (segmentWidth / sectionValueTotal) * section.value;
                        section.update({
                            x: this.originX + sectionWidthTotal + this.getSegmentOffset(this.tt, segmentHeightTotal),
                            y: this.originY + segmentHeightTotal,
                            height: this.segmentHeight,
                            width: sectionWidth
                        });

                        if (section.leftT > 0) {
                            sectionWidthTotal += this.segmentHeight * this.tt;
                        }

                        sectionWidthTotal += sectionWidth;
                    }, this)
                } else {
                    segment.update({
                        x: this.originX + this.getSegmentOffset(this.tt, segmentHeightTotal),
                        y: this.originY + segmentHeightTotal,
                        height: this.segmentHeight,
                        width: this.getSegmentWidth(this.widthTop, this.tt, segmentHeightTotal, this.segmentHeight)
                    })
                }
                segmentHeightTotal += this.segmentHeight;
            }, this);
        },

        draw : function(easeDecimal){
            this.clear();
            helpers.each(this.segments, function(segment) {
                if (Object.prototype.toString.call(segment) === '[object Array]') {
                    helpers.each(segment, function(section) {
                        section.draw();
                    })
                } else {
                    segment.draw();
                }
            });
        }
    });
}).call(this);
<commit_msg>Removing the halfwidth adjustments on the stroke as the chart is not affected by lack of inner stroke.<commit_after>(function(){
    "use strict";

    var root = this,
        Chart = root.Chart,
        //Cache a local reference to Chart.helpers
        helpers = Chart.helpers;

    var defaultConfig = {
        //Number - Funnel top width as fraction of the chart conainer width
        widthTop: 0.75,
        //Number - Funnel bottom width as fraction of the chart conainer width
        widthBottom: 0.25,

        //Nunber - Height as fraction of chart container height
        height: 0.9,

        //String - Standard stroke colour for the funnel segments
        strokeColor: '#FFFFFF',

        //String - Standard fill colour for funnel segments
        fillColor: '#000000',

        //String - Standard fill colour for funnel segment labels
        labelColor: '#888888',

        //Number - Segment/Section stroke size
        strokeWidth: 1,

        //Boolean - Whether we should show each funnel segment as equal height
        equalHeight: true,

        //Number - Funnel type does not support animation
        animationSteps : 0,
    };

    Chart.Trapezoid = Chart.Element.extend({
        initialize: function() {
            // adjusted width when there are angles. These widths are euqal to
            // the base of the trapezoid
            this.xl = this.leftT > 0 ? this.height * Math.tan(this.leftT) : 0;
            this.xr = this.rightT > 0 ? this.height * Math.tan(this.rightT) : 0;
        },

        inRange: function(chartX, chartY) {
            return (chartX >= this.x + this.xl && chartX <= this.x + this.xl + this.width) && (chartY >= this.y && chartY <= this.y + this.height);
        },

        tooltipPosition : function(){
            return {
                x : this.x + ((this.xl + this.xr + this.width)/2),
                y : this.y + (this.height/2)
            };
        },
        update: function(newProps) {
            newProps.xl = this.leftT > 0 ? newProps.height * Math.tan(this.leftT) : 0;
            newProps.xr = this.rightT > 0 ? newProps.height * Math.tan(this.rightT) : 0;
            Chart.Element.prototype.update.call(this, newProps);
        },

        draw: function() {
            var ctx = this.ctx;

            ctx.save();

            ctx.fillStyle   = this.fillColor;
            ctx.strokeStyle = this.strokeColor;
            ctx.lineWidth   = this.strokeWidth;

            ctx.translate(this.x + 0.5, this.y + 0.5);
            ctx.beginPath();
            ctx.moveTo(0, 0)
            ctx.lineTo(this.xl, this.height);
            ctx.lineTo(this.xl + this.width, this.height);
            ctx.lineTo(this.xl + this.xr + this.width, 0);
            ctx.closePath();
            ctx.fill();
            this.strokeWidth && ctx.stroke();

            if (this.segmentLabel) {
                ctx.fillStyle = this.labelColor;
                ctx.font = "10px sans-serif";
                ctx.textBaseline = "middle";
                ctx.textAlign = "left";
                ctx.fillText(this.segmentLabel, this.xl + this.xr + this.width, this.height/2);
            }
            ctx.restore();
        }
    });

    Chart.Type.extend({
        //Passing in a name registers this chart in the Chart namespace
        name: "Funnel",
        //Providing a defaults will also register the deafults in the chart namespace
        defaults : defaultConfig,
        //Initialize is fired when the chart is initialized - Data is passed in as a parameter
        //Config is automatically merged by the core of Chart.js, and is available at this.options
        initialize:  function(data){
            this.widthTop    = this.chart.width * this.options.widthTop;
            this.widthBottom = this.chart.width * this.options.widthBottom;
            this.height      = this.chart.height * this.options.height;
            this.originX     = 0;
            this.originY     = (this.chart.height - this.height) / 2;
            this.t           = Math.atan((this.widthTop - this.widthBottom)/(2 * this.height));
            this.tt          = Math.tan(this.t);

            this.segments    = [];

            if (this.options.showTooltips){
                helpers.bindEvents(this, this.options.tooltipEvents, function(evt) {
                    var activeSegments = (evt.type !== 'mouseout') ? this.getSegmentsAtEvent(evt) : [];
                    // var activeBars = this.segme;
                    this.showTooltip(activeSegments);
                });
            }

            this.wedgeClass = Chart.Trapezoid.extend({
                ctx: this.chart.ctx,
                fillColor: this.options.fillColor,
                strokeColor: this.options.strokeColor,
                strokeWidth: this.options.strokeWidth,
                labelColor: this.options.labelColor
            });

            // height for each segment when using equal height segments
            this.segmentHeight = this.height / data.length;

            // collated segment heights
            var segmentHeightTotal = 0;

            helpers.each(data, function(segment, segmentIndex) {
                var segmentWidth = this.getSegmentWidth(this.widthTop, this.tt, segmentHeightTotal, this.segmentHeight);

                if (segment.hasOwnProperty('sections')) {
                    var sectionValueTotal = 0;
                    var sectionWidthTotal = 0;
                    var sections          = [];

                    // individual section width will be a percent so we need to calculate
                    // the total value for all sections within this segment first
                    helpers.each(segment.sections, function(section) {
                        sectionValueTotal += section.value;
                    }, this);

                    helpers.each(segment.sections, function(section, sectionIndex) {
                        var sectionConfig = {
                            x: this.originX + sectionWidthTotal + this.getSegmentOffset(this.tt, segmentHeightTotal),
                            y: this.originY + segmentHeightTotal,
                            height: this.segmentHeight,
                            width: (segmentWidth / sectionValueTotal) * section.value,
                            leftT: 0,
                            rightT: 0,
                            fillColor: section.color,
                            segmentLabel: null,
                            label : section.label || null,
                            value : section.value,
                        };

                        if (sectionIndex === 0) {
                            sectionConfig.leftT = this.t;
                            // as there is an angle on left side of the trapezoid the renderer
                            // will add a wedge, we need to adjust for the wedge width as well
                            sectionWidthTotal += this.segmentHeight * this.tt;
                        }

                        if ((sectionIndex + 1) === segment.sections.length) {
                            sectionConfig.rightT = this.t;
                            sectionConfig.segmentLabel = segment.label;
                        }

                        sectionWidthTotal += sectionConfig.width;
                        sections.push(new this.wedgeClass(sectionConfig));
                    }, this);

                    this.segments.push(sections);
                } else {
                    this.segments.push(new this.wedgeClass({
                            x: this.originX + this.getSegmentOffset(this.tt, segmentHeightTotal),
                            y: this.originY + segmentHeightTotal,
                            height: this.segmentHeight,
                            width: this.getSegmentWidth(this.widthTop, this.tt, segmentHeightTotal, this.segmentHeight),
                            leftT: this.t,
                            rightT: this.t,
                            fillColor: segment.color,
                            segmentLabel: segment.label || null,
                            label : segment.label || null,
                            value : segment.value
                        })
                    )
                }

                segmentHeightTotal += this.segmentHeight;
            },this);
            this.render();
        },

        // calculates the total width of segment
        getSegmentWidth: function(chartWidth, tt, y, segmentHeight) {
            var totalHeight = y + segmentHeight;

            return chartWidth - ((totalHeight * tt) * 2);
        },

        getSegmentOffset: function(tt, y) {
            return y * tt;
        },

        getSegmentsAtEvent : function(e){
            var segmentsArray = [];
            var location = helpers.getRelativePosition(e);
            helpers.each(this.segments, function(segment) {
                if (Object.prototype.toString.call(segment) === '[object Array]') {
                    helpers.each(segment, function(section) {
                        if (section.inRange(location.x,location.y)) segmentsArray.push(section);
                    })
                } else {
                    if (segment.inRange(location.x,location.y)) segmentsArray.push(segment);
                }
            },this);

            return segmentsArray;
        },

        update : function(){

        },

        removeData: function(atIndex){

        },

        reflow : function(){
            this.widthTop    = this.chart.width * this.options.widthTop;
            this.widthBottom = this.chart.width * this.options.widthBottom;
            this.height      = this.chart.height * this.options.height;
            this.originX     = 0;
            this.originY     = (this.chart.height - this.height) / 2;

            // height for each segment when using equal height segments
            this.segmentHeight = this.height / this.segments.length;

            // collated segment heights
            var segmentHeightTotal = 0;

            helpers.each(this.segments, function(segment) {
                var segmentWidth = this.getSegmentWidth(this.widthTop, this.tt, segmentHeightTotal, this.segmentHeight);

                if (Object.prototype.toString.call(segment) === '[object Array]') {
                    var sectionValueTotal = 0;
                    var sectionWidthTotal = 0;

                    // individual section width will be a percent so we need to calculate
                    // the total value for all sections within this segment first
                    helpers.each(segment, function(section) {
                        sectionValueTotal += section.value;
                    });

                    helpers.each(segment, function(section) {
                        var sectionWidth = (segmentWidth / sectionValueTotal) * section.value;
                        section.update({
                            x: this.originX + sectionWidthTotal + this.getSegmentOffset(this.tt, segmentHeightTotal),
                            y: this.originY + segmentHeightTotal,
                            height: this.segmentHeight,
                            width: sectionWidth
                        });

                        if (section.leftT > 0) {
                            sectionWidthTotal += this.segmentHeight * this.tt;
                        }

                        sectionWidthTotal += sectionWidth;
                    }, this)
                } else {
                    segment.update({
                        x: this.originX + this.getSegmentOffset(this.tt, segmentHeightTotal),
                        y: this.originY + segmentHeightTotal,
                        height: this.segmentHeight,
                        width: this.getSegmentWidth(this.widthTop, this.tt, segmentHeightTotal, this.segmentHeight)
                    })
                }
                segmentHeightTotal += this.segmentHeight;
            }, this);
        },

        draw : function(easeDecimal){
            this.clear();
            helpers.each(this.segments, function(segment) {
                if (Object.prototype.toString.call(segment) === '[object Array]') {
                    helpers.each(segment, function(section) {
                        section.draw();
                    })
                } else {
                    segment.draw();
                }
            });
        }
    });
}).call(this);
<|endoftext|>