<commit_before>'use strict';

var path = require('path'),
    FFmpeg = require('fluent-ffmpeg'),
    probe = require('node-ffprobe'),
    imageMagick = require('gm').subClass({imageMagick: true}),
    config = require('../../config/config'),
    fs = require('fs'),
    async = require('async'),
    mongoose = require('mongoose'),
    Asset = mongoose.model('Asset'),
    _ = require('lodash'),
    rest = require('../others/restware');

var sendResponse = function (res, err) {
    if (err) {
        return rest.sendError(res, 'Assets data queued for processing, but with errors: ', err);
    } else {
        return rest.sendSuccess(res, 'Queued for Processing');
    }
}

exports.storeDetails = function (req, res) {

    var errorMessages = [];

    var files = req.body.files,
        assetDir = config.mediaDir;

    var processFile = function (fileObj, array_cb) {
        var src = path.join(assetDir, fileObj.name),
            ext = path.extname(fileObj.name),
            destName = path.basename(fileObj.name, ext) + '_c.mp4',
            destPath = path.join(assetDir, destName),
            type,
            duration,
            resolution,
            thumbnail;

        async.series([
                function (task_cb) {
                    if (fileObj.name.match(config.imageRegex)) {
                        type = 'image';
                        async.series([
                            function (img_cb) {
                                imageMagick(src)
                                    .autoOrient()
                                    .resize(64)
                                    .write(path.join(config.thumbnailDir, fileObj.name), function (err) {
                                        console.log(' imageKick write done: ' + fileObj.name + ';error:' + err);
                                        if (err)
                                            errorMessages.push(err);
                                        thumbnail = "/media/_thumbnails/" + fileObj.name;
                                        img_cb();
                                    });
                            },
                            function (img_cb) {
                                imageMagick(src).size(function (err, value) {
                                    if (err)
                                        errorMessages.push(err);
                                    resolution = value;
                                    img_cb();
                                });
                            }
                        ], function () {
                            task_cb();
                        })
                    } else if (fileObj.name.match(config.videoRegex)) {
                        type = 'video';
                        var filePath = src;
                        async.series([
                            function (video_cb) {
                                probe(src, function (err, metadata) {
                                    console.dir(metadata);
                                    if (metadata && metadata.streams) {
                                        var vdoInfo = _.find(metadata.streams, {'codec_type': 'video'});
                                        var formatName;
                                        if (metadata.format)
                                            formatName = metadata.format.format_name;
                                        if ((vdoInfo && vdoInfo.codec_name != 'h264') || (formatName.indexOf('mp4') == -1)) {
                                            new FFmpeg({source: src})
                                                .audioCodec('libfdk_aac')
                                                .videoCodec('libx264')
                                                .size('?x720')
                                                .toFormat('mp4')
                                                .on('error', function (err, stdout, stderr) {
                                                    console.log('Conversion Err: ' + err);
                                                    console.log("stdout: " + stdout);
                                                    console.log("stderr: " + stderr);
                                                    errorMessages.push(err.message);
                                                    video_cb();
                                                })
                                                .on('end', function () {
                                                    fs.unlink(src, function (err) {
                                                        if (err) {
                                                            console.log('Conversion Src Unlink Err: ' + err);
                                                            errorMessages.push(err.message);
                                                        }
                                                        fileObj.name = destName;
                                                        filePath = destPath;
                                                        video_cb();
                                                    })
                                                })
                                                .saveToFile(destPath);
                                        } else {
                                            video_cb();
                                        }
                                    } else {
                                        video_cb();
                                    }
                                });
                            },
                            function (video_cb) {
                                probe(filePath, function (err, metadata) {
                                    if (metadata) {
                                        duration = metadata.format.duration;
                                        var vdoInfo = _.find(metadata.streams, {'codec_type': 'video'});
                                        if (vdoInfo) {
                                            resolution = {
                                                width: vdoInfo.width,
                                                height: vdoInfo.height
                                            }
                                        }
                                    }
                                    video_cb();
                                });
                            },
                            function (video_cb) {
                                new FFmpeg({source: filePath})
                                    .on('error', function (err) {
                                        console.log('ffmpeg, An error occurred: ' + err.message);
                                        errorMessages.push(err.message);
                                        video_cb()
                                    })
                                    .on('end', function (filenames) {
                                        thumbnail = "/media/_thumbnails/" + fileObj.name + '_1.png';
                                        video_cb()
                                    })
                                    .takeScreenshots({
                                        size: '64x64',
                                        count: 1,
                                        timemarks: ['10'],
                                        filename: fileObj.name + '.png'
                                    }, config.thumbnailDir);
                            }
                        ], function () {
                            task_cb();
                        })
                    } else {
                        if (fileObj.name.match(config.noticeRegex))
                            type = 'notice';
                        task_cb();
                    }
                }],
            function (err) {
                Asset.findOne({name: fileObj.name}, function (err, data) {
                    var asset,
                        object = {
                            name: fileObj.name,
                            type: type,
                            resolution: resolution,
                            duration: ~~duration,
                            size: ~~(fileObj.size / 1000) + ' KB',
                            labels: req.body.categories,
                            thumbnail: thumbnail,

                            createdBy: {_id: req.user._id, name: req.user.username}
                        };
                    if (object.duration == 10)      //hack for default avoidance
                        object.duration = 11;

                    if (err || !data) {
                        asset = new Asset(object);
                    } else {
                        asset = _.extend(data, object)
                    }
                    asset.save(function (err) {
                        if (err)
                            errorMessages = errorMessages.push(err);
                        array_cb();
                    })
                })
            }
        )
    }
    async.eachSeries(files, processFile, function () {
        if (errorMessages.length > 0) {
            console.log('store details(media processing) error messages: ')
            console.log(errorMessages.join());
        } else {
            console.log('Successfully processed files: ', files.join())
        }
    });
    sendResponse(res);
}
<commit_msg>removing user<commit_after>'use strict';

var path = require('path'),
    FFmpeg = require('fluent-ffmpeg'),
    probe = require('node-ffprobe'),
    imageMagick = require('gm').subClass({imageMagick: true}),
    config = require('../../config/config'),
    fs = require('fs'),
    async = require('async'),
    mongoose = require('mongoose'),
    Asset = mongoose.model('Asset'),
    _ = require('lodash'),
    rest = require('../others/restware');

var sendResponse = function (res, err) {
    if (err) {
        return rest.sendError(res, 'Assets data queued for processing, but with errors: ', err);
    } else {
        return rest.sendSuccess(res, 'Queued for Processing');
    }
}

exports.storeDetails = function (req, res) {

    var errorMessages = [];

    var files = req.body.files,
        assetDir = config.mediaDir;

    var processFile = function (fileObj, array_cb) {
        var src = path.join(assetDir, fileObj.name),
            ext = path.extname(fileObj.name),
            destName = path.basename(fileObj.name, ext) + '_c.mp4',
            destPath = path.join(assetDir, destName),
            type,
            duration,
            resolution,
            thumbnail;

        async.series([
                function (task_cb) {
                    if (fileObj.name.match(config.imageRegex)) {
                        type = 'image';
                        async.series([
                            function (img_cb) {
                                imageMagick(src)
                                    .autoOrient()
                                    .resize(64)
                                    .write(path.join(config.thumbnailDir, fileObj.name), function (err) {
                                        console.log(' imageKick write done: ' + fileObj.name + ';error:' + err);
                                        if (err)
                                            errorMessages.push(err);
                                        thumbnail = "/media/_thumbnails/" + fileObj.name;
                                        img_cb();
                                    });
                            },
                            function (img_cb) {
                                imageMagick(src).size(function (err, value) {
                                    if (err)
                                        errorMessages.push(err);
                                    resolution = value;
                                    img_cb();
                                });
                            }
                        ], function () {
                            task_cb();
                        })
                    } else if (fileObj.name.match(config.videoRegex)) {
                        type = 'video';
                        var filePath = src;
                        async.series([
                            function (video_cb) {
                                probe(src, function (err, metadata) {
                                    console.dir(metadata);
                                    if (metadata && metadata.streams) {
                                        var vdoInfo = _.find(metadata.streams, {'codec_type': 'video'});
                                        var formatName;
                                        if (metadata.format)
                                            formatName = metadata.format.format_name;
                                        if ((vdoInfo && vdoInfo.codec_name != 'h264') || (formatName.indexOf('mp4') == -1)) {
                                            new FFmpeg({source: src})
                                                .audioCodec('libfdk_aac')
                                                .videoCodec('libx264')
                                                .size('?x720')
                                                .toFormat('mp4')
                                                .on('error', function (err, stdout, stderr) {
                                                    console.log('Conversion Err: ' + err);
                                                    console.log("stdout: " + stdout);
                                                    console.log("stderr: " + stderr);
                                                    errorMessages.push(err.message);
                                                    video_cb();
                                                })
                                                .on('end', function () {
                                                    fs.unlink(src, function (err) {
                                                        if (err) {
                                                            console.log('Conversion Src Unlink Err: ' + err);
                                                            errorMessages.push(err.message);
                                                        }
                                                        fileObj.name = destName;
                                                        filePath = destPath;
                                                        video_cb();
                                                    })
                                                })
                                                .saveToFile(destPath);
                                        } else {
                                            video_cb();
                                        }
                                    } else {
                                        video_cb();
                                    }
                                });
                            },
                            function (video_cb) {
                                probe(filePath, function (err, metadata) {
                                    if (metadata) {
                                        duration = metadata.format.duration;
                                        var vdoInfo = _.find(metadata.streams, {'codec_type': 'video'});
                                        if (vdoInfo) {
                                            resolution = {
                                                width: vdoInfo.width,
                                                height: vdoInfo.height
                                            }
                                        }
                                    }
                                    video_cb();
                                });
                            },
                            function (video_cb) {
                                new FFmpeg({source: filePath})
                                    .on('error', function (err) {
                                        console.log('ffmpeg, An error occurred: ' + err.message);
                                        errorMessages.push(err.message);
                                        video_cb()
                                    })
                                    .on('end', function (filenames) {
                                        thumbnail = "/media/_thumbnails/" + fileObj.name + '_1.png';
                                        video_cb()
                                    })
                                    .takeScreenshots({
                                        size: '64x64',
                                        count: 1,
                                        timemarks: ['10'],
                                        filename: fileObj.name + '.png'
                                    }, config.thumbnailDir);
                            }
                        ], function () {
                            task_cb();
                        })
                    } else {
                        if (fileObj.name.match(config.noticeRegex))
                            type = 'notice';
                        task_cb();
                    }
                }],
            function (err) {
                Asset.findOne({name: fileObj.name}, function (err, data) {
                    var asset,
                        object = {
                            name: fileObj.name,
                            type: type,
                            resolution: resolution,
                            duration: ~~duration,
                            size: ~~(fileObj.size / 1000) + ' KB',
                            labels: req.body.categories,
                            thumbnail: thumbnail

                        };
                    if (object.duration == 10)      //hack for default avoidance
                        object.duration = 11;

                    if (err || !data) {
                        asset = new Asset(object);
                    } else {
                        asset = _.extend(data, object)
                    }
                    asset.save(function (err) {
                        if (err)
                            errorMessages = errorMessages.push(err);
                        array_cb();
                    })
                })
            }
        )
    }
    async.eachSeries(files, processFile, function () {
        if (errorMessages.length > 0) {
            console.log('store details(media processing) error messages: ')
            console.log(errorMessages.join());
        } else {
            console.log('Successfully processed files: ', files.join())
        }
    });
    sendResponse(res);
}
<|endoftext|>


<commit_before>const request = require('request');
const https = require('https');
const url = require('url');

var arbiterURL   = process.env.DATABOX_ARBITER_ENDPOINT;
var hostname     = process.env.DATABOX_LOCAL_NAME;
var arbiterToken = process.env.ARBITER_TOKEN;

// Modify component URLs if not dunning in Docker
if (!require('containerized')()) {
	console.warn("Warning: Running outside Docker; Databox hostnames will be replaced with 'localhost'");
	if (arbiterURL)
		arbiterURL = arbiterURL.replace(url.parse(arbiterURL).hostname, 'localhost');
}

// Configure HTTPS agent (as in stores)
const CM_HTTPS_CA_ROOT_CERT = process.env.CM_HTTPS_CA_ROOT_CERT;
var agentOptions = {};
if (!CM_HTTPS_CA_ROOT_CERT) {
	console.warn('Warning: No HTTPS root certficate provided so Databox HTTPS certificates will not be checked');
	agentOptions.rejectUnauthorized = false;
} else {
	agentOptions.ca = CM_HTTPS_CA_ROOT_CERT;
}
var httpsAgent = new https.Agent(agentOptions);

exports.makeArbiterRequest = function(method, path, json) {
	return new Promise((resolve, reject) => {
		request({
			method: method,
			url: arbiterURL + path,
			json: json,
			headers: { 'X-Api-Key': arbiterToken },
			agent: httpsAgent
		}, (err, res, body) => {
			if (err || res.statusCode !== 200) {
				reject(err || body);
				return;
			}
			resolve(body);
		});
	});
};

exports.makeStoreRequest = function() {
	var tokenCache = {};

	return (options) => {
		var route = {
			target: url.parse(options.url).hostname,
			path:   url.parse(options.url).pathname,
			method: options.method
		};
		var routeHash = JSON.stringify(route);

		return new Promise((resolve, reject) => {
			Promise.resolve().then(() => {
				if (!(routeHash in tokenCache))
					return exports.requestToken(route.target, route.path, route.method);
				return tokenCache[routeHash];
			}).then((token) => {
				options.agent = httpsAgent;
				options.headers = options.headers || {};
				options.headers['X-Api-Key'] = tokenCache[routeHash] = token;

				return new Promise((resolve, reject) => {
					request(options, (err, res, body) => {
						if (err || res.statusCode !== 200) {
							reject(err || body);
							return;
						}
						resolve(body);
					});
				});
			}).then((body) => {
				resolve(body)
			}).catch((err) => {
				// TODO: If request was rejected because a token is expired, remove it from tokenCache so it can be renewed
				reject(err)
			});
		});
	};
}();

exports.requestToken = function(hostname, endpoint, method) {
	return exports.makeArbiterRequest('POST', '/token', {
		target: hostname,
		path:   endpoint,
		method: method
	});
};

exports.waitForStoreStatus = function() {
	function retry(func, maxRetries, test) {
		test = test || (()=>{});
		let promise = Promise.reject();
		for (let i = 0; i < maxRetries; ++i)
			promise = promise.catch(() => func()).then(test);
		return promise;
	}

	return function(href, status, maxRetries) {
		maxRetries = maxRetries || 10;

		return new Promise((resolve, reject) => {
			retry(() => {
				return exports.makeStoreRequest({
					method: 'GET',
					url: href + '/status'
				});
			}, maxRetries, (body) => {
				if (body !== status)
					throw new Error('Status not ' + status);
			}).then(resolve).catch(reject);
		});
	};
}();
<commit_msg>Option.method can be undefined<commit_after>const request = require('request');
const https = require('https');
const url = require('url');

var arbiterURL   = process.env.DATABOX_ARBITER_ENDPOINT;
var hostname     = process.env.DATABOX_LOCAL_NAME;
var arbiterToken = process.env.ARBITER_TOKEN;

// Modify component URLs if not dunning in Docker
if (!require('containerized')()) {
	console.warn("Warning: Running outside Docker; Databox hostnames will be replaced with 'localhost'");
	if (arbiterURL)
		arbiterURL = arbiterURL.replace(url.parse(arbiterURL).hostname, 'localhost');
}

// Configure HTTPS agent (as in stores)
const CM_HTTPS_CA_ROOT_CERT = process.env.CM_HTTPS_CA_ROOT_CERT;
var agentOptions = {};
if (!CM_HTTPS_CA_ROOT_CERT) {
	console.warn('Warning: No HTTPS root certficate provided so Databox HTTPS certificates will not be checked');
	agentOptions.rejectUnauthorized = false;
} else {
	agentOptions.ca = CM_HTTPS_CA_ROOT_CERT;
}
var httpsAgent = new https.Agent(agentOptions);

exports.makeArbiterRequest = function(method, path, json) {
	return new Promise((resolve, reject) => {
		request({
			method: method,
			url: arbiterURL + path,
			json: json,
			headers: { 'X-Api-Key': arbiterToken },
			agent: httpsAgent
		}, (err, res, body) => {
			if (err || res.statusCode !== 200) {
				reject(err || body);
				return;
			}
			resolve(body);
		});
	});
};

exports.makeStoreRequest = function() {
	var tokenCache = {};

	return (options) => {
		var route = {
			target: url.parse(options.url).hostname,
			path:   url.parse(options.url).pathname,
			method: options.method || 'GET'
		};
		var routeHash = JSON.stringify(route);

		return new Promise((resolve, reject) => {
			Promise.resolve().then(() => {
				if (!(routeHash in tokenCache))
					return exports.requestToken(route.target, route.path, route.method);
				return tokenCache[routeHash];
			}).then((token) => {
				options.agent = httpsAgent;
				options.headers = options.headers || {};
				options.headers['X-Api-Key'] = tokenCache[routeHash] = token;

				return new Promise((resolve, reject) => {
					request(options, (err, res, body) => {
						if (err || res.statusCode !== 200) {
							reject(err || body);
							return;
						}
						resolve(body);
					});
				});
			}).then((body) => {
				resolve(body)
			}).catch((err) => {
				// TODO: If request was rejected because a token is expired, remove it from tokenCache so it can be renewed
				reject(err)
			});
		});
	};
}();

exports.requestToken = function(hostname, endpoint, method) {
	return exports.makeArbiterRequest('POST', '/token', {
		target: hostname,
		path:   endpoint,
		method: method
	});
};

exports.waitForStoreStatus = function() {
	function retry(func, maxRetries, test) {
		test = test || (()=>{});
		let promise = Promise.reject();
		for (let i = 0; i < maxRetries; ++i)
			promise = promise.catch(() => func()).then(test);
		return promise;
	}

	return function(href, status, maxRetries) {
		maxRetries = maxRetries || 10;

		return new Promise((resolve, reject) => {
			retry(() => {
				return exports.makeStoreRequest({
					method: 'GET',
					url: href + '/status'
				});
			}, maxRetries, (body) => {
				if (body !== status)
					throw new Error('Status not ' + status);
			}).then(resolve).catch(reject);
		});
	};
}();
<|endoftext|>


<commit_before>
function contentLink(contentItem) {
  const entryId = contentItem.entryId;
  const category = contentItem.channelName;

  switch(category) {
    case "series_newspring":
      return `/series/${entryId}`
    case "sermons":
      const seriesId = contentItem.parent && contentItem.parent.entryId;
      return `/series/${seriesId}/sermon/${entryId}`
    case "devotionals":
      return `/devotions/${entryId}`
    case "newspring_albums":
      return `/music/${entryId}`
    case "articles":
      return `/articles/${entryId}`
    case "stories":
      return `/stories/${entryId}`

  }
}

export default contentLink
<commit_msg>use id when generating links if no entryId<commit_after>
function contentLink(contentItem) {
  const entryId = contentItem.entryId || contentItem.id;
  const category = contentItem.channelName;

  switch(category) {
    case "series_newspring":
      return `/series/${entryId}`
    case "sermons":
      const seriesId = contentItem.parent && contentItem.parent.entryId;
      return `/series/${seriesId}/sermon/${entryId}`
    case "devotionals":
      return `/devotions/${entryId}`
    case "newspring_albums":
      return `/music/${entryId}`
    case "articles":
      return `/articles/${entryId}`
    case "stories":
      return `/stories/${entryId}`

  }
}

export default contentLink
<|endoftext|>


<commit_before><commit_msg>Fix: Fixed issue with data importer - double saved import and without history<commit_after><|endoftext|>


<commit_before>/*
Flot plugin for selecting times in a flight

The plugin defines the following options:

  selection: {
    mode: null or "x",
    color: color
  }

Selection support is enabled by setting the mode to "x". The user will
only be able to specify the x range. "color" is color of the selection
(if you need to change the color later on, you can get to it with
plot.getOptions().selection.color).

When selection support is enabled, a "plotselected" event will be
emitted on the DOM element you passed into the plot function. The
event handler gets a parameter with the ranges selected on the axes,
like this:

  placeholder.bind("plotselected", function(event, ranges) {
    alert("You selected " + ranges.xaxis.from + " to " + ranges.xaxis.to)
    // with multiple axes, the extra ones are in x2axis, x3axis, ...
  });

The "plotselected" event is only fired when the user has finished
making the selection. A "plotselecting" event is fired during the
process with the same parameters as the "plotselected" event, in case
you want to know what's happening while it's happening,

A "plotunselected" event with no arguments is emitted when the user
clicks the mouse to remove the selection.

The plugin allso adds the following methods to the plot object:

- setSelection(ranges, preventEvent)

  Set the selection range. The passed in ranges is on the same
  form as returned in the "plotselected" event, like this:

    setSelection({ xaxis: { from: 0, to: 10 } });

  setSelection will trigger the "plotselected" event when called. If
  you don't want that to happen, e.g. if you're inside a
  "plotselected" handler, pass true as the second parameter. If you
  are using multiple axes, you can specify the ranges on any of those,
  e.g. as x2axis/x3axis/... instead of xaxis, the plugin picks the
  first one it sees.

- clearSelection(preventEvent)

  Clear the selection rectangle. Pass in true to avoid getting a
  "plotunselected" event.

- getSelection()

  Returns the current selection in the same format as the
  "plotselected" event. If there's currently no selection, the
  function returns null.

*/

(function($) {
  function init(plot) {
    var selection = {
      first: { x: -1}, second: { x: -1},
      show: false,
      active: false
    };

    // FIXME: The drag handling implemented here should be
    // abstracted out, there's some similar code from a library in
    // the navigation plugin, this should be massaged a bit to fit
    // the Flot cases here better and reused. Doing this would
    // make this plugin much slimmer.
    var savedhandlers = {};

    var mouseUpHandler = null;

    function onMouseMove(e) {
      if (selection.active) {
        updateSelection(e);

        plot.getPlaceholder().trigger('plotselecting', [getSelection()]);
      }
    }

    function onMouseDown(e) {
      if (e.which != 1)  // only accept left-click
        return;

      // cancel out any text selections
      document.body.focus();

      // prevent text selection and drag in old-school browsers
      if (document.onselectstart !== undefined && savedhandlers.onselectstart == null) {
        savedhandlers.onselectstart = document.onselectstart;
        document.onselectstart = function() { return false; };
      }
      if (document.ondrag !== undefined && savedhandlers.ondrag == null) {
        savedhandlers.ondrag = document.ondrag;
        document.ondrag = function() { return false; };
      }

      setSelectionPos(selection.first, e);

      selection.active = true;

      // this is a bit silly, but we have to use a closure to be
      // able to whack the same handler again
      mouseUpHandler = function(e) { onMouseUp(e); };

      $(document).one('mouseup', mouseUpHandler);
    }

    function onMouseUp(e) {
      mouseUpHandler = null;

      // revert drag stuff for old-school browsers
      if (document.onselectstart !== undefined)
        document.onselectstart = savedhandlers.onselectstart;
      if (document.ondrag !== undefined)
        document.ondrag = savedhandlers.ondrag;

      // no more dragging
      selection.active = false;
      updateSelection(e);

      triggerSelectedEvent();

      return false;
    }

    function getSelection() {

      var r = {}, c1 = selection.first, c2 = selection.second;
      $.each(plot.getAxes(), function(name, axis) {
        if (axis.used) {
          var p1 = axis.c2p(c1[axis.direction]), p2 = axis.c2p(c2[axis.direction]);
          r[name] = { from: Math.min(p1, p2), to: Math.max(p1, p2) };
        }
      });
      return r;
    }

    function triggerSelectedEvent() {
      var r = getSelection();

      plot.getPlaceholder().trigger('plotselected', [r]);

      // backwards-compat stuff, to be removed in future
      if (r.xaxis && r.yaxis)
        plot.getPlaceholder().trigger('selected', [{ x1: r.xaxis.from, x2: r.xaxis.to }]);
    }

    function clamp(min, value, max) {
      return value < min ? min : (value > max ? max : value);
    }

    function setSelectionPos(pos, e) {
      var o = plot.getOptions();
      var offset = plot.getPlaceholder().offset();
      var plotOffset = plot.getPlotOffset();
      pos.x = clamp(0, e.pageX - offset.left - plotOffset.left, plot.width());

    }

    function updateSelection(pos) {
      if (pos.pageX == null)
        return;

      setSelectionPos(selection.second, pos);
      selection.show = true;
      plot.triggerRedrawOverlay();
    }

    function clearSelection(preventEvent) {
      if (selection.show) {
        selection.show = false;
        plot.triggerRedrawOverlay();
        if (!preventEvent)
          plot.getPlaceholder().trigger('plotunselected', []);
      }
    }

    // function taken from markings support in Flot
    function extractRange(ranges, coord) {
      var axis, from, to, key, axes = plot.getAxes();

      for (var k in axes) {
        axis = axes[k];
        if (axis.direction == coord) {
          key = coord + axis.n + 'axis';
          if (!ranges[key] && axis.n == 1)
            key = coord + 'axis'; // support x1axis as xaxis
          if (ranges[key]) {
            from = ranges[key].from;
            to = ranges[key].to;
            break;
          }
        }
      }

      // backwards-compat stuff - to be removed in future
      if (!ranges[key]) {
        axis = coord == 'x' ? plot.getXAxes()[0] : plot.getYAxes()[0];
        from = ranges[coord + '1'];
        to = ranges[coord + '2'];
      }

      // auto-reverse as an added bonus
      if (from != null && to != null && from > to) {
        var tmp = from;
        from = to;
        to = tmp;
      }

      return { from: from, to: to, axis: axis };
    }

    function setSelection(ranges, preventEvent) {
      var axis, range, o = plot.getOptions();

      range = extractRange(ranges, 'x');

      selection.first.x = range.axis.p2c(range.from);
      selection.second.x = range.axis.p2c(range.to);

      selection.show = true;
      plot.triggerRedrawOverlay();
      if (!preventEvent)
        triggerSelectedEvent();
    }

    plot.clearSelection = clearSelection;
    plot.setSelection = setSelection;
    plot.getSelection = getSelection;

    plot.hooks.bindEvents.push(function(plot, eventHolder) {
      var o = plot.getOptions();
      if (o.selection.mode != null) {
        eventHolder.mousemove(onMouseMove);
        eventHolder.mousedown(onMouseDown);
      }
    });

    plot.hooks.drawOverlay.push(function(plot, ctx) {
      // draw selection
      if (selection.show) {
        var plotOffset = plot.getPlotOffset();
        var o = plot.getOptions();

        ctx.save();
        ctx.translate(plotOffset.left, plotOffset.top);

        var c = $.color.parse(o.selection.color);

        ctx.strokeStyle = c.scale('a', 0.8).toString();
        ctx.lineWidth = 1;
        ctx.lineJoin = 'round';
        ctx.fillStyle = c.scale('a', 0.4).toString();

        var y = 0,
            h = plot.height();

        var w_left = Math.min(selection.first.x, selection.second.x);

        ctx.fillRect(0, y, w_left, h);
        ctx.strokeRect(0, y, w_left, h);

        var x_right = Math.max(selection.first.x, selection.second.x);

        ctx.fillRect(x_right, y, plot.width() - x_right, h);
        ctx.strokeRect(x_right, y, plot.width() - x_right, h);

        ctx.restore();
      }
    });

    plot.hooks.shutdown.push(function(plot, eventHolder) {
      eventHolder.unbind('mousemove', onMouseMove);
      eventHolder.unbind('mousedown', onMouseDown);

      if (mouseUpHandler)
        $(document).unbind('mouseup', mouseUpHandler);
    });

  }

  $.plot.plugins.push({
    init: init,
    options: {
      selection: {
        mode: null, // one of null, "x"
        color: '#777777'
      }
    },
    name: 'flight-upload',
    version: '0.9'
  });
})(jQuery);
<commit_msg>flight-upload.js: change selection only when dragging the takeoff/landing markers<commit_after>/*
Flot plugin for selecting times in a flight

The plugin defines the following options:

  selection: {
    mode: null or "x",
    color: color
  }

Selection support is enabled by setting the mode to "x". The user will
only be able to specify the x range. "color" is color of the selection
(if you need to change the color later on, you can get to it with
plot.getOptions().selection.color).

When selection support is enabled, a "plotselected" event will be
emitted on the DOM element you passed into the plot function. The
event handler gets a parameter with the ranges selected on the axes,
like this:

  placeholder.bind("plotselected", function(event, ranges) {
    alert("You selected " + ranges.xaxis.from + " to " + ranges.xaxis.to)
    // with multiple axes, the extra ones are in x2axis, x3axis, ...
  });

The "plotselected" event is only fired when the user has finished
making the selection. A "plotselecting" event is fired during the
process with the same parameters as the "plotselected" event, in case
you want to know what's happening while it's happening,

A "plotunselected" event with no arguments is emitted when the user
clicks the mouse to remove the selection.

The plugin allso adds the following methods to the plot object:

- setSelection(ranges, preventEvent)

  Set the selection range. The passed in ranges is on the same
  form as returned in the "plotselected" event, like this:

    setSelection({ xaxis: { from: 0, to: 10 } });

  setSelection will trigger the "plotselected" event when called. If
  you don't want that to happen, e.g. if you're inside a
  "plotselected" handler, pass true as the second parameter. If you
  are using multiple axes, you can specify the ranges on any of those,
  e.g. as x2axis/x3axis/... instead of xaxis, the plugin picks the
  first one it sees.

- clearSelection(preventEvent)

  Clear the selection rectangle. Pass in true to avoid getting a
  "plotunselected" event.

- getSelection()

  Returns the current selection in the same format as the
  "plotselected" event. If there's currently no selection, the
  function returns null.

*/

(function($) {
  function init(plot) {
    var selection = {
      first: { x: -1}, second: { x: -1},
      show: false,
      active: false
    };

    // FIXME: The drag handling implemented here should be
    // abstracted out, there's some similar code from a library in
    // the navigation plugin, this should be massaged a bit to fit
    // the Flot cases here better and reused. Doing this would
    // make this plugin much slimmer.
    var savedhandlers = {};

    var mouseUpHandler = null;

    function onMouseMove(e) {
      if (selection.active) {
        updateSelection(e);

        plot.getPlaceholder().trigger('plotselecting', [getSelection()]);
      }
    }

    function onMouseDown(e) {
      if (e.which != 1)  // only accept left-click
        return;

      // cancel out any text selections
      document.body.focus();

      // prevent text selection and drag in old-school browsers
      if (document.onselectstart !== undefined && savedhandlers.onselectstart == null) {
        savedhandlers.onselectstart = document.onselectstart;
        document.onselectstart = function() { return false; };
      }
      if (document.ondrag !== undefined && savedhandlers.ondrag == null) {
        savedhandlers.ondrag = document.ondrag;
        document.ondrag = function() { return false; };
      }

      var selected_marker = getMarker(e);

      if (selected_marker == null)
        return;
      else if (selected_marker == 'first')
        setSelectionPos(selection.first, e);
      else
        setSelectionPos(selection.second, e);

      selection.active = selected_marker;

      // this is a bit silly, but we have to use a closure to be
      // able to whack the same handler again
      mouseUpHandler = function(e) { onMouseUp(e); };

      $(document).one('mouseup', mouseUpHandler);
    }

    function onMouseUp(e) {
      mouseUpHandler = null;

      // revert drag stuff for old-school browsers
      if (document.onselectstart !== undefined)
        document.onselectstart = savedhandlers.onselectstart;
      if (document.ondrag !== undefined)
        document.ondrag = savedhandlers.ondrag;

      // no more dragging
      selection.active = null;
      updateSelection(e);

      triggerSelectedEvent();

      return false;
    }

    function getMarker(e) {
      var o = plot.getOptions();
      var offset = plot.getPlaceholder().offset();
      var plotOffset = plot.getPlotOffset();
      var pos = clamp(0, e.pageX - offset.left - plotOffset.left, plot.width());

      var dist_to_first = Math.abs(selection.first.x - pos),
          dist_to_second = Math.abs(selection.second.x - pos);

      if (dist_to_first <= dist_to_second && dist_to_first < 10)
        return 'first';
      else if (dist_to_first > dist_to_second && dist_to_second < 10)
        return 'second';
      else
        return null;
    }

    function getSelection() {
      var r = {}, c1 = selection.first, c2 = selection.second;
      $.each(plot.getAxes(), function(name, axis) {
        if (axis.used) {
          var p1 = axis.c2p(c1[axis.direction]), p2 = axis.c2p(c2[axis.direction]);
          r[name] = { from: Math.min(p1, p2), to: Math.max(p1, p2) };
        }
      });
      return r;
    }

    function triggerSelectedEvent() {
      var r = getSelection();

      plot.getPlaceholder().trigger('plotselected', [r]);

      // backwards-compat stuff, to be removed in future
      if (r.xaxis && r.yaxis)
        plot.getPlaceholder().trigger('selected', [{ x1: r.xaxis.from, x2: r.xaxis.to }]);
    }

    function clamp(min, value, max) {
      return value < min ? min : (value > max ? max : value);
    }

    function setSelectionPos(pos, e) {
      var o = plot.getOptions();
      var offset = plot.getPlaceholder().offset();
      var plotOffset = plot.getPlotOffset();
      pos.x = clamp(0, e.pageX - offset.left - plotOffset.left, plot.width());

    }

    function updateSelection(pos) {
      if (pos.pageX == null)
        return;

      if (selection.active == 'first')
        setSelectionPos(selection.first, pos);
      else if (selection.active == 'second')
        setSelectionPos(selection.second, pos);

      selection.show = true;
      plot.triggerRedrawOverlay();
    }

    function clearSelection(preventEvent) {
      if (selection.show) {
        selection.show = false;
        plot.triggerRedrawOverlay();
        if (!preventEvent)
          plot.getPlaceholder().trigger('plotunselected', []);
      }
    }

    // function taken from markings support in Flot
    function extractRange(ranges, coord) {
      var axis, from, to, key, axes = plot.getAxes();

      for (var k in axes) {
        axis = axes[k];
        if (axis.direction == coord) {
          key = coord + axis.n + 'axis';
          if (!ranges[key] && axis.n == 1)
            key = coord + 'axis'; // support x1axis as xaxis
          if (ranges[key]) {
            from = ranges[key].from;
            to = ranges[key].to;
            break;
          }
        }
      }

      // backwards-compat stuff - to be removed in future
      if (!ranges[key]) {
        axis = coord == 'x' ? plot.getXAxes()[0] : plot.getYAxes()[0];
        from = ranges[coord + '1'];
        to = ranges[coord + '2'];
      }

      // auto-reverse as an added bonus
      if (from != null && to != null && from > to) {
        var tmp = from;
        from = to;
        to = tmp;
      }

      return { from: from, to: to, axis: axis };
    }

    function setSelection(ranges, preventEvent) {
      var axis, range, o = plot.getOptions();

      range = extractRange(ranges, 'x');

      selection.first.x = range.axis.p2c(range.from);
      selection.second.x = range.axis.p2c(range.to);

      selection.show = true;
      plot.triggerRedrawOverlay();
      if (!preventEvent)
        triggerSelectedEvent();
    }

    plot.clearSelection = clearSelection;
    plot.setSelection = setSelection;
    plot.getSelection = getSelection;

    plot.hooks.bindEvents.push(function(plot, eventHolder) {
      var o = plot.getOptions();
      if (o.selection.mode != null) {
        eventHolder.mousemove(onMouseMove);
        eventHolder.mousedown(onMouseDown);
      }
    });

    plot.hooks.drawOverlay.push(function(plot, ctx) {
      // draw selection
      if (selection.show) {
        var plotOffset = plot.getPlotOffset();
        var o = plot.getOptions();

        ctx.save();
        ctx.translate(plotOffset.left, plotOffset.top);

        var c = $.color.parse(o.selection.color);

        ctx.strokeStyle = c.scale('a', 0.8).toString();
        ctx.lineWidth = 1;
        ctx.lineJoin = 'round';
        ctx.fillStyle = c.scale('a', 0.4).toString();

        var y = 0,
            h = plot.height();

        var w_left = Math.min(selection.first.x, selection.second.x);

        ctx.fillRect(0, y, w_left, h);
        ctx.strokeRect(0, y, w_left, h);

        var x_right = Math.max(selection.first.x, selection.second.x);

        ctx.fillRect(x_right, y, plot.width() - x_right, h);
        ctx.strokeRect(x_right, y, plot.width() - x_right, h);

        ctx.restore();
      }
    });

    plot.hooks.shutdown.push(function(plot, eventHolder) {
      eventHolder.unbind('mousemove', onMouseMove);
      eventHolder.unbind('mousedown', onMouseDown);

      if (mouseUpHandler)
        $(document).unbind('mouseup', mouseUpHandler);
    });

  }

  $.plot.plugins.push({
    init: init,
    options: {
      selection: {
        mode: null, // one of null, "x"
        color: '#777777'
      }
    },
    name: 'flight-upload',
    version: '0.9'
  });
})(jQuery);
<|endoftext|>


<commit_before><commit_msg>refactoring service<commit_after><|endoftext|>


<commit_before>var domains = ['msn.com', 'bellsouth.net', 'telus.net', 'comcast.net', 'optusnet.com.au', 'earthlink.net', 'qq.com',
                'sky.com', 'icloud.com', 'mac.com', 'sympatico.ca', 'googlemail.com', 'att.net', 'xtra.co.nz', 'web.de',
                'cox.net', 'gmail.com', 'ymail.com', 'aim.com', 'rogers.com', 'verizon.net', 'rocketmail.com',
                'google.com', 'optonline.net', 'sbcglobal.net', 'aol.com', 'me.com', 'btinternet.com', 'charter.net',
                'shaw.ca', 'xplore.com', 'abnorth.com', 'pentnet.net', 'canadasurf.net'];

var secondLevelDomains = ["yahoo", "hotmail", "mail", "live", "outlook", "gmx"];

var $email = $('[type="email"]');
var $hint = $("*#email_hint");

$email.on('blur', function() {
  $(this).mailcheck({
    domains: domains,                       // optional
    secondLevelDomains: secondLevelDomains, // optional
    suggested: function(element, suggestion) {
      var text = "Did you mean " +
        "" + suggestion.address + "" +
        "@" + suggestion.domain +
        "?";

      element.parent().siblings("#email_hint").html(text).fadeIn(150);
    },
    empty: function(element) {
      element.parent().siblings("#email_hint").html("");
    }
  });
});

$hint.on('click', '.email_domain', function() {
  email_hint = $(this).parents("#email_hint");
  email = $(email_hint).siblings(".input.email").find("[type='email']")

  $(email).val($(".suggestion").first().text());

  $(email_hint).fadeOut(200, function() {
    $(this).empty();
  });
  return false;
});
<commit_msg>Getting rid of second level domains since they are the same as default.<commit_after>var domains = ['msn.com', 'bellsouth.net', 'telus.net', 'comcast.net', 'optusnet.com.au', 'earthlink.net', 'qq.com',
                'sky.com', 'icloud.com', 'mac.com', 'sympatico.ca', 'googlemail.com', 'att.net', 'xtra.co.nz', 'web.de',
                'cox.net', 'gmail.com', 'ymail.com', 'aim.com', 'rogers.com', 'verizon.net', 'rocketmail.com',
                'google.com', 'optonline.net', 'sbcglobal.net', 'aol.com', 'me.com', 'btinternet.com', 'charter.net',
                'shaw.ca', 'xplore.com', 'abnorth.com', 'pentnet.net', 'canadasurf.net'];

var $email = $('[type="email"]');
var $hint = $("*#email_hint");

$email.on('blur', function() {
  $(this).mailcheck({
    domains: domains,
    suggested: function(element, suggestion) {
      var text = "Did you mean " +
        "" + suggestion.address + "" +
        "@" + suggestion.domain +
        "?";

      element.parent().siblings("#email_hint").html(text).fadeIn(150);
    },
    empty: function(element) {
      element.parent().siblings("#email_hint").html("");
    }
  });
});

$hint.on('click', '.email_domain', function() {
  email_hint = $(this).parents("#email_hint");
  email = $(email_hint).siblings(".input.email").find("[type='email']")

  $(email).val($(".suggestion").first().text());

  $(email_hint).fadeOut(200, function() {
    $(this).empty();
  });
  return false;
});
<|endoftext|>


<commit_before>'use strict';

var plugin = require('./plugins');
var utils = require('./utils');
var Base = require('./base');

/**
 * Expose `Views`
 */

module.exports = Views;

/**
 * Create an instance of `Views` with the given `options`.
 *
 * ```js
 * var collection = new Views();
 * collection.addView('foo', {content: 'bar'});
 * ```
 * @param {Object} `options`
 * @api public
 */

function Views(options) {
  if (!(this instanceof Views)) {
    return new Views(options);
  }
  Base.call(this);
  this.init(options || {});
}

/**
 * Inherit `Base`
 */

Base.extend(Views);

/**
 * Mixin prototype methods
 */

plugin.routes(Views.prototype);
plugin.engine(Views.prototype);
plugin.layout(Views.prototype);
plugin.render(Views.prototype);
plugin.lookup(Views.prototype);
plugin.errors(Views.prototype, 'Views');

/**
 * Initialize `Views` defaults
 */

Views.prototype.init = function(opts) {
  this.define('isCollection', true);
  this.define('isViews', true);

  // decorate the instance
  this.use(plugin.init);
  this.use(plugin.renameKey());
  this.use(plugin.context);
  this.use(plugin.helpers);
  this.use(plugin.item('view', 'View'));

  // add constructors
  this.define('List', opts.List || require('./list'));
  this.define('View', opts.View || require('./view'));
  this.define('loaded', false);

  this.queue = [];
  this.views = {};

  // if an instance of `List` of `Views` is passed, load it now
  if (Array.isArray(opts) || opts.isList) {
    this.options = opts.options;
    this.addList(opts.items);

  } else if (opts.isCollection) {
    this.options = opts.options;
    this.addViews(opts.views);

  } else {
    this.options = opts;
  }
};

/**
 * Set a view on the collection. This is identical to [addView](#addView)
 * except `setView` does not emit an event for each view.
 *
 * ```js
 * collection.setView('foo', {content: 'bar'});
 * ```
 *
 * @param {String|Object} `key` View key or object
 * @param {Object} `value` If key is a string, value is the view object.
 * @developer This method is decorated onto the collection in the constructor using the `createView` utility method.
 * @return {Object} returns the `view` instance.
 * @api public
 */

Views.prototype.setView = function(key, value) {
  var view = this.view(key, value);
  this.views[view.key] = view;
  return view;
};

/**
 * Similar to [setView](#setView), adds a view to the collection
 * but also fires an event and iterates over the loading `queue`
 * for loading views from the `addView` event listener. If the
 * given view is not already an instance of `View`, it will be
 * converted to one before being added to the `views` object.
 *
 * ```js
 * var views = new Views(...);
 * views.addView('a.html', {path: 'a.html', contents: '...'});
 * ```
 * @param {String} `key`
 * @param {Object} `value`
 * @return {Object} Returns the instance of the created `View` to allow chaining view methods.
 * @api public
 */

Views.prototype.addView = function(/*key, value*/) {
  var args = [].slice.call(arguments);
  this.emit.call(this, 'addView', args);

  var view = this.setView.apply(this, args);
  while (this.queue.length) {
    this.setView(this.queue.shift());
  }
  this.extendView(view);
  return view;
};

/**
 * Load multiple views onto the collection.
 *
 * ```js
 * collection.addViews({
 *   'a.html': {content: '...'},
 *   'b.html': {content: '...'},
 *   'c.html': {content: '...'}
 * });
 * ```
 * @param {Object|Array} `views`
 * @return {Object} returns the `collection` object
 * @api public
 */

Views.prototype.addViews = function(views) {
  if (Array.isArray(views)) {
    return this.addList.apply(this, arguments);
  }
  if (arguments.length > 1 && utils.isView(arguments[1])) {
    return this.addView.apply(this, arguments);
  }
  this.emit('addViews', views);
  if (this.loaded) return this;
  this.visit('addView', views);
  return this;
};

/**
 * Load an array of views onto the collection.
 *
 * ```js
 * collection.addList([
 *   {path: 'a.html', content: '...'},
 *   {path: 'b.html', content: '...'},
 *   {path: 'c.html', content: '...'}
 * ]);
 * ```
 * @param {Array} `list`
 * @return {Object} returns the `views` instance
 * @api public
 */

Views.prototype.addList = function(list, fn) {
  this.emit('addList', list);
  if (this.loaded) return this;

  if (!Array.isArray(list)) {
    throw new TypeError('expected list to be an array.');
  }

  if (typeof fn !== 'function') {
    fn = utils.identity;
  }

  var len = list.length, i = -1;
  while (++i < len) {
    var view = fn(list[i]);
    this.addView(view.path, view);
  }
  return this;
};

/**
 * Get a view from the collection.
 *
 * ```js
 * collection.getView('a.html');
 * ```
 * @param {String} `key` Key of the view to get.
 * @return {Object}
 * @api public
 */

Views.prototype.getView = function(key) {
  return this.views[key] || this.views[this.renameKey(key)];
};

/**
 * Decorate each view on the collection with additional methods
 * and properties. This provides a way of easily overriding
 * defaults.
 *
 * @param {Object} `view`
 * @return {Object}
 * @api public
 */

Views.prototype.extendView = function(view) {
  plugin.view.all(this, view);
};

/**
 * Set view types for the collection.
 *
 * @param {String} `plural` e.g. `pages`
 * @param {Object} `options`
 * @api private
 */

Views.prototype.viewType = function() {
  this.options.viewType = utils.arrayify(this.options.viewType || []);
  if (this.options.viewType.length === 0) {
    this.options.viewType.push('renderable');
  }
  return this.options.viewType;
};
<commit_msg>ensure the instance is returned on plural methods<commit_after>'use strict';

var plugin = require('./plugins');
var utils = require('./utils');
var Base = require('./base');

/**
 * Expose `Views`
 */

module.exports = Views;

/**
 * Create an instance of `Views` with the given `options`.
 *
 * ```js
 * var collection = new Views();
 * collection.addView('foo', {content: 'bar'});
 * ```
 * @param {Object} `options`
 * @api public
 */

function Views(options) {
  if (!(this instanceof Views)) {
    return new Views(options);
  }
  Base.call(this);
  this.init(options || {});
}

/**
 * Inherit `Base`
 */

Base.extend(Views);

/**
 * Mixin prototype methods
 */

plugin.routes(Views.prototype);
plugin.engine(Views.prototype);
plugin.layout(Views.prototype);
plugin.render(Views.prototype);
plugin.lookup(Views.prototype);
plugin.errors(Views.prototype, 'Views');

/**
 * Initialize `Views` defaults
 */

Views.prototype.init = function(opts) {
  this.define('isCollection', true);
  this.define('isViews', true);

  // decorate the instance
  this.use(plugin.init);
  this.use(plugin.renameKey());
  this.use(plugin.context);
  this.use(plugin.helpers);
  this.use(plugin.item('view', 'View'));

  // add constructors
  this.define('List', opts.List || require('./list'));
  this.define('View', opts.View || require('./view'));
  this.define('loaded', false);

  this.queue = [];
  this.views = {};

  // if an instance of `List` of `Views` is passed, load it now
  if (Array.isArray(opts) || opts.isList) {
    this.options = opts.options;
    this.addList(opts.items);

  } else if (opts.isCollection) {
    this.options = opts.options;
    this.addViews(opts.views);

  } else {
    this.options = opts;
  }
};

/**
 * Set a view on the collection. This is identical to [addView](#addView)
 * except `setView` does not emit an event for each view.
 *
 * ```js
 * collection.setView('foo', {content: 'bar'});
 * ```
 *
 * @param {String|Object} `key` View key or object
 * @param {Object} `value` If key is a string, value is the view object.
 * @developer This method is decorated onto the collection in the constructor using the `createView` utility method.
 * @return {Object} returns the `view` instance.
 * @api public
 */

Views.prototype.setView = function(key, value) {
  var view = this.view(key, value);
  this.views[view.key] = view;
  return view;
};

/**
 * Similar to [setView](#setView), adds a view to the collection
 * but also fires an event and iterates over the loading `queue`
 * for loading views from the `addView` event listener. If the
 * given view is not already an instance of `View`, it will be
 * converted to one before being added to the `views` object.
 *
 * ```js
 * var views = new Views(...);
 * views.addView('a.html', {path: 'a.html', contents: '...'});
 * ```
 * @param {String} `key`
 * @param {Object} `value`
 * @return {Object} Returns the instance of the created `View` to allow chaining view methods.
 * @api public
 */

Views.prototype.addView = function(/*key, value*/) {
  var args = [].slice.call(arguments);
  this.emit.call(this, 'addView', args);

  var view = this.setView.apply(this, args);
  while (this.queue.length) {
    this.setView(this.queue.shift());
  }
  this.extendView(view);
  return view;
};

/**
 * Load multiple views onto the collection.
 *
 * ```js
 * collection.addViews({
 *   'a.html': {content: '...'},
 *   'b.html': {content: '...'},
 *   'c.html': {content: '...'}
 * });
 * ```
 * @param {Object|Array} `views`
 * @return {Object} returns the `collection` object
 * @api public
 */

Views.prototype.addViews = function(views) {
  if (Array.isArray(views)) {
    this.addList.apply(this, arguments);
    return this;
  }
  if (arguments.length > 1 && utils.isView(arguments[1])) {
    this.addView.apply(this, arguments);
    return this;
  }
  this.emit('addViews', views);
  if (this.loaded) return this;
  this.visit('addView', views);
  return this;
};

/**
 * Load an array of views onto the collection.
 *
 * ```js
 * collection.addList([
 *   {path: 'a.html', content: '...'},
 *   {path: 'b.html', content: '...'},
 *   {path: 'c.html', content: '...'}
 * ]);
 * ```
 * @param {Array} `list`
 * @return {Object} returns the `views` instance
 * @api public
 */

Views.prototype.addList = function(list, fn) {
  this.emit('addList', list);
  if (this.loaded) return this;

  if (!Array.isArray(list)) {
    throw new TypeError('expected list to be an array.');
  }
  if (typeof fn !== 'function') {
    fn = utils.identity;
  }
  var len = list.length, i = -1;
  while (++i < len) {
    var view = fn(list[i]);
    this.addView(view.path, view);
  }
  return this;
};

/**
 * Get a view from the collection.
 *
 * ```js
 * collection.getView('a.html');
 * ```
 * @param {String} `key` Key of the view to get.
 * @return {Object}
 * @api public
 */

Views.prototype.getView = function(key) {
  return this.views[key] || this.views[this.renameKey(key)];
};

/**
 * Decorate each view on the collection with additional methods
 * and properties. This provides a way of easily overriding
 * defaults.
 *
 * @param {Object} `view`
 * @return {Object}
 * @api public
 */

Views.prototype.extendView = function(view) {
  plugin.view.all(this, view);
};

/**
 * Set view types for the collection.
 *
 * @param {String} `plural` e.g. `pages`
 * @param {Object} `options`
 * @api private
 */

Views.prototype.viewType = function() {
  this.options.viewType = utils.arrayify(this.options.viewType || []);
  if (this.options.viewType.length === 0) {
    this.options.viewType.push('renderable');
  }
  return this.options.viewType;
};
<|endoftext|>


<commit_before>function choiceLineGraph(nationalData) {
  var parseDate = d3.time.format("%Y-%m-%d");
  nationalData.forEach(function(choice) {
    choice.minDate = d3.min(choice.attributes.responses.map(function(d) { return parseDate.parse(d.date); }));
    choice.maxDate = d3.max(choice.attributes.responses.map(function(d) { return parseDate.parse(d.date); }));
    choice.maxPercentage = d3.max(choice.attributes.responses.map(function(d) { return d.percentage; }));
    choice.name = choice.attributes.answer;
  });
  var graph = d3.select("#line-graph");
  var WIDTH = 975;
  var HEIGHT = 500;
  var MARGINS = {
      top: 20,
      right: 130,
      bottom: 26,
      left: 50
    };
  var Xscale = d3.time.scale().range([MARGINS.left, WIDTH - MARGINS.right]);
  Xscale.domain([
      d3.min(nationalData.map(function(choice) { return choice.minDate; })),
      d3.max(nationalData.map(function(choice) { return choice.maxDate; }))
    ]);
  var Yscale = d3.scale.linear().range([HEIGHT - MARGINS.top, MARGINS.bottom]);
  Yscale.domain([
      0,
      d3.max(nationalData.map(function(choice) { return parseFloat(choice.maxPercentage) })) + 5
    ]);

  var colorScale = d3.scale.ordinal()
    .range(["#e41a1c","#377eb8","#4daf4a","#984ea3","#ff7f00","#a65628","#f781bf","#999999", "#66c2a5","#fc8d62","#8da0cb","#e78ac3","#a6d854","#ffd92f","#e5c494","#b3b3b3", "#8dd3c7","#bebada","#fb8072","#80b1d3","#fdb462","#b3de69","#fccde5","#d9d9d9","#bc80bd","#ccebc5", "#a6cee3","#1f78b4","#b2df8a","#33a02c","#fb9a99","#e31a1c","#fdbf6f","#ff7f00","#cab2d6","#6a3d9a","#b15928"]);

  var Xaxis = d3.svg.axis()
      .scale(Xscale)
      .orient("bottom");

  var Yaxis = d3.svg.axis()
      .scale(Yscale)
      .orient("left");

  // append the axes
  graph.append("svg:g")
    .attr("transform", "translate(0," + (HEIGHT - MARGINS.bottom) + ")")
    .call(Xaxis);
  graph.append("svg:g")
    .attr("transform", "translate(" + (MARGINS.left) + ",0)")
    .call(Yaxis);

  var lineGen = d3.svg.line()
    .x(function(d) {
      return Xscale(parseDate.parse(d.date));
    })
    .y(function(d) {
      return Yscale(parseFloat(d.percentage));
    })
    .interpolate('basis'); // make the lines rounded

  nationalData.forEach(function(choice, index) {
    var line = graph.append("svg:path")
      .attr("stroke", colorScale(index))
      .attr("data-legend", choice.name)
      .attr("stroke-width", 3)
      .attr("fill", "none")
      .on("mousemove", mousemove)
      .on("mouseout", mouseout);

    line.transition() // transition for line generation
      .duration(4000)
      .delay(100 + index * 100)
      .ease("linear")
      .attr('d', lineGen(choice.attributes.responses)); // generate lines on graph
  });

  var yaxiscords = d3.range(26, HEIGHT, 45.4);
  var xaxiscords = d3.range(50, WIDTH - 120, 25);

  graph.selectAll("line.vertical") // grid for x axis
    .data(xaxiscords)
    .enter().append("svg:line")
    .attr("x1", function(d) {return d;})
    .attr("y1", 26)
    .attr("x2", function(d) {return d;})
    .attr("y2", HEIGHT - 20)
    .style("stroke", "rgb(192,192,192)")
    .style("opacity", 0.3)
    .style("stroke-width", 2);

  graph.selectAll("line.horizontal") // grid for y axis
    .data(yaxiscords)
    .enter().append("svg:line")
    .attr("x1", 50)
    .attr("y1", function(d) {return d;})
    .attr("x2", WIDTH - 125)
    .attr("y2", function(d) {return d;})
    .style("stroke", "rgb(192,192,192)")
    .style("opacity", 0.3)
    .style("stroke-width", 2);

  var legend = graph.append("g")
    .attr("class","legend")
    .attr("transform","translate(870,30)")
    .style("font-size","15px")
    .call(d3.legend);

  $(".legend-items > text").on("mouseenter", mouseOnLegend);
  $(".legend-items > text").on("mouseleave", mouseOffOfLegend);

}

function mousemove() {
  $(this).css("cursor", "pointer");
  d3.select(this).classed("line-hover", true);
  $(this).parent()
    .find($(".legend-items")
    .find($("text:contains('" + $(this).attr('data-legend') + "')")))
    .css("font-weight","bold")
    .css("font-size","1.3em");
}

function mouseout() {
  d3.select(this).classed("line-hover", false);
  $(this).parent()
    .find($(".legend-items")
    .find($("text:contains('" + $(this).attr('data-legend') + "')")))
    .css("font-weight","initial")
    .css("font-size","initial");
}

function mouseOnLegend() {
  $(this).css("font-weight", "bold")
    .css("font-size", "1.3em")
    .css("cursor", "pointer")
  $(document)
    .find("path[data-legend='" + escape($(this).text()) +"']")
    .css("stroke-width", "6px");
}

function mouseOffOfLegend() {
  $(this).css("font-weight", "initial")
    .css("font-size","initial");
  $(document)
    .find("path[data-legend='" + escape($(this).text()) +"']")
    .css("stroke-width", "3px");
}
<commit_msg>fix formatting<commit_after>function choiceLineGraph(nationalData) {
  var parseDate = d3.time.format("%Y-%m-%d");
  nationalData.forEach(function(choice) {
    choice.minDate = d3.min(choice.attributes.responses.map(function(d) { return parseDate.parse(d.date); }));
    choice.maxDate = d3.max(choice.attributes.responses.map(function(d) { return parseDate.parse(d.date); }));
    choice.maxPercentage = d3.max(choice.attributes.responses.map(function(d) { return d.percentage; }));
    choice.name = choice.attributes.answer;
  });
  var graph = d3.select("#line-graph");
  var WIDTH = 975;
  var HEIGHT = 500;
  var MARGINS = {
      top: 20,
      right: 130,
      bottom: 26,
      left: 50
    };
  var Xscale = d3.time.scale().range([MARGINS.left, WIDTH - MARGINS.right]);
  Xscale.domain([
      d3.min(nationalData.map(function(choice) { return choice.minDate; })),
      d3.max(nationalData.map(function(choice) { return choice.maxDate; }))
    ]);
  var Yscale = d3.scale.linear().range([HEIGHT - MARGINS.top, MARGINS.bottom]);
  Yscale.domain([
      0,
      d3.max(nationalData.map(function(choice) { return parseFloat(choice.maxPercentage) })) + 5
    ]);

  var colorScale = d3.scale.ordinal()
    .range(["#e41a1c","#377eb8","#4daf4a","#984ea3","#ff7f00","#a65628","#f781bf","#999999", "#66c2a5","#fc8d62","#8da0cb","#e78ac3","#a6d854","#ffd92f","#e5c494","#b3b3b3", "#8dd3c7","#bebada","#fb8072","#80b1d3","#fdb462","#b3de69","#fccde5","#d9d9d9","#bc80bd","#ccebc5", "#a6cee3","#1f78b4","#b2df8a","#33a02c","#fb9a99","#e31a1c","#fdbf6f","#ff7f00","#cab2d6","#6a3d9a","#b15928"]);

  var Xaxis = d3.svg.axis()
      .scale(Xscale)
      .orient("bottom");

  var Yaxis = d3.svg.axis()
      .scale(Yscale)
      .orient("left");

  // append the axes
  graph.append("svg:g")
    .attr("transform", "translate(0," + (HEIGHT - MARGINS.bottom) + ")")
    .call(Xaxis);
  graph.append("svg:g")
    .attr("transform", "translate(" + (MARGINS.left) + ",0)")
    .call(Yaxis);

  var lineGen = d3.svg.line()
    .x(function(d) {
      return Xscale(parseDate.parse(d.date));
    })
    .y(function(d) {
      return Yscale(parseFloat(d.percentage));
    })
    .interpolate('basis'); // make the lines rounded

  nationalData.forEach(function(choice, index) {
    var line = graph.append("svg:path")
      .attr("stroke", colorScale(index))
      .attr("data-legend", choice.name)
      .attr("stroke-width", 3)
      .attr("fill", "none")
      .on("mousemove", mousemove)
      .on("mouseout", mouseout);

    line.transition() // transition for line generation
      .duration(4000)
      .delay(100 + index * 100)
      .ease("linear")
      .attr('d', lineGen(choice.attributes.responses)); // generate lines on graph
  });

  var yaxiscords = d3.range(26, HEIGHT, 45.4);
  var xaxiscords = d3.range(50, WIDTH - 120, 25);

  graph.selectAll("line.vertical") // grid for x axis
    .data(xaxiscords)
    .enter().append("svg:line")
    .attr("x1", function(d) {return d;})
    .attr("y1", 26)
    .attr("x2", function(d) {return d;})
    .attr("y2", HEIGHT - 20)
    .style("stroke", "rgb(192,192,192)")
    .style("opacity", 0.3)
    .style("stroke-width", 2);

  graph.selectAll("line.horizontal") // grid for y axis
    .data(yaxiscords)
    .enter().append("svg:line")
    .attr("x1", 50)
    .attr("y1", function(d) {return d;})
    .attr("x2", WIDTH - 125)
    .attr("y2", function(d) {return d;})
    .style("stroke", "rgb(192,192,192)")
    .style("opacity", 0.3)
    .style("stroke-width", 2);

  var legend = graph.append("g")
    .attr("class","legend")
    .attr("transform","translate(870,30)")
    .style("font-size","15px")
    .call(d3.legend);

  $(".legend-items > text").on("mouseenter", mouseOnLegend);
  $(".legend-items > text").on("mouseleave", mouseOffOfLegend);

}

function mousemove() {
  $(this).css("cursor", "pointer");
  d3.select(this).classed("line-hover", true);
  $(this).parent()
    .find($(".legend-items")
    .find($("text:contains('" + $(this).attr('data-legend') + "')")))
    .css("font-weight","bold")
    .css("font-size","1.3em");
}

function mouseout() {
  d3.select(this).classed("line-hover", false);
  $(this).parent()
    .find($(".legend-items")
    .find($("text:contains('" + $(this).attr('data-legend') + "')")))
    .css("font-weight","initial")
    .css("font-size","initial");
}

function mouseOnLegend() {
  $(this).css("font-weight", "bold")
    .css("font-size", "1.3em")
    .css("cursor", "pointer");
  $(document)
    .find("path[data-legend='" + escape($(this).text()) +"']")
    .css("stroke-width", "6px");
}

function mouseOffOfLegend() {
  $(this).css("font-weight", "initial")
    .css("font-size","initial");
  $(document)
    .find("path[data-legend='" + escape($(this).text()) +"']")
    .css("stroke-width", "3px");
}
<|endoftext|>


<commit_before>import { WidgetClickHook,
         WidgetClickOutsideHook,
         WidgetKeyUpHook,
         WidgetKeyDownHook,
         WidgetDragHook } from 'discourse/widgets/hooks';
import { h } from 'virtual-dom';
import DecoratorHelper from 'discourse/widgets/decorator-helper';

function emptyContent() { }

const _registry = {};
let _dirty = {};

export function keyDirty(key, options) {
  _dirty[key] = options || {};
}

export function renderedKey(key) {
  delete _dirty[key];
}

export function queryRegistry(name) {
  return _registry[name];
}

const _decorators = {};

export function decorateWidget(widgetName, cb) {
  _decorators[widgetName] = _decorators[widgetName] || [];
  _decorators[widgetName].push(cb);
}

export function applyDecorators(widget, type, attrs, state) {
  const decorators = _decorators[`${widget.name}:${type}`] || [];

  if (decorators.length) {
    const helper = new DecoratorHelper(widget, attrs, state);
    return decorators.map(d => d(helper));
  }

  return [];
}

const _customSettings = {};
export function changeSetting(widgetName, settingName, newValue) {
  _customSettings[widgetName] = _customSettings[widgetName] || {};
  _customSettings[widgetName][settingName] = newValue;
}

function drawWidget(builder, attrs, state) {
  const properties = {};

  if (this.buildClasses) {
    let classes = this.buildClasses(attrs, state) || [];
    if (!Array.isArray(classes)) { classes = [classes]; }

    const customClasses = applyDecorators(this, 'classNames', attrs, state);
    if (customClasses && customClasses.length) {
      classes = classes.concat(customClasses);
    }

    if (classes.length) {
      properties.className = classes.join(' ');
    }
  }
  if (this.buildId) {
    properties.id = this.buildId(attrs);
  }

  if (this.buildAttributes) {
    properties.attributes = this.buildAttributes(attrs);
  }

  if (this.keyUp) {
    properties['widget-key-up'] = new WidgetKeyUpHook(this);
  }

  if (this.keyDown) {
    properties['widget-key-down'] = new WidgetKeyDownHook(this);
  }

  if (this.clickOutside) {
    properties['widget-click-outside'] = new WidgetClickOutsideHook(this);
  }
  if (this.click) {
    properties['widget-click'] = new WidgetClickHook(this);
  }
  if (this.drag) {
    properties['widget-drag'] = new WidgetDragHook(this);
  }

  const attributes = properties['attributes'] || {};
  properties.attributes = attributes;

  if (this.title) {
    if (typeof this.title === 'function') {
      attributes.title = this.title(attrs, state);
    } else {
      attributes.title = I18n.t(this.title);
    }
  }

  let contents = this.html(attrs, state);
  if (this.name) {
    const beforeContents = applyDecorators(this, 'before', attrs, state) || [];
    const afterContents = applyDecorators(this, 'after', attrs, state) || [];
    contents = beforeContents.concat(contents).concat(afterContents);
  }

  return h(this.tagName || 'div', properties, contents);
}

export function createWidget(name, opts) {
  const result = class CustomWidget extends Widget {};

  if (name) {
    _registry[name] = result;
  }

  opts.name = name;
  opts.html = opts.html || emptyContent;
  opts.draw = drawWidget;

  Object.keys(opts).forEach(k => result.prototype[k] = opts[k]);
  return result;
}

export default class Widget {
  constructor(attrs, register, opts) {
    opts = opts || {};
    this.attrs = attrs || {};
    this.mergeState = opts.state;
    this.model = opts.model;
    this.register = register;

    register.deprecateContainer(this);

    this.key = this.buildKey ? this.buildKey(attrs) : null;
    this.site = register.lookup('site:main');
    this.siteSettings = register.lookup('site-settings:main');
    this.currentUser = register.lookup('current-user:main');
    this.capabilities = register.lookup('capabilities:main');
    this.store = register.lookup('store:main');
    this.appEvents = register.lookup('app-events:main');
    this.keyValueStore = register.lookup('key-value-store:main');

    // Helps debug widgets
    if (Ember.testing) {
      const ds = this.defaultState(attrs);
      if (typeof ds !== "object") {
        Ember.warn(`defaultState must return an object`);
      } else if (Object.keys(ds).length > 0 && !this.key) {
        Ember.warn(`you need a key when using state ${this.name}`);
      }
    }

    if (this.name) {
      const custom = _customSettings[this.name];
      if (custom) {
        Object.keys(custom).forEach(k => this.settings[k] = custom[k]);
      }
    }
  }

  defaultState() {
    return {};
  }

  destroy() {
    console.log('destroy called');
  }

  render(prev) {
    if (prev && prev.key && prev.key === this.key) {
      this.state = prev.state;
    } else {
      this.state = this.defaultState(this.attrs, this.state);
    }

    // Sometimes we pass state down from the parent
    if (this.mergeState) {
      this.state = _.merge(this.state, this.mergeState);
    }

    if (prev) {
      const dirtyOpts = _dirty[prev.key] || {};
      if (prev.shadowTree) {
        this.shadowTree = true;
        if (!dirtyOpts && !_dirty['*']) {
          return prev.vnode;
        }
      }
      renderedKey(prev.key);

      const refreshAction = dirtyOpts.onRefresh;
      if (refreshAction) {
        this.sendWidgetAction(refreshAction, dirtyOpts.refreshArg);
      }
    }

    return this.draw(h, this.attrs, this.state);
  }

  _findAncestorWithProperty(property) {
    let widget = this;
    while (widget) {
      const value = widget[property];
      if (value) {
        return widget;
      }
      widget = widget.parentWidget;
    }
  }

  _findView() {
    const widget = this._findAncestorWithProperty('_emberView');
    if (widget) {
      return widget._emberView;
    }
  }

  attach(widgetName, attrs, opts) {
    let WidgetClass = _registry[widgetName];

    if (!WidgetClass) {
      if (!this.register) {
        console.error("couldn't find register");
        return;
      }
      WidgetClass = this.register.lookupFactory(`widget:${widgetName}`);
    }

    if (WidgetClass) {
      const result = new WidgetClass(attrs, this.register, opts);
      result.parentWidget = this;
      return result;
    } else {
      throw `Couldn't find ${widgetName} factory`;
    }
  }

  scheduleRerender() {
    let widget = this;
    while (widget) {
      if (widget.shadowTree) {
        keyDirty(widget.key);
      }

      const rerenderable = widget._rerenderable;
      if (rerenderable) {
        return rerenderable.queueRerender();
      }

      widget = widget.parentWidget;
    }
  }

  _sendComponentAction(name, param) {
    let promise;

    const view = this._findView();
    if (view) {
      const method = view.get(name);
      if (!method) {
        console.warn(`${name} not found`);
        return;
      }

      if (typeof method === "string") {
        view.sendAction(method, param);
        promise = Ember.RSVP.resolve();
      } else {
        const target = view.get('targetObject');
        promise = method.call(target, param);
        if (!promise || !promise.then) {
          promise = Ember.RSVP.resolve(promise);
        }
      }
    }

    return this.rerenderResult(() => promise);
  }

  findAncestorModel() {
    const modelWidget = this._findAncestorWithProperty('model');
    if (modelWidget) {
      return modelWidget.model;
    }
  }

  rerenderResult(fn) {
    this.scheduleRerender();
    const result = fn();
    // re-render after any promises complete, too!
    if (result && result.then) {
      return result.then(() => this.scheduleRerender());
    }
    return result;
  }

  sendWidgetEvent(name) {
    const methodName = `${name}Event`;
    return this.rerenderResult(() => {
      const widget = this._findAncestorWithProperty(methodName);
      if (widget) {
        return widget[methodName]();
      }
    });
  }

  sendWidgetAction(name, param) {
    return this.rerenderResult(() => {
      const widget = this._findAncestorWithProperty(name);
      if (widget) {
        return widget[name].call(widget, param);
      }

      return this._sendComponentAction(name, param || this.findAncestorModel());
    });
  }
}

Widget.prototype.type = 'Thunk';
<commit_msg>Add reopenWidget method<commit_after>import { WidgetClickHook,
         WidgetClickOutsideHook,
         WidgetKeyUpHook,
         WidgetKeyDownHook,
         WidgetDragHook } from 'discourse/widgets/hooks';
import { h } from 'virtual-dom';
import DecoratorHelper from 'discourse/widgets/decorator-helper';

function emptyContent() { }

const _registry = {};
let _dirty = {};

export function keyDirty(key, options) {
  _dirty[key] = options || {};
}

export function renderedKey(key) {
  delete _dirty[key];
}

export function queryRegistry(name) {
  return _registry[name];
}

const _decorators = {};

export function decorateWidget(widgetName, cb) {
  _decorators[widgetName] = _decorators[widgetName] || [];
  _decorators[widgetName].push(cb);
}

export function applyDecorators(widget, type, attrs, state) {
  const decorators = _decorators[`${widget.name}:${type}`] || [];

  if (decorators.length) {
    const helper = new DecoratorHelper(widget, attrs, state);
    return decorators.map(d => d(helper));
  }

  return [];
}

const _customSettings = {};
export function changeSetting(widgetName, settingName, newValue) {
  _customSettings[widgetName] = _customSettings[widgetName] || {};
  _customSettings[widgetName][settingName] = newValue;
}

function drawWidget(builder, attrs, state) {
  const properties = {};

  if (this.buildClasses) {
    let classes = this.buildClasses(attrs, state) || [];
    if (!Array.isArray(classes)) { classes = [classes]; }

    const customClasses = applyDecorators(this, 'classNames', attrs, state);
    if (customClasses && customClasses.length) {
      classes = classes.concat(customClasses);
    }

    if (classes.length) {
      properties.className = classes.join(' ');
    }
  }
  if (this.buildId) {
    properties.id = this.buildId(attrs);
  }

  if (this.buildAttributes) {
    properties.attributes = this.buildAttributes(attrs);
  }

  if (this.keyUp) {
    properties['widget-key-up'] = new WidgetKeyUpHook(this);
  }

  if (this.keyDown) {
    properties['widget-key-down'] = new WidgetKeyDownHook(this);
  }

  if (this.clickOutside) {
    properties['widget-click-outside'] = new WidgetClickOutsideHook(this);
  }
  if (this.click) {
    properties['widget-click'] = new WidgetClickHook(this);
  }
  if (this.drag) {
    properties['widget-drag'] = new WidgetDragHook(this);
  }

  const attributes = properties['attributes'] || {};
  properties.attributes = attributes;

  if (this.title) {
    if (typeof this.title === 'function') {
      attributes.title = this.title(attrs, state);
    } else {
      attributes.title = I18n.t(this.title);
    }
  }

  let contents = this.html(attrs, state);
  if (this.name) {
    const beforeContents = applyDecorators(this, 'before', attrs, state) || [];
    const afterContents = applyDecorators(this, 'after', attrs, state) || [];
    contents = beforeContents.concat(contents).concat(afterContents);
  }

  return h(this.tagName || 'div', properties, contents);
}

export function createWidget(name, opts) {
  const result = class CustomWidget extends Widget {};

  if (name) {
    _registry[name] = result;
  }

  opts.name = name;
  opts.html = opts.html || emptyContent;
  opts.draw = drawWidget;

  Object.keys(opts).forEach(k => result.prototype[k] = opts[k]);
  return result;
}

export function reopenWidget(name, opts) {
  let existing = _registry[name]
  if (!existing) {
    console.error(`Could not find widget ${name} in registry`);
    return
  }

  Object.keys(opts).forEach(k => existing.prototype[k] = opts[k])
  return existing
}

export default class Widget {
  constructor(attrs, register, opts) {
    opts = opts || {};
    this.attrs = attrs || {};
    this.mergeState = opts.state;
    this.model = opts.model;
    this.register = register;

    register.deprecateContainer(this);

    this.key = this.buildKey ? this.buildKey(attrs) : null;
    this.site = register.lookup('site:main');
    this.siteSettings = register.lookup('site-settings:main');
    this.currentUser = register.lookup('current-user:main');
    this.capabilities = register.lookup('capabilities:main');
    this.store = register.lookup('store:main');
    this.appEvents = register.lookup('app-events:main');
    this.keyValueStore = register.lookup('key-value-store:main');

    // Helps debug widgets
    if (Ember.testing) {
      const ds = this.defaultState(attrs);
      if (typeof ds !== "object") {
        Ember.warn(`defaultState must return an object`);
      } else if (Object.keys(ds).length > 0 && !this.key) {
        Ember.warn(`you need a key when using state ${this.name}`);
      }
    }

    if (this.name) {
      const custom = _customSettings[this.name];
      if (custom) {
        Object.keys(custom).forEach(k => this.settings[k] = custom[k]);
      }
    }
  }

  defaultState() {
    return {};
  }

  destroy() {
    console.log('destroy called');
  }

  render(prev) {
    if (prev && prev.key && prev.key === this.key) {
      this.state = prev.state;
    } else {
      this.state = this.defaultState(this.attrs, this.state);
    }

    // Sometimes we pass state down from the parent
    if (this.mergeState) {
      this.state = _.merge(this.state, this.mergeState);
    }

    if (prev) {
      const dirtyOpts = _dirty[prev.key] || {};
      if (prev.shadowTree) {
        this.shadowTree = true;
        if (!dirtyOpts && !_dirty['*']) {
          return prev.vnode;
        }
      }
      renderedKey(prev.key);

      const refreshAction = dirtyOpts.onRefresh;
      if (refreshAction) {
        this.sendWidgetAction(refreshAction, dirtyOpts.refreshArg);
      }
    }

    return this.draw(h, this.attrs, this.state);
  }

  _findAncestorWithProperty(property) {
    let widget = this;
    while (widget) {
      const value = widget[property];
      if (value) {
        return widget;
      }
      widget = widget.parentWidget;
    }
  }

  _findView() {
    const widget = this._findAncestorWithProperty('_emberView');
    if (widget) {
      return widget._emberView;
    }
  }

  attach(widgetName, attrs, opts) {
    let WidgetClass = _registry[widgetName];

    if (!WidgetClass) {
      if (!this.register) {
        console.error("couldn't find register");
        return;
      }
      WidgetClass = this.register.lookupFactory(`widget:${widgetName}`);
    }

    if (WidgetClass) {
      const result = new WidgetClass(attrs, this.register, opts);
      result.parentWidget = this;
      return result;
    } else {
      throw `Couldn't find ${widgetName} factory`;
    }
  }

  scheduleRerender() {
    let widget = this;
    while (widget) {
      if (widget.shadowTree) {
        keyDirty(widget.key);
      }

      const rerenderable = widget._rerenderable;
      if (rerenderable) {
        return rerenderable.queueRerender();
      }

      widget = widget.parentWidget;
    }
  }

  _sendComponentAction(name, param) {
    let promise;

    const view = this._findView();
    if (view) {
      const method = view.get(name);
      if (!method) {
        console.warn(`${name} not found`);
        return;
      }

      if (typeof method === "string") {
        view.sendAction(method, param);
        promise = Ember.RSVP.resolve();
      } else {
        const target = view.get('targetObject');
        promise = method.call(target, param);
        if (!promise || !promise.then) {
          promise = Ember.RSVP.resolve(promise);
        }
      }
    }

    return this.rerenderResult(() => promise);
  }

  findAncestorModel() {
    const modelWidget = this._findAncestorWithProperty('model');
    if (modelWidget) {
      return modelWidget.model;
    }
  }

  rerenderResult(fn) {
    this.scheduleRerender();
    const result = fn();
    // re-render after any promises complete, too!
    if (result && result.then) {
      return result.then(() => this.scheduleRerender());
    }
    return result;
  }

  sendWidgetEvent(name) {
    const methodName = `${name}Event`;
    return this.rerenderResult(() => {
      const widget = this._findAncestorWithProperty(methodName);
      if (widget) {
        return widget[methodName]();
      }
    });
  }

  sendWidgetAction(name, param) {
    return this.rerenderResult(() => {
      const widget = this._findAncestorWithProperty(name);
      if (widget) {
        return widget[name].call(widget, param);
      }

      return this._sendComponentAction(name, param || this.findAncestorModel());
    });
  }
}

Widget.prototype.type = 'Thunk';
<|endoftext|>