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

	var async = require('async'),
		fs = require('fs'),
		path = require('path'),
		categories = module.parent.require('./categories'),
		user = module.parent.require('./user'),
		plugins = module.parent.require('./plugins'),
		topics = module.parent.require('./topics'),
		translator = module.parent.require('../public/src/translator'),
		templates = module.parent.require('../public/src/templates'),
		app;

	var Widget = {
		templates: {}
	};

	Widget.renderHTMLWidget = function(widget, callback) {
		var html = widget.data.html;

		callback(null, html);
	};

	Widget.renderTextWidget = function(widget, callback) {
		var parseAsPost = !!widget.data.parseAsPost,
			text = widget.data.text;

		if (parseAsPost) {
			plugins.fireHook('filter:post.parse', text, function(err, text) {
				callback(err, text);
			});
		} else {
			callback(null, text.replace(/\r\n/g, "
"));
		}
	};

	Widget.renderRecentViewWidget = function(widget, callback) {
		topics.getLatestTopics(widget.uid, 0, 19, 'month', function (err, data) {
			if(err) {
				return callback(err);
			}

			app.render('recent', data, function(err, html) {
				html = html.replace(/<ol[\s\S]*?
/, '').replace('
', '');

				translator.translate(html, function(translatedHTML) {
					callback(err, translatedHTML);
				});
			});
		});
	};

	Widget.renderRecentRepliesWidget = function(widget, callback) {
		var html = Widget.templates['recentreplies.tpl'];

		html = templates.parse(html, {cid: widget.data.cid || false});

		callback(null, html);
	};

	Widget.renderActiveUsersWidget = function(widget, callback) {
		function getUserData(err, uids) {
			if (err) {
				return callback(err);
			}

			user.getMultipleUserFields(uids, ['uid', 'username', 'userslug', 'picture'], function(err, users) {
				if (err) {
					return callback(err);
				}

				html = templates.parse(html, {active_users: users});

				callback(err, html);
			});
		}

		var html = Widget.templates['activeusers.tpl'], cidOrtid;

		if (widget.data.cid) {
			cidOrtid = widget.data.cid;
		} else if (widget.area.url.indexOf('topic') === 0) {
			var match = widget.area.url.match('topic/([0-9]+)');
			cidOrtid = (match && match.length > 1) ? match[1] : 1;
			topics.getUids(cidOrtid, getUserData);
		} else {
			var match = widget.area.url.match('[0-9]+');
			cidOrtid = match ? match[0] : 1;
			categories.getActiveUsers(cidOrtid, getUserData);
		}
	};

	Widget.renderModeratorsWidget = function(widget, callback) {
		var html = Widget.templates['moderators.tpl'], cid;

		if (widget.data.cid) {
			cid = widget.data.cid;
		} else {
			var match = widget.area.url.match('[0-9]+');
			cid = match ? match[0] : 1;
		}

		categories.getModerators(cid, function(err, moderators) {
			html = templates.parse(html, {moderators: moderators});

			callback(err, html);
		});
	};

	Widget.renderForumStatsWidget = function(widget, callback) {
		var html = Widget.templates['forumstats.tpl'];

		html = templates.parse(html, {statsClass: widget.data.statsClass});

		translator.translate(html, function(translatedHTML) {
			callback(null, translatedHTML);
		});
	};

	Widget.renderRecentPostsWidget = function(widget, callback) {
		var html = Widget.templates['recentposts.tpl'];

		html = templates.parse(html, {
			numPosts: widget.data.numPosts || 8,
			duration: widget.data.duration || 'day'
		});

		callback(null, html);
	};

	Widget.renderRecentTopicsWidget = function(widget, callback) {
		var html = Widget.templates['recenttopics.tpl'];

		html = templates.parse(html, {
			numTopics: widget.data.numTopics || 8,
			duration: widget.data.duration || 'day'
		});

		callback(null, html);
	};

	Widget.renderCategories = function(widget, callback) {
		var html = Widget.templates['categories.tpl'];

		categories.getVisibleCategories(widget.uid, function(err, data) {
			html = templates.parse(html, {categories: data});

			callback(err, html);
		});
	};

	Widget.renderPopularTags = function(widget, callback) {
		var html = Widget.templates['populartags.tpl'];
		var numTags = widget.data.numTags || 8;
		topics.getTags(0, numTags - 1, function(err, tags) {
			if (err) {
				return callback(err);
			}

			html = templates.parse(html, {tags: tags});

			callback(err, html);
		});
	};

	Widget.defineWidgets = function(widgets, callback) {
		widgets = widgets.concat([
			{
				widget: "html",
				name: "HTML",
				description: "Any text, html, or embedded script.",
				content: Widget.templates['admin/html.tpl']
			},
			{
				widget: "text",
				name: "Text",
				description: "Text, optionally parsed as a post.",
				content: Widget.templates['admin/text.tpl']
			},
			{
				widget: "recentreplies",
				name: "Recent Replies",
				description: "List of recent replies in a category.",
				content: Widget.templates['admin/categorywidget.tpl']
			},
			{
				widget: "activeusers",
				name: "Active Users",
				description: "List of active users in a category.",
				content: Widget.templates['admin/categorywidget.tpl']
			},
			{
				widget: "moderators",
				name: "Moderators",
				description: "List of moderators in a category.",
				content: Widget.templates['admin/categorywidget.tpl']
			},
			{
				widget: "forumstats",
				name: "Forum Stats",
				description: "Lists user, topics, and post count.",
				content: Widget.templates['admin/forumstats.tpl']
			},
			{
				widget: "recentposts",
				name: "Recent Posts",
				description: "Lists the latest posts on your forum.",
				content: Widget.templates['admin/recentposts.tpl']
			},
			{
				widget: "recenttopics",
				name: "Recent Topics",
				description: "Lists the latest topics on your forum.",
				content: Widget.templates['admin/recenttopics.tpl']
			},
			{
				widget: "recentview",
				name: "Recent View",
				description: "Renders the /recent page",
				content: Widget.templates['admin/defaultwidget.tpl']
			},
			{
				widget: "categories",
				name: "Categories",
				description: "Lists the categories on your forum",
				content: Widget.templates['admin/categorieswidget.tpl']
			},
			{
				widget:"populartags",
				name:"Popular Tags",
				description:"Lists popular tags on your forum",
				content: Widget.templates['admin/populartags.tpl']
			}
		]);

		callback(null, widgets);
	};

	Widget.init = function(express, middleware, controllers, callback) {
		app = express;

		var templatesToLoad = [
			"recentreplies.tpl", "activeusers.tpl", "moderators.tpl", "forumstats.tpl", "recentposts.tpl", "recenttopics.tpl",
			"categories.tpl", "populartags.tpl",
			"admin/categorywidget.tpl", "admin/forumstats.tpl", "admin/html.tpl", "admin/text.tpl", "admin/recentposts.tpl",
			"admin/recenttopics.tpl", "admin/defaultwidget.tpl", "admin/categorieswidget.tpl", "admin/populartags.tpl"
		];

		function loadTemplate(template, next) {
			fs.readFile(path.resolve(__dirname, './public/templates/' + template), function (err, data) {
				if (err) {
					console.log(err.message);
					return next(err);
				}
				Widget.templates[template] = data.toString();
				next(null);
			});
		}

		async.each(templatesToLoad, loadTemplate);

		callback();
	};

	module.exports = Widget;
}(module));<commit_msg>on home display recent posters<commit_after>(function(module) {
	"use strict";

	var async = require('async'),
		fs = require('fs'),
		path = require('path'),
		categories = module.parent.require('./categories'),
		user = module.parent.require('./user'),
		plugins = module.parent.require('./plugins'),
		topics = module.parent.require('./topics'),
		posts = module.parent.require('./posts'),
		translator = module.parent.require('../public/src/translator'),
		templates = module.parent.require('../public/src/templates'),
		app;

	var Widget = {
		templates: {}
	};

	Widget.renderHTMLWidget = function(widget, callback) {
		var html = widget.data.html;

		callback(null, html);
	};

	Widget.renderTextWidget = function(widget, callback) {
		var parseAsPost = !!widget.data.parseAsPost,
			text = widget.data.text;

		if (parseAsPost) {
			plugins.fireHook('filter:post.parse', text, function(err, text) {
				callback(err, text);
			});
		} else {
			callback(null, text.replace(/\r\n/g, "
"));
		}
	};

	Widget.renderRecentViewWidget = function(widget, callback) {
		topics.getLatestTopics(widget.uid, 0, 19, 'month', function (err, data) {
			if(err) {
				return callback(err);
			}

			app.render('recent', data, function(err, html) {
				html = html.replace(/<ol[\s\S]*?
/, '').replace('
', '');

				translator.translate(html, function(translatedHTML) {
					callback(err, translatedHTML);
				});
			});
		});
	};

	Widget.renderRecentRepliesWidget = function(widget, callback) {
		var html = Widget.templates['recentreplies.tpl'];

		html = templates.parse(html, {cid: widget.data.cid || false});

		callback(null, html);
	};

	Widget.renderActiveUsersWidget = function(widget, callback) {
		function getUserData(err, uids) {
			if (err) {
				return callback(err);
			}

			user.getMultipleUserFields(uids, ['uid', 'username', 'userslug', 'picture'], function(err, users) {
				if (err) {
					return callback(err);
				}

				html = templates.parse(html, {active_users: users});

				callback(err, html);
			});
		}

		var html = Widget.templates['activeusers.tpl'], cidOrtid;

		if (widget.data.cid) {
			cidOrtid = widget.data.cid;
		} else if (widget.area.url.indexOf('topic') === 0) {
			var match = widget.area.url.match('topic/([0-9]+)');
			cidOrtid = (match && match.length > 1) ? match[1] : 1;
			topics.getUids(cidOrtid, getUserData);
		} else if (widget.area.url === '') {
			posts.getRecentPosterUids(0, 24, getUserData);
		} else {
			var match = widget.area.url.match('[0-9]+');
			cidOrtid = match ? match[0] : 1;
			categories.getActiveUsers(cidOrtid, getUserData);
		}
	};

	Widget.renderModeratorsWidget = function(widget, callback) {
		var html = Widget.templates['moderators.tpl'], cid;

		if (widget.data.cid) {
			cid = widget.data.cid;
		} else {
			var match = widget.area.url.match('[0-9]+');
			cid = match ? match[0] : 1;
		}

		categories.getModerators(cid, function(err, moderators) {
			html = templates.parse(html, {moderators: moderators});

			callback(err, html);
		});
	};

	Widget.renderForumStatsWidget = function(widget, callback) {
		var html = Widget.templates['forumstats.tpl'];

		html = templates.parse(html, {statsClass: widget.data.statsClass});

		translator.translate(html, function(translatedHTML) {
			callback(null, translatedHTML);
		});
	};

	Widget.renderRecentPostsWidget = function(widget, callback) {
		var html = Widget.templates['recentposts.tpl'];

		html = templates.parse(html, {
			numPosts: widget.data.numPosts || 8,
			duration: widget.data.duration || 'day'
		});

		callback(null, html);
	};

	Widget.renderRecentTopicsWidget = function(widget, callback) {
		var html = Widget.templates['recenttopics.tpl'];

		html = templates.parse(html, {
			numTopics: widget.data.numTopics || 8,
			duration: widget.data.duration || 'day'
		});

		callback(null, html);
	};

	Widget.renderCategories = function(widget, callback) {
		var html = Widget.templates['categories.tpl'];

		categories.getVisibleCategories(widget.uid, function(err, data) {
			html = templates.parse(html, {categories: data});

			callback(err, html);
		});
	};

	Widget.renderPopularTags = function(widget, callback) {
		var html = Widget.templates['populartags.tpl'];
		var numTags = widget.data.numTags || 8;
		topics.getTags(0, numTags - 1, function(err, tags) {
			if (err) {
				return callback(err);
			}

			html = templates.parse(html, {tags: tags});

			callback(err, html);
		});
	};

	Widget.defineWidgets = function(widgets, callback) {
		widgets = widgets.concat([
			{
				widget: "html",
				name: "HTML",
				description: "Any text, html, or embedded script.",
				content: Widget.templates['admin/html.tpl']
			},
			{
				widget: "text",
				name: "Text",
				description: "Text, optionally parsed as a post.",
				content: Widget.templates['admin/text.tpl']
			},
			{
				widget: "recentreplies",
				name: "Recent Replies",
				description: "List of recent replies in a category.",
				content: Widget.templates['admin/categorywidget.tpl']
			},
			{
				widget: "activeusers",
				name: "Active Users",
				description: "List of active users in a category.",
				content: Widget.templates['admin/categorywidget.tpl']
			},
			{
				widget: "moderators",
				name: "Moderators",
				description: "List of moderators in a category.",
				content: Widget.templates['admin/categorywidget.tpl']
			},
			{
				widget: "forumstats",
				name: "Forum Stats",
				description: "Lists user, topics, and post count.",
				content: Widget.templates['admin/forumstats.tpl']
			},
			{
				widget: "recentposts",
				name: "Recent Posts",
				description: "Lists the latest posts on your forum.",
				content: Widget.templates['admin/recentposts.tpl']
			},
			{
				widget: "recenttopics",
				name: "Recent Topics",
				description: "Lists the latest topics on your forum.",
				content: Widget.templates['admin/recenttopics.tpl']
			},
			{
				widget: "recentview",
				name: "Recent View",
				description: "Renders the /recent page",
				content: Widget.templates['admin/defaultwidget.tpl']
			},
			{
				widget: "categories",
				name: "Categories",
				description: "Lists the categories on your forum",
				content: Widget.templates['admin/categorieswidget.tpl']
			},
			{
				widget:"populartags",
				name:"Popular Tags",
				description:"Lists popular tags on your forum",
				content: Widget.templates['admin/populartags.tpl']
			}
		]);

		callback(null, widgets);
	};

	Widget.init = function(express, middleware, controllers, callback) {
		app = express;

		var templatesToLoad = [
			"recentreplies.tpl", "activeusers.tpl", "moderators.tpl", "forumstats.tpl", "recentposts.tpl", "recenttopics.tpl",
			"categories.tpl", "populartags.tpl",
			"admin/categorywidget.tpl", "admin/forumstats.tpl", "admin/html.tpl", "admin/text.tpl", "admin/recentposts.tpl",
			"admin/recenttopics.tpl", "admin/defaultwidget.tpl", "admin/categorieswidget.tpl", "admin/populartags.tpl"
		];

		function loadTemplate(template, next) {
			fs.readFile(path.resolve(__dirname, './public/templates/' + template), function (err, data) {
				if (err) {
					console.log(err.message);
					return next(err);
				}
				Widget.templates[template] = data.toString();
				next(null);
			});
		}

		async.each(templatesToLoad, loadTemplate);

		callback();
	};

	module.exports = Widget;
}(module));<|endoftext|>


<commit_before>'use strict';

var meta = module.parent.require('./meta');
var user = module.parent.require('./user');
var nconf = require('nconf');

var db = module.parent.require('./database'); // NodeBB / src / database 참고.
var au = module.parent.require('./controllers/authentication'); // NodeBB / src / controllers 참고.

var winston = require('winston');
// 모듈 객체.
var plugin = {};

plugin.init = function (params, callback)	{
	var router = params.router;

	winston.info('Start community..');

	router.get('/community', function ()	{
		console.log(arguments);
	});

	callback();
}

plugin.verifyUser = function (token, callback)	{

}

// 이 함수는 NodeBB 에서 어떤 동작 또는 페이지이동 때마다 호출되므로 계속해서 토큰을 받아오고 유저 유효성 검사를 할 것이다.
// 그러므로 함수 첫줄에 세션 확인을 하는 구문을 만들어 같은 세션일 경우 유저 유효성 검사를 넘어가도록 한다.
plugin.addMiddleware = function (req, res, next)	{
	nconf.set('mongo:database', 'biobank-session');
	console.log(nconf.get('mongo:database'));
	console.log('login add middle ware!!', req.headers.cookie);
	// 이미 있는 세션일 경우 요청 프로퍼티에 user 와 user 안에 uid 가 존재 한다.
	// TODO.
	// 로그아웃 후 창을 닫고 바이오클라우드에서 다시 커뮤니티로 접속하면 세션관련 리프레쉬가 발생한다.
	// 이 경우 세션이 변경됨을 확인하고 리프레쉬를 해줘야한다.
	var hasSession = req.hasOwnProperty('user') && req.user.hasOwnProperty('uid') && parseInt(req.user.uid, 10) > 10;

};
// 로그아웃 함수.
plugin.doLogout = function (data, callback)	{
	winston.info('Do logout..');

	if (typeof callback === 'function')	{
		callback();
	} else {
		return true;
	}
}

module.exports = plugin;<commit_msg>20170405141039<commit_after>'use strict';

var meta = module.parent.require('./meta');
var user = module.parent.require('./user');
var nconf = require('nconf');

var db = module.parent.require('./database'); // NodeBB / src / database 참고.
var au = module.parent.require('./controllers/authentication'); // NodeBB / src / controllers 참고.

var winston = require('winston');
// 모듈 객체.
var plugin = {};

plugin.init = function (params, callback)	{
	var router = params.router;

	winston.info('Start community..');

	router.get('/community', function ()	{
		console.log(arguments);
	});

	callback();
}

plugin.verifyUser = function (token, callback)	{

}

// 이 함수는 NodeBB 에서 어떤 동작 또는 페이지이동 때마다 호출되므로 계속해서 토큰을 받아오고 유저 유효성 검사를 할 것이다.
// 그러므로 함수 첫줄에 세션 확인을 하는 구문을 만들어 같은 세션일 경우 유저 유효성 검사를 넘어가도록 한다.
plugin.addMiddleware = function (req, res, next)	{
	nconf.set('mongo:database', 'biobank-session');
	var newdb = nconf.get('mongo:database');

	db.info(newdb, function ()	{
		console.log(arguments);
	})
	console.log('login add middle ware!!', req.headers.cookie);
	// 이미 있는 세션일 경우 요청 프로퍼티에 user 와 user 안에 uid 가 존재 한다.
	// TODO.
	// 로그아웃 후 창을 닫고 바이오클라우드에서 다시 커뮤니티로 접속하면 세션관련 리프레쉬가 발생한다.
	// 이 경우 세션이 변경됨을 확인하고 리프레쉬를 해줘야한다.
	var hasSession = req.hasOwnProperty('user') && req.user.hasOwnProperty('uid') && parseInt(req.user.uid, 10) > 10;

};
// 로그아웃 함수.
plugin.doLogout = function (data, callback)	{
	winston.info('Do logout..');

	if (typeof callback === 'function')	{
		callback();
	} else {
		return true;
	}
}

module.exports = plugin;<|endoftext|>


<commit_before>/*jslint node: true */
/*jshint laxbreak: true */
"use strict";
/*
 * Copyright (c) 2015 ljgarcia
 * Licensed under the Apache 2 license.
 */
var d3 = require('d3');
var _ = require('underscore');
var jQuery = require('jquery');
var BiolinksParser = require('biotea-io-parser');
var AnnotationViewer = require('biotea-vis-annotation');
var DistributionViewer = require('biotea-vis-topicDistribution');
var SimilarityViewer = require('biotea-vis-similarity');

/**
 * Private Methods
 */
/**
 * content: [{label: 'ft', text: 'Full content'}, {label: 'ta', text: 'Titla and abstract'}]
 * topics: {ft: [{id: '', text: ''}], ta: [{id: '', text: ''}]}
 * articles: {ft: [{topic: '', id: '', altId: '', title: ''}], ta: []}
*/
var defaultOpts = {
    title: 'Biotea-Biolinks',
    width: 880,
    annotationHeight: 400,
    similarityHeight: 400,
    distributionHeight: 400,
    content: [],
    topics: {},
    articles: {},
    paths: {},
    onlyTF: false
};

/*
 * Public Methods
 */
var BiolinksViewer = function(opts){
    var viewer = this;
    viewer.options = _.extend({}, defaultOpts, opts);
    viewer.parser = new BiolinksParser();
    init(viewer);
};

var init = function(viewer) {
    var controlsDiv = d3.select(viewer.options.el).append('div').classed('biolinks_menu', true);
    //TODO create group selection and scope menu

    var titleDiv = d3.select(viewer.options.el).append('h1').text(viewer.options.title);
    initContentAndTopics(viewer);
    initDistribution(viewer);
    initSimilarity(viewer);
    initAnnotations(viewer);
    updateTopics(viewer);
};

var initContentAndTopics = function(viewer) {
    var contentDiv = d3.select(viewer.options.el).append('div').classed('biolinks_content', true);
    contentDiv.append('span').text('Content/Chapter: ');
    viewer.contentSelect = contentDiv.append('span').append('select')
        .attr('id', 'biolinks_contentSelection')
        .on('change', function() {
            var selectedIndex = viewer.contentSelect.property('selectedIndex')
            viewer.selectedContent = viewer.contentOptions[0][selectedIndex].__data__;
            updateTopics(viewer);
        });

    viewer.contentOptions = viewer.contentSelect.selectAll('option')
        .data(viewer.options.content)
        .enter().append('option')
        .attr('value', function(d) {return d.label;})
        .text(function(d) {return d.text;});
    viewer.selectedContent = viewer.options.content[0];

    var topicsDiv = d3.select(viewer.options.el).append('div');
    topicsDiv.append('span').text('Topic/Subject: ');
    viewer.topicsSelect = topicsDiv.append('span').append('select')
        .attr('id', 'biolinks_topicsSelection')
        .on('change', function() {
            var selectedIndex = viewer.topicsSelect.property('selectedIndex')
            viewer.selectedTopic = viewer.topicsOption[0][selectedIndex].__data__.id;
            updateDistribution(viewer);
        });
};

var initDistribution = function(viewer) {
    var distributionGroupDiv = d3.select(viewer.options.el).append('div').classed('biolinks_distribution_group', true);
    distributionGroupDiv.append('h2').text('Biolinks (UMLS-based) group distribution')

    distributionGroupDiv.append('div').classed('biolinks_caption', true)
        .html('Rows are semantic groups and columns are articles, the darker a cell the more representative the '
            + 'semantic group for an article.
White means the group is not present in the article. '
            + '
Hover and click any cell for more information and options');

    var articleDiv = distributionGroupDiv.append('div').html('');
    var header = articleDiv.append('h4');
    header.append('span').text('Selected-reference Article: ');
    viewer.articleTitle = header.append('span').text('Select an article by clicking on any column in the ' +
        'distribution matrix. Hover for more information.');

    var distDivId = 'biolinks_dist_' + new Date().getTime();
    viewer.distributionDiv = distributionGroupDiv.append('div')
        .attr('id', distDivId)
        .classed('biolinks_distribution', true);
    viewer.topicDistribution = new DistributionViewer({
        el: '#' + distDivId,
        width: viewer.options.width
    });

    viewer.topicDistribution.getDispatcher().on('selected', function(obj) {
        if (obj.type === 'distribution') {
            var collection = viewer.options.articles[viewer.selectedContent.label];
            viewer.selectedArticle = _.find(collection, function(el) {
                return +obj.article === +el.id;
            });

            var articleText = viewer.selectedArticle.title + ' (' + viewer.selectedContent.prefix + ':' +
                viewer.selectedArticle.id + ')';
            viewer.articleTitle.text(articleText);

            if (viewer.topicArticles.length >= 3) {
                showSimilarityGroup(viewer);
                updateSimilarity(viewer);
            } else {
               showAnnotationGroup(viewer);
               updateAnnotations(viewer);
            }

        }
    });
};

var initSimilarity = function(viewer) {
    viewer.similarityGroupDiv = d3.select(viewer.options.el).append('div')
        .classed('biolinks_similarity_group', true)
        .style('display', 'none');
    viewer.similarityGroupDiv.append('h2').text('Similarity network for selected-reference article');
    viewer.secondaryArticleDiv = viewer.similarityGroupDiv.append('div');

    viewer.similarityViewers = {};
    var table = viewer.similarityGroupDiv.append('table').attr('width', '100%');
    var temp = [];
    var row = table.append('tr');
    if (viewer.selectedContent.seeAlso) {
        row.append('th').text('Similarity for ' + viewer.selectedContent.text);
        var seeAlso = _.find(viewer.options.content, function(content) {
            return content.label === viewer.selectedContent.seeAlso;
        });
        row.append('th').text('Similarity for ' + seeAlso.text);
        temp = [viewer.selectedContent, seeAlso];
    } else {
        row.append('th').text('Similarity for ' + viewer.selectedContent.text);
        temp = [viewer.selectedContent];
    }
    row = table.append('tr');
    _.each(temp, function(content) {
        var divId = 'biolinks_sim_' + content.label + '_' + new Date().getTime();
        var simViewer = row.append('td').append('div').attr('id', divId);
        viewer.similarityViewers[content.label] = {
            display: simViewer,
            id: '#' + divId,
            content: content
        };
    });
};

var initAnnotations = function(viewer) {
    viewer.similarityGroupDiv = d3.select(viewer.options.el).append('div')
        .classed('biolinks_annotation_group', true)
        .style('display', 'none');
    viewer.similarityGroupDiv.append('h2').text('Semantic annotations for selected articles');
};

var hideSimilarityGroup = function(viewer) {
    viewer.similarityGroupDiv.style('display', 'none');
    viewer.secondaryArticleDiv.selectAll('*').remove();
    viewer.secondaryArticleDiv.html('');
    _.each(viewer.similarityViewers, function(simViewer) {
        d3.select(simViewer.id).selectAll('*').remove();
        d3.select(simViewer.id).html('');
    });
};

var showSimilarityGroup = function(viewer) {
    viewer.similarityGroupDiv.style('display', 'block');
    var header = viewer.secondaryArticleDiv.append('h4');
    header.append('span').text('Selected-compared Article: ');
    header.append('span').text('Select an article by clicking on any column node ' +
        'in any of the graphs. Hover for more information.');
};

var hideAnnotationGroup = function(viewer) {

};

var showAnnotationGroup = function(viewer) {

};

var updateTopics = function(viewer) {
    var topics = viewer.options.topics[viewer.selectedContent.label];

    viewer.topicsSelect.selectAll('option').remove();
    viewer.topicsOption = viewer.topicsSelect.selectAll('option')
        .data(topics)
        .enter().append('option')
        .attr('value', function(topic) {return topic.id;})
        .text(function(topic) {return topic.text;});

    viewer.selectedTopic = topics[0].id;
    updateDistribution(viewer);
};

var updateDistribution = function(viewer) {
    var path = viewer.options.paths[viewer.selectedContent.label];
    var topics = viewer.options.topics[viewer.selectedContent.label];
    var articles = viewer.options.articles[viewer.selectedContent.label]
    var prefix = viewer.selectedContent.prefix;

    viewer.topicArticles = _.filter(articles, function(art) {
        return art.topic === viewer.selectedTopic;
    });
    viewer.topicArticleIds = _.pluck(viewer.topicArticles, 'id');
    viewer.topicArticleAltIds = _.pluck(viewer.topicArticles, 'altId');

    loadAnnotations(viewer);

    jQuery.when.apply(null, viewer.loaders)
        .then(function() {
            var distData = viewer.parser.calculateDistribution(viewer.annotatedArticles[viewer.selectedContent.label],
                viewer.options.onlyTF);
            viewer.topicDistribution.setData(distData);
            viewer.topicDistribution.render();

            if (viewer.articleTitle !== undefined) {
                if (viewer.topicArticles.length >= 3) {
                    viewer.articleTitle.text('Click on any column in the distribution matrix to select an article and ' +
                        'display similarity network');
                } else {
                    viewer.articleTitle.text('Click on any column in the distribution matrix to select an article ' +
                        'and display annotations');
                }
                hideSimilarityGroup(viewer);
                hideAnnotationGroup(viewer);
            }
        }
    );
};

var updateSimilarity = function(viewer) {
    var queryArticle = {};
    var relatedIds = {};
    relatedIds[viewer.selectedContent.label] = _.filter(viewer.annotatedArticles[viewer.selectedContent.label],
        function(annotatedArticle) {
            if (annotatedArticle.id === viewer.selectedArticle.id) {
                queryArticle[viewer.selectedContent.label] = annotatedArticle;
            }
            return _.contains(viewer.topicArticleIds, annotatedArticle.id);
        }
    );

    var seeAlso;
    if (viewer.selectedContent.seeAlso) {
        relatedIds[viewer.selectedContent.seeAlso] = _.filter(viewer.annotatedArticles[viewer.selectedContent.seeAlso],
            function(annotatedArticle) {
                if (annotatedArticle.id === viewer.selectedArticle.altId) {
                    queryArticle[viewer.selectedContent.seeAlso] = annotatedArticle;
                }
                return _.contains(viewer.topicArticleAltIds, annotatedArticle.id);
            }
        );
        seeAlso = _.find(viewer.options.content, function(content) {
            return content.label === viewer.selectedContent.seeAlso;
        });
    }

    var keys = _.keys(viewer.similarityViewers);
    _.each(viewer.similarityViewers, function(simViewer, key) {
        if (simViewer.viewer) {
            simViewer.viewer.stopForce();
        }

        d3.select(simViewer.id).selectAll('*').remove();
        d3.select(simViewer.id).html('');

        var data = viewer.parser.calculateSimilarity(queryArticle[key], relatedIds[key]);

        var simil = new SimilarityViewer({
            el: simViewer.id,
            width: viewer.options.width / keys.length - 10,
            height: viewer.options.similarityHeight,
            data: data,
            queryId: queryArticle[key].id,
            alternativeQueryId: key === viewer.selectedContent.label
                ? queryArticle[key].altId
                : seeAlso ? queryArticle[viewer.selectedContent.label].id : undefined,
            prefixId: simViewer.content.prefix,
            alternativePrefixId: key === viewer.selectedContent.label
                ? seeAlso ? seeAlso.prefix : undefined
                : seeAlso ? viewer.selectedContent.prefix : undefined,
            useAlternativeIds: key === viewer.selectedContent.label ? false : true
        });

        viewer.similarityViewers[key].viewer = simil;
        console.log(viewer.similarityViewers);

        viewer.similarityViewers[key].viewer.getDispatcher().on('selected', function(obj) {
            if (obj.type === 'similarity') {
                console.log('node selected');
                console.log(obj);
            }/*
            var pmid = _.find(articles, function(art) {
                return art.id === obj.datum.relatedId;
            }).pmid;
            self.updateAnnotation(obj.datum.relatedId, pmid, articles);*/
        });
    });
};

var updateAnnotations = function(viewer) {

};

var loadAnnotations = function(viewer) {
    viewer.annotatedArticles = {};
    viewer.loaders = [];
    var content = viewer.selectedContent.label;
    var topic = viewer.selectedTopic;

    if (viewer.options.articles[content]) {
        viewer.annotatedArticles[content] = [];
        _.each(viewer.topicArticles, function(article) {
            loadAnnotatedArticle(viewer, content, article, viewer.options.paths[content]);
        });
        var key = viewer.selectedContent.seeAlso;
        if ((key !== undefined) && (viewer.options.articles[key])) {
            viewer.annotatedArticles[key] = [];
            _.each(viewer.options.articles[key], function(article) {
                if ((article.topic === topic) && (_.contains(viewer.topicArticleAltIds, article.id))) {
                    loadAnnotatedArticle(viewer, key, article, viewer.options.paths[key]);
                }
            });
        }
    }
};

var loadAnnotatedArticle = function(viewer, content, article, path) {
    var loader = viewer.parser.loadAnnotations(path, article.id);
    viewer.loaders.push(loader);
    loader.done(function(loadedData) {
        if (article.altId) {
            viewer.annotatedArticles[content].push({id: article.id, display: article.title, altId: article.altId,
                annotations: loadedData.data});
        } else {
            viewer.annotatedArticles[content].push({id: article.id, display: article.title,
                annotations: loadedData.data});
        }
    }).fail( function(e) {
        console.log(e);
    });
};

module.exports = BiolinksViewer;
<commit_msg>Fix bug due to similarityGroupDiv duplication.<commit_after>/*jslint node: true */
/*jshint laxbreak: true */
"use strict";
/*
 * Copyright (c) 2015 ljgarcia
 * Licensed under the Apache 2 license.
 */
var d3 = require('d3');
var _ = require('underscore');
var jQuery = require('jquery');
var BiolinksParser = require('biotea-io-parser');
var AnnotationViewer = require('biotea-vis-annotation');
var DistributionViewer = require('biotea-vis-topicDistribution');
var SimilarityViewer = require('biotea-vis-similarity');

/**
 * Private Methods
 */
/**
 * content: [{label: 'ft', text: 'Full content'}, {label: 'ta', text: 'Titla and abstract'}]
 * topics: {ft: [{id: '', text: ''}], ta: [{id: '', text: ''}]}
 * articles: {ft: [{topic: '', id: '', altId: '', title: ''}], ta: []}
*/
var defaultOpts = {
    title: 'Biotea-Biolinks',
    width: 880,
    annotationHeight: 400,
    similarityHeight: 400,
    distributionHeight: 400,
    content: [],
    topics: {},
    articles: {},
    paths: {},
    onlyTF: false
};

/*
 * Public Methods
 */
var BiolinksViewer = function(opts){
    var viewer = this;
    viewer.options = _.extend({}, defaultOpts, opts);
    viewer.parser = new BiolinksParser();
    init(viewer);
};

var init = function(viewer) {
    var controlsDiv = d3.select(viewer.options.el).append('div').classed('biolinks_menu', true);
    //TODO create group selection and scope menu

    var titleDiv = d3.select(viewer.options.el).append('h1').text(viewer.options.title);
    initContentAndTopics(viewer);
    initDistribution(viewer);
    initSimilarity(viewer);
    initAnnotations(viewer);
    updateTopics(viewer);
};

var initContentAndTopics = function(viewer) {
    var contentDiv = d3.select(viewer.options.el).append('div').classed('biolinks_content', true);
    contentDiv.append('span').text('Content/Chapter: ');
    viewer.contentSelect = contentDiv.append('span').append('select')
        .attr('id', 'biolinks_contentSelection')
        .on('change', function() {
            var selectedIndex = viewer.contentSelect.property('selectedIndex')
            viewer.selectedContent = viewer.contentOptions[0][selectedIndex].__data__;
            updateTopics(viewer);
        });

    viewer.contentOptions = viewer.contentSelect.selectAll('option')
        .data(viewer.options.content)
        .enter().append('option')
        .attr('value', function(d) {return d.label;})
        .text(function(d) {return d.text;});
    viewer.selectedContent = viewer.options.content[0];

    var topicsDiv = d3.select(viewer.options.el).append('div');
    topicsDiv.append('span').text('Topic/Subject: ');
    viewer.topicsSelect = topicsDiv.append('span').append('select')
        .attr('id', 'biolinks_topicsSelection')
        .on('change', function() {
            var selectedIndex = viewer.topicsSelect.property('selectedIndex')
            viewer.selectedTopic = viewer.topicsOption[0][selectedIndex].__data__.id;
            updateDistribution(viewer);
        });
};

var initDistribution = function(viewer) {
    var distributionGroupDiv = d3.select(viewer.options.el).append('div').classed('biolinks_distribution_group', true);
    distributionGroupDiv.append('h2').text('Biolinks (UMLS-based) group distribution')

    distributionGroupDiv.append('div').classed('biolinks_caption', true)
        .html('Rows are semantic groups and columns are articles, the darker a cell the more representative the '
            + 'semantic group for an article.
White means the group is not present in the article. '
            + '
Hover and click any cell for more information and options');

    var articleDiv = distributionGroupDiv.append('div').html('');
    var header = articleDiv.append('h4');
    header.append('span').text('Selected-reference Article: ');
    viewer.articleTitle = header.append('span').text('Select an article by clicking on any column in the ' +
        'distribution matrix. Hover for more information.');

    var distDivId = 'biolinks_dist_' + new Date().getTime();
    viewer.distributionDiv = distributionGroupDiv.append('div')
        .attr('id', distDivId)
        .classed('biolinks_distribution', true);
    viewer.topicDistribution = new DistributionViewer({
        el: '#' + distDivId,
        width: viewer.options.width
    });

    viewer.topicDistribution.getDispatcher().on('selected', function(obj) {
        if (obj.type === 'distribution') {
            var collection = viewer.options.articles[viewer.selectedContent.label];
            viewer.selectedArticle = _.find(collection, function(el) {
                return +obj.article === +el.id;
            });

            var articleText = viewer.selectedArticle.title + ' (' + viewer.selectedContent.prefix + ':' +
                viewer.selectedArticle.id + ')';
            viewer.articleTitle.text(articleText);

            if (viewer.topicArticles.length >= 3) {
                showSimilarityGroup(viewer);
                updateSimilarity(viewer);
            } else {
               showAnnotationGroup(viewer);
               updateAnnotations(viewer);
            }

        }
    });
};

var initSimilarity = function(viewer) {
    viewer.similarityGroupDiv = d3.select(viewer.options.el).append('div')
        .classed('biolinks_similarity_group', true)
        .style('display', 'none');
    viewer.similarityGroupDiv.append('h2').text('Similarity network for selected-reference article');
    viewer.secondaryArticleDiv = viewer.similarityGroupDiv.append('div');

    viewer.similarityViewers = {};
    var table = viewer.similarityGroupDiv.append('table').attr('width', '100%');
    var temp = [];
    var row = table.append('tr');
    if (viewer.selectedContent.seeAlso) {
        row.append('th').text('Similarity for ' + viewer.selectedContent.text);
        var seeAlso = _.find(viewer.options.content, function(content) {
            return content.label === viewer.selectedContent.seeAlso;
        });
        row.append('th').text('Similarity for ' + seeAlso.text);
        temp = [viewer.selectedContent, seeAlso];
    } else {
        row.append('th').text('Similarity for ' + viewer.selectedContent.text);
        temp = [viewer.selectedContent];
    }
    row = table.append('tr');
    _.each(temp, function(content) {
        var divId = 'biolinks_sim_' + content.label + '_' + new Date().getTime();
        var simViewer = row.append('td').append('div').attr('id', divId);
        viewer.similarityViewers[content.label] = {
            display: simViewer,
            id: '#' + divId,
            content: content
        };
    });
};

var initAnnotations = function(viewer) {
    viewer.annotationGroupDiv = d3.select(viewer.options.el).append('div')
        .classed('biolinks_annotation_group', true)
        .style('display', 'none');
    viewer.annotationGroupDiv.append('h2').text('Semantic annotations for selected articles');
};

var hideSimilarityGroup = function(viewer) {
    viewer.similarityGroupDiv.style('display', 'none');
    viewer.secondaryArticleDiv.selectAll('*').remove();
    viewer.secondaryArticleDiv.html('');
    _.each(viewer.similarityViewers, function(simViewer) {
        d3.select(simViewer.id).selectAll('*').remove();
        d3.select(simViewer.id).html('');
    });
};

var showSimilarityGroup = function(viewer) {
    viewer.similarityGroupDiv.style('display', 'block');
    var header = viewer.secondaryArticleDiv.append('h4');
    header.append('span').text('Selected-compared Article: ');
    header.append('span').text('Select an article by clicking on any column node ' +
       'in any of the graphs. Hover for more information.');
};

var hideAnnotationGroup = function(viewer) {

};

var showAnnotationGroup = function(viewer) {

};

var updateTopics = function(viewer) {
    var topics = viewer.options.topics[viewer.selectedContent.label];

    viewer.topicsSelect.selectAll('option').remove();
    viewer.topicsOption = viewer.topicsSelect.selectAll('option')
        .data(topics)
        .enter().append('option')
        .attr('value', function(topic) {return topic.id;})
        .text(function(topic) {return topic.text;});

    viewer.selectedTopic = topics[0].id;
    updateDistribution(viewer);
};

var updateDistribution = function(viewer) {
    var path = viewer.options.paths[viewer.selectedContent.label];
    var topics = viewer.options.topics[viewer.selectedContent.label];
    var articles = viewer.options.articles[viewer.selectedContent.label]
    var prefix = viewer.selectedContent.prefix;

    viewer.topicArticles = _.filter(articles, function(art) {
        return art.topic === viewer.selectedTopic;
    });
    viewer.topicArticleIds = _.pluck(viewer.topicArticles, 'id');
    viewer.topicArticleAltIds = _.pluck(viewer.topicArticles, 'altId');

    loadAnnotations(viewer);

    jQuery.when.apply(null, viewer.loaders)
        .then(function() {
            var distData = viewer.parser.calculateDistribution(viewer.annotatedArticles[viewer.selectedContent.label],
                viewer.options.onlyTF);
            viewer.topicDistribution.setData(distData);
            viewer.topicDistribution.render();

            if (viewer.articleTitle !== undefined) {
                if (viewer.topicArticles.length >= 3) {
                    viewer.articleTitle.text('Click on any column in the distribution matrix to select an article and ' +
                        'display similarity network');
                } else {
                    viewer.articleTitle.text('Click on any column in the distribution matrix to select an article ' +
                        'and display annotations');
                }
                hideSimilarityGroup(viewer);
                hideAnnotationGroup(viewer);
            }
        }
    );
};

var updateSimilarity = function(viewer) {
    var queryArticle = {};
    var relatedIds = {};
    relatedIds[viewer.selectedContent.label] = _.filter(viewer.annotatedArticles[viewer.selectedContent.label],
        function(annotatedArticle) {
            if (annotatedArticle.id === viewer.selectedArticle.id) {
                queryArticle[viewer.selectedContent.label] = annotatedArticle;
            }
            return _.contains(viewer.topicArticleIds, annotatedArticle.id);
        }
    );

    var seeAlso;
    if (viewer.selectedContent.seeAlso) {
        relatedIds[viewer.selectedContent.seeAlso] = _.filter(viewer.annotatedArticles[viewer.selectedContent.seeAlso],
            function(annotatedArticle) {
                if (annotatedArticle.id === viewer.selectedArticle.altId) {
                    queryArticle[viewer.selectedContent.seeAlso] = annotatedArticle;
                }
                return _.contains(viewer.topicArticleAltIds, annotatedArticle.id);
            }
        );
        seeAlso = _.find(viewer.options.content, function(content) {
            return content.label === viewer.selectedContent.seeAlso;
        });
    }

    var keys = _.keys(viewer.similarityViewers);
    _.each(viewer.similarityViewers, function(simViewer, key) {
        if (simViewer.viewer) {
            simViewer.viewer.stopForce();
        }

        d3.select(simViewer.id).selectAll('*').remove();
        d3.select(simViewer.id).html('');

        var data = viewer.parser.calculateSimilarity(queryArticle[key], relatedIds[key]);

        var simil = new SimilarityViewer({
            el: simViewer.id,
            width: viewer.options.width / keys.length - 10,
            height: viewer.options.similarityHeight,
            data: data,
            queryId: queryArticle[key].id,
            alternativeQueryId: key === viewer.selectedContent.label
                ? queryArticle[key].altId
                : seeAlso ? queryArticle[viewer.selectedContent.label].id : undefined,
            prefixId: simViewer.content.prefix,
            alternativePrefixId: key === viewer.selectedContent.label
                ? seeAlso ? seeAlso.prefix : undefined
                : seeAlso ? viewer.selectedContent.prefix : undefined,
            useAlternativeIds: key === viewer.selectedContent.label ? false : true
        });

        viewer.similarityViewers[key].viewer = simil;
        console.log(viewer.similarityViewers);

        viewer.similarityViewers[key].viewer.getDispatcher().on('selected', function(obj) {
            if (obj.type === 'similarity') {
                console.log('node selected');
                console.log(obj);
            }
        });
    });
};

var updateAnnotations = function(viewer) {

};

var loadAnnotations = function(viewer) {
    viewer.annotatedArticles = {};
    viewer.loaders = [];
    var content = viewer.selectedContent.label;
    var topic = viewer.selectedTopic;

    if (viewer.options.articles[content]) {
        viewer.annotatedArticles[content] = [];
        _.each(viewer.topicArticles, function(article) {
            loadAnnotatedArticle(viewer, content, article, viewer.options.paths[content]);
        });
        var key = viewer.selectedContent.seeAlso;
        if ((key !== undefined) && (viewer.options.articles[key])) {
            viewer.annotatedArticles[key] = [];
            _.each(viewer.options.articles[key], function(article) {
                if ((article.topic === topic) && (_.contains(viewer.topicArticleAltIds, article.id))) {
                    loadAnnotatedArticle(viewer, key, article, viewer.options.paths[key]);
                }
            });
        }
    }
};

var loadAnnotatedArticle = function(viewer, content, article, path) {
    var loader = viewer.parser.loadAnnotations(path, article.id);
    viewer.loaders.push(loader);
    loader.done(function(loadedData) {
        if (article.altId) {
            viewer.annotatedArticles[content].push({id: article.id, display: article.title, altId: article.altId,
                annotations: loadedData.data});
        } else {
            viewer.annotatedArticles[content].push({id: article.id, display: article.title,
                annotations: loadedData.data});
        }
    }).fail( function(e) {
        console.log(e);
    });
};

module.exports = BiolinksViewer;
<|endoftext|>


<commit_before>'use strict';

var _         = require('underscore'),
    path      = require('path'),
    winston   = module.parent.require('winston'),
    meta      = module.parent.require('./meta'),

    settings  = null,
    namespace = 'ns:custom-fields';

(function (Plugin) {
    Plugin.hooks = {
        filters: {
            menu   : function (custom_header, callback) {
                custom_header.plugins.push({
                    route: '/plugins/custom-fields',
                    icon : 'fa-plus-square',
                    name : 'Custom Fields'
                });
                callback(null, custom_header);
            },
            statics: {
                load: function (params, callback) {
                    var router      = params.router,
                        middleware  = params.middleware,
                        controllers = params.controllers;

                    meta.settings.get(namespace, function (err, config) {
                        if (err) {
                            winston.warn('[' + namespace + '] Error has been occurred! There is no configuration.');
                            return callback(err);
                        }

                        settings = config;

                        router.get('/admin/plugins/custom-fields', middleware.applyCSRF, middleware.admin.buildHeader, Plugin.renderAdmin);
                        router.get('/api/admin/plugins/custom-fields', middleware.applyCSRF, Plugin.renderAdmin);

                        callback();
                    });
                }
            }
        }
    };

    Plugin.renderAdmin = function (req, res, next) {
        res.render(
            'admin/plugins/custom-fields',
            {
                settings: settings || {}
            }
        );
    };

})(module.exports);<commit_msg>Fix statics<commit_after>'use strict';

var _         = require('underscore'),
    path      = require('path'),
    winston   = module.parent.require('winston'),
    meta      = module.parent.require('./meta'),

    settings  = null,
    namespace = 'ns:custom_fields';

(function (Plugin) {
    Plugin.hooks = {
        filters: {
            menu: function (custom_header, callback) {
                custom_header.plugins.push({
                    route: '/plugins/custom-fields',
                    icon : 'fa-plus-square',
                    name : 'Custom Fields'
                });
                callback(null, custom_header);
            }
        },
        statics: {
            load: function (params, callback) {
                var router      = params.router,
                    middleware  = params.middleware,
                    controllers = params.controllers;

                meta.settings.get(namespace, function (err, config) {
                    if (err) {
                        winston.warn('[' + namespace + '] Error has been occurred! There is no configuration.');
                        return callback(err);
                    }

                    settings = config;

                    router.get('/admin/plugins/custom-fields', middleware.applyCSRF, middleware.admin.buildHeader, Plugin.renderAdmin);
                    router.get('/api/admin/plugins/custom-fields', middleware.applyCSRF, Plugin.renderAdmin);

                    callback();
                });
            }
        }
    };

    Plugin.renderAdmin = function (req, res, next) {
        res.render(
            'admin/plugins/custom-fields',
            {
                settings: settings || {}
            }
        );
    };

})(module.exports);<|endoftext|>


<commit_before>'use strict';

var inherits = require('util').inherits;
var EventEmitter = require('events').EventEmitter;
var once = require('./util/once').once;

module.exports = {
  Characteristic: Characteristic
};

/**
 * Characteristic represents a particular typed variable that can be assigned to a Service. For instance, a
 * "Hue" Characteristic might store a 'float' value of type 'arcdegrees'. You could add the Hue Characteristic
 * to a Service in order to store that value. A particular Characteristic is distinguished from others by its
 * UUID. HomeKit provides a set of known Characteristic UUIDs defined in HomeKitTypes.js along with a
 * corresponding concrete subclass.
 *
 * You can also define custom Characteristics by providing your own UUID. Custom Characteristics can be added
 * to any native or custom Services, but Siri will likely not be able to work with these.
 *
 * Note that you can get the "value" of a Characteristic by accessing the "value" property directly, but this
 * is really a "cached value". If you want to fetch the latest value, which may involve doing some work, then
 * call getValue().
 *
 * @event 'get' => function(callback(err, newValue), context) { }
 *        Emitted when someone calls getValue() on this Characteristic and desires the latest non-cached
 *        value. If there are any listeners to this event, one of them MUST call the callback in order
 *        for the value to ever be delivered. The `context` object is whatever was passed in by the initiator
 *        of this event (for instance whomever called `getValue`).
 *
 * @event 'set' => function(newValue, callback(err), context) { }
 *        Emitted when someone calls setValue() on this Characteristic with a desired new value. If there
 *        are any listeners to this event, one of them MUST call the callback in order for this.value to
 *        actually be set. The `context` object is whatever was passed in by the initiator of this change
 *        (for instance, whomever called `setValue`).
 *
 * @event 'change' => function({ oldValue, newValue, context }) { }
 *        Emitted after a change in our value has occurred. The new value will also be immediately accessible
 *        in this.value. The event object contains the new value as well as the context object originally
 *        passed in by the initiator of this change (if known).
 */

function Characteristic(displayName, UUID, props) {
  this.displayName = displayName;
  this.UUID = UUID;
  this.iid = null; // assigned by our containing Service
  this.value = null;
  this.eventOnlyCharacteristic = false;
  this.props = props || {
    format: null,
    unit: null,
    minValue: null,
    maxValue: null,
    minStep: null,
    perms: []
  };
}

inherits(Characteristic, EventEmitter);

// Known HomeKit formats
Characteristic.Formats = {
  BOOL: 'bool',
  INT: 'int',
  FLOAT: 'float',
  STRING: 'string',
  ARRAY: 'array', // unconfirmed
  DICTIONARY: 'dictionary', // unconfirmed
  UINT8: 'uint8',
  UINT16: 'uint16',
  UINT32: 'uint32',
  UINT64: 'uint64',
  DATA: 'data', // unconfirmed
  TLV8: 'tlv8'
}

// Known HomeKit unit types
Characteristic.Units = {
  // HomeKit only defines Celsius, for Fahrenheit, it requires iOS app to do the conversion.
  CELSIUS: 'celsius',
  PERCENTAGE: 'percentage',
  ARC_DEGREE: 'arcdegrees',
  LUX: 'lux',
  SECONDS: 'seconds'
}

// Known HomeKit permission types
Characteristic.Perms = {
  READ: 'pr',
  WRITE: 'pw',
  NOTIFY: 'ev',
  HIDDEN: 'hd'
}

/**
 * Copies the given properties to our props member variable,
 * and returns 'this' for chaining.
 *
 * @param 'props' {
 *   format: <one of Characteristic.Formats>,
 *   unit: <one of Characteristic.Units>,
 *   minValue: <minimum value for numeric characteristics>,
 *   maxValue: <maximum value for numeric characteristics>,
 *   minStep: <smallest allowed increment for numeric characteristics>,
 *   perms: array of [Characteristic.Perms] like [Characteristic.Perms.READ, Characteristic.Perms.WRITE]
 * }
 */
Characteristic.prototype.setProps = function(props) {
  for (var key in (props || {}))
    if (Object.prototype.hasOwnProperty.call(props, key))
      this.props[key] = props[key];
  return this;
}

Characteristic.prototype.getValue = function(callback, context, connectionID) {
  // Handle special event only characteristics.
  if (this.eventOnlyCharacteristic === true) {
    if (callback) {
      callback(null, null);
    }

    return;
  }

  if (this.listeners('get').length > 0) {

    // allow a listener to handle the fetching of this value, and wait for completion
    this.emit('get', once(function(err, newValue) {

      if (err) {
        // pass the error along to our callback
        if (callback) callback(err);
      }
      else {
        if (newValue === undefined || newValue === null)
          newValue = this.getDefaultValue();

        // getting the value was a success; we can pass it along and also update our cached value
        var oldValue = this.value;
        this.value = newValue;
        if (callback) callback(null, newValue);

        // emit a change event if necessary
        if (oldValue !== newValue)
          this.emit('change', { oldValue:oldValue, newValue:newValue, context:context });
      }

    }.bind(this)), context, connectionID);
  }
  else {

    // no one is listening to the 'get' event, so just return the cached value
    if (callback)
      callback(null, this.value);
  }
}

Characteristic.prototype.setValue = function(newValue, callback, context, connectionID) {

  if (this.listeners('set').length > 0) {

    // allow a listener to handle the setting of this value, and wait for completion
    this.emit('set', newValue, once(function(err) {

      if (err) {
        // pass the error along to our callback
        if (callback) callback(err);
      }
      else {
        if (newValue === undefined || newValue === null)
          newValue = this.getDefaultValue();
        // setting the value was a success; so we can cache it now
        var oldValue = this.value;
        this.value = newValue;
        if (callback) callback();

        if (this.eventOnlyCharacteristic === false && oldValue !== newValue)
          this.emit('change', { oldValue:oldValue, newValue:newValue, context:context });
      }

    }.bind(this)), context, connectionID);

  }
  else {
    if (newValue === undefined || newValue === null)
      newValue = this.getDefaultValue();
    // no one is listening to the 'set' event, so just assign the value blindly
    var oldValue = this.value;
    this.value = newValue;
    if (callback) callback();

    if (this.eventOnlyCharacteristic === false && oldValue !== newValue)
      this.emit('change', { oldValue:oldValue, newValue:newValue, context:context });
  }

  return this; // for chaining
}

Characteristic.prototype.updateValue = function(newValue, callback, context) {

  if (newValue === undefined || newValue === null)
    newValue = this.getDefaultValue();
    // no one is listening to the 'set' event, so just assign the value blindly
  var oldValue = this.value;
  this.value = newValue;
  if (callback) callback();

  if (this.eventOnlyCharacteristic === false && oldValue !== newValue)
    this.emit('change', { oldValue:oldValue, newValue:newValue, context:context });
  return this; // for chaining
}

Characteristic.prototype.getDefaultValue = function() {
  switch (this.props.format) {
    case Characteristic.Formats.BOOL: return false;
    case Characteristic.Formats.STRING: return "";
    case Characteristic.Formats.ARRAY: return []; // who knows!
    case Characteristic.Formats.DICTIONARY: return {}; // who knows!
    case Characteristic.Formats.DATA: return ""; // who knows!
    case Characteristic.Formats.TLV8: return ""; // who knows!
    default: return this.props.minValue || 0;
  }
}

Characteristic.prototype._assignID = function(identifierCache, accessoryName, serviceUUID, serviceSubtype) {

  // generate our IID based on our UUID
  this.iid = identifierCache.getIID(accessoryName, serviceUUID, serviceSubtype, this.UUID);
}

/**
 * Returns a JSON representation of this Accessory suitable for delivering to HAP clients.
 */
Characteristic.prototype.toHAP = function(opt) {

  // ensure our value fits within our constraints if present
  var value = this.value;
  if (this.props.minValue != null && value < this.props.minValue) value = this.props.minValue;
  if (this.props.maxValue != null && value > this.props.maxValue) value = this.props.maxValue;
  if (this.props.format != null) {
    if (this.props.format === Characteristic.Formats.INT)
      value = parseInt(value);
    else if (this.props.format === Characteristic.Formats.UINT8)
      value = parseInt(value);
    else if (this.props.format === Characteristic.Formats.UINT16)
      value = parseInt(value);
    else if (this.props.format === Characteristic.Formats.UINT32)
      value = parseInt(value);
    else if (this.props.format === Characteristic.Formats.UINT64)
      value = parseInt(value);
    else if (this.props.format === Characteristic.Formats.FLOAT) {
      value = parseFloat(value);
      if (this.props.minStep != null) {
        var pow = Math.pow(10, decimalPlaces(this.props.minStep));
        value = Math.round(value * pow) / pow;
      }
    }
  }

  if (this.eventOnlyCharacteristic === true) {
    value = null;
  }

  var hap = {
    iid: this.iid,
    type: this.UUID,
    perms: this.props.perms,
    format: this.props.format,
    value: value,
    description: this.displayName

    // These properties used to be sent but do not seem to be used:
    //
    // events: false,
    // bonjour: false
  };

  if (this.props.validValues != null && this.props.validValues.length > 0) {
    hap['valid-values'] = this.props.validValues;
  }

  if (this.props.validValueRanges != null && this.props.validValueRanges.length > 0 && !(this.props.validValueRanges.length & 1)) {
    hap['valid-values-range'] = this.props.validValueRanges;
  }

  // extra properties
  if (this.props.unit != null) hap.unit = this.props.unit;
  if (this.props.maxValue != null) hap.maxValue = this.props.maxValue;
  if (this.props.minValue != null) hap.minValue = this.props.minValue;
  if (this.props.minStep != null) hap.minStep = this.props.minStep;

  // add maxLen if string length is > 64 bytes and trim to max 256 bytes
  if (this.props.format === Characteristic.Formats.STRING) {
    var str = new Buffer(value, 'utf8'),
        len = str.byteLength;
    if (len > 256) { // 256 bytes is the max allowed length
      hap.value = str.toString('utf8', 0, 256);
      hap.maxLen = 256;
    } else if (len > 64) { // values below can be ommited
      hap.maxLen = len;
    }
  }

  // if we're not readable, omit the "value" property - otherwise iOS will complain about non-compliance
  if (this.props.perms.indexOf(Characteristic.Perms.READ) == -1)
    delete hap.value;

  // delete the "value" property anyway if we were asked to
  if (opt && opt.omitValues)
    delete hap.value;

  return hap;
}

// Mike Samuel
// http://stackoverflow.com/questions/10454518/javascript-how-to-retrieve-the-number-of-decimals-of-a-string-number
function decimalPlaces(num) {
  var match = (''+num).match(/(?:\.(\d+))?(?:[eE]([+-]?\d+))?$/);
  if (!match) { return 0; }
  return Math.max(
       0,
       // Number of digits right of decimal point.
       (match[1] ? match[1].length : 0)
       // Adjust for scientific notation.
       - (match[2] ? +match[2] : 0));
}
<commit_msg>Fix update logic<commit_after>'use strict';

var inherits = require('util').inherits;
var EventEmitter = require('events').EventEmitter;
var once = require('./util/once').once;

module.exports = {
  Characteristic: Characteristic
};

/**
 * Characteristic represents a particular typed variable that can be assigned to a Service. For instance, a
 * "Hue" Characteristic might store a 'float' value of type 'arcdegrees'. You could add the Hue Characteristic
 * to a Service in order to store that value. A particular Characteristic is distinguished from others by its
 * UUID. HomeKit provides a set of known Characteristic UUIDs defined in HomeKitTypes.js along with a
 * corresponding concrete subclass.
 *
 * You can also define custom Characteristics by providing your own UUID. Custom Characteristics can be added
 * to any native or custom Services, but Siri will likely not be able to work with these.
 *
 * Note that you can get the "value" of a Characteristic by accessing the "value" property directly, but this
 * is really a "cached value". If you want to fetch the latest value, which may involve doing some work, then
 * call getValue().
 *
 * @event 'get' => function(callback(err, newValue), context) { }
 *        Emitted when someone calls getValue() on this Characteristic and desires the latest non-cached
 *        value. If there are any listeners to this event, one of them MUST call the callback in order
 *        for the value to ever be delivered. The `context` object is whatever was passed in by the initiator
 *        of this event (for instance whomever called `getValue`).
 *
 * @event 'set' => function(newValue, callback(err), context) { }
 *        Emitted when someone calls setValue() on this Characteristic with a desired new value. If there
 *        are any listeners to this event, one of them MUST call the callback in order for this.value to
 *        actually be set. The `context` object is whatever was passed in by the initiator of this change
 *        (for instance, whomever called `setValue`).
 *
 * @event 'change' => function({ oldValue, newValue, context }) { }
 *        Emitted after a change in our value has occurred. The new value will also be immediately accessible
 *        in this.value. The event object contains the new value as well as the context object originally
 *        passed in by the initiator of this change (if known).
 */

function Characteristic(displayName, UUID, props) {
  this.displayName = displayName;
  this.UUID = UUID;
  this.iid = null; // assigned by our containing Service
  this.value = null;
  this.eventOnlyCharacteristic = false;
  this.props = props || {
    format: null,
    unit: null,
    minValue: null,
    maxValue: null,
    minStep: null,
    perms: []
  };
}

inherits(Characteristic, EventEmitter);

// Known HomeKit formats
Characteristic.Formats = {
  BOOL: 'bool',
  INT: 'int',
  FLOAT: 'float',
  STRING: 'string',
  ARRAY: 'array', // unconfirmed
  DICTIONARY: 'dictionary', // unconfirmed
  UINT8: 'uint8',
  UINT16: 'uint16',
  UINT32: 'uint32',
  UINT64: 'uint64',
  DATA: 'data', // unconfirmed
  TLV8: 'tlv8'
}

// Known HomeKit unit types
Characteristic.Units = {
  // HomeKit only defines Celsius, for Fahrenheit, it requires iOS app to do the conversion.
  CELSIUS: 'celsius',
  PERCENTAGE: 'percentage',
  ARC_DEGREE: 'arcdegrees',
  LUX: 'lux',
  SECONDS: 'seconds'
}

// Known HomeKit permission types
Characteristic.Perms = {
  READ: 'pr',
  WRITE: 'pw',
  NOTIFY: 'ev',
  HIDDEN: 'hd'
}

/**
 * Copies the given properties to our props member variable,
 * and returns 'this' for chaining.
 *
 * @param 'props' {
 *   format: <one of Characteristic.Formats>,
 *   unit: <one of Characteristic.Units>,
 *   minValue: <minimum value for numeric characteristics>,
 *   maxValue: <maximum value for numeric characteristics>,
 *   minStep: <smallest allowed increment for numeric characteristics>,
 *   perms: array of [Characteristic.Perms] like [Characteristic.Perms.READ, Characteristic.Perms.WRITE]
 * }
 */
Characteristic.prototype.setProps = function(props) {
  for (var key in (props || {}))
    if (Object.prototype.hasOwnProperty.call(props, key))
      this.props[key] = props[key];
  return this;
}

Characteristic.prototype.getValue = function(callback, context, connectionID) {
  // Handle special event only characteristics.
  if (this.eventOnlyCharacteristic === true) {
    if (callback) {
      callback(null, null);
    }

    return;
  }

  if (this.listeners('get').length > 0) {

    // allow a listener to handle the fetching of this value, and wait for completion
    this.emit('get', once(function(err, newValue) {

      if (err) {
        // pass the error along to our callback
        if (callback) callback(err);
      }
      else {
        if (newValue === undefined || newValue === null)
          newValue = this.getDefaultValue();

        // getting the value was a success; we can pass it along and also update our cached value
        var oldValue = this.value;
        this.value = newValue;
        if (callback) callback(null, newValue);

        // emit a change event if necessary
        if (oldValue !== newValue)
          this.emit('change', { oldValue:oldValue, newValue:newValue, context:context });
      }

    }.bind(this)), context, connectionID);
  }
  else {

    // no one is listening to the 'get' event, so just return the cached value
    if (callback)
      callback(null, this.value);
  }
}

Characteristic.prototype.setValue = function(newValue, callback, context, connectionID) {

  if (this.listeners('set').length > 0) {

    // allow a listener to handle the setting of this value, and wait for completion
    this.emit('set', newValue, once(function(err) {

      if (err) {
        // pass the error along to our callback
        if (callback) callback(err);
      }
      else {
        if (newValue === undefined || newValue === null)
          newValue = this.getDefaultValue();
        // setting the value was a success; so we can cache it now
        var oldValue = this.value;
        this.value = newValue;
        if (callback) callback();

        if (this.eventOnlyCharacteristic === true || oldValue !== newValue)
          this.emit('change', { oldValue:oldValue, newValue:newValue, context:context });
      }

    }.bind(this)), context, connectionID);

  }
  else {
    if (newValue === undefined || newValue === null)
      newValue = this.getDefaultValue();
    // no one is listening to the 'set' event, so just assign the value blindly
    var oldValue = this.value;
    this.value = newValue;
    if (callback) callback();

    if (this.eventOnlyCharacteristic === true || oldValue !== newValue)
      this.emit('change', { oldValue:oldValue, newValue:newValue, context:context });
  }

  return this; // for chaining
}

Characteristic.prototype.updateValue = function(newValue, callback, context) {

  if (newValue === undefined || newValue === null)
    newValue = this.getDefaultValue();
    // no one is listening to the 'set' event, so just assign the value blindly
  var oldValue = this.value;
  this.value = newValue;
  if (callback) callback();

  if (this.eventOnlyCharacteristic === true || oldValue !== newValue)
    this.emit('change', { oldValue:oldValue, newValue:newValue, context:context });
  return this; // for chaining
}

Characteristic.prototype.getDefaultValue = function() {
  switch (this.props.format) {
    case Characteristic.Formats.BOOL: return false;
    case Characteristic.Formats.STRING: return "";
    case Characteristic.Formats.ARRAY: return []; // who knows!
    case Characteristic.Formats.DICTIONARY: return {}; // who knows!
    case Characteristic.Formats.DATA: return ""; // who knows!
    case Characteristic.Formats.TLV8: return ""; // who knows!
    default: return this.props.minValue || 0;
  }
}

Characteristic.prototype._assignID = function(identifierCache, accessoryName, serviceUUID, serviceSubtype) {

  // generate our IID based on our UUID
  this.iid = identifierCache.getIID(accessoryName, serviceUUID, serviceSubtype, this.UUID);
}

/**
 * Returns a JSON representation of this Accessory suitable for delivering to HAP clients.
 */
Characteristic.prototype.toHAP = function(opt) {

  // ensure our value fits within our constraints if present
  var value = this.value;
  if (this.props.minValue != null && value < this.props.minValue) value = this.props.minValue;
  if (this.props.maxValue != null && value > this.props.maxValue) value = this.props.maxValue;
  if (this.props.format != null) {
    if (this.props.format === Characteristic.Formats.INT)
      value = parseInt(value);
    else if (this.props.format === Characteristic.Formats.UINT8)
      value = parseInt(value);
    else if (this.props.format === Characteristic.Formats.UINT16)
      value = parseInt(value);
    else if (this.props.format === Characteristic.Formats.UINT32)
      value = parseInt(value);
    else if (this.props.format === Characteristic.Formats.UINT64)
      value = parseInt(value);
    else if (this.props.format === Characteristic.Formats.FLOAT) {
      value = parseFloat(value);
      if (this.props.minStep != null) {
        var pow = Math.pow(10, decimalPlaces(this.props.minStep));
        value = Math.round(value * pow) / pow;
      }
    }
  }

  if (this.eventOnlyCharacteristic === true) {
    value = null;
  }

  var hap = {
    iid: this.iid,
    type: this.UUID,
    perms: this.props.perms,
    format: this.props.format,
    value: value,
    description: this.displayName

    // These properties used to be sent but do not seem to be used:
    //
    // events: false,
    // bonjour: false
  };

  if (this.props.validValues != null && this.props.validValues.length > 0) {
    hap['valid-values'] = this.props.validValues;
  }

  if (this.props.validValueRanges != null && this.props.validValueRanges.length > 0 && !(this.props.validValueRanges.length & 1)) {
    hap['valid-values-range'] = this.props.validValueRanges;
  }

  // extra properties
  if (this.props.unit != null) hap.unit = this.props.unit;
  if (this.props.maxValue != null) hap.maxValue = this.props.maxValue;
  if (this.props.minValue != null) hap.minValue = this.props.minValue;
  if (this.props.minStep != null) hap.minStep = this.props.minStep;

  // add maxLen if string length is > 64 bytes and trim to max 256 bytes
  if (this.props.format === Characteristic.Formats.STRING) {
    var str = new Buffer(value, 'utf8'),
        len = str.byteLength;
    if (len > 256) { // 256 bytes is the max allowed length
      hap.value = str.toString('utf8', 0, 256);
      hap.maxLen = 256;
    } else if (len > 64) { // values below can be ommited
      hap.maxLen = len;
    }
  }

  // if we're not readable, omit the "value" property - otherwise iOS will complain about non-compliance
  if (this.props.perms.indexOf(Characteristic.Perms.READ) == -1)
    delete hap.value;

  // delete the "value" property anyway if we were asked to
  if (opt && opt.omitValues)
    delete hap.value;

  return hap;
}

// Mike Samuel
// http://stackoverflow.com/questions/10454518/javascript-how-to-retrieve-the-number-of-decimals-of-a-string-number
function decimalPlaces(num) {
  var match = (''+num).match(/(?:\.(\d+))?(?:[eE]([+-]?\d+))?$/);
  if (!match) { return 0; }
  return Math.max(
       0,
       // Number of digits right of decimal point.
       (match[1] ? match[1].length : 0)
       // Adjust for scientific notation.
       - (match[2] ? +match[2] : 0));
}
<|endoftext|>


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

	var Youtube = {},
		embed = '';

	Youtube.parse = function(postContent, callback) {
		postContent = postContent.replace(/<a href="(?:http:\/\/)?(?:www\.)?(?:youtube\.com|youtu\.be)\/(?:watch\?v=)?(.+)<\/a>/g, embed);
		callback(null, postContent);
	};

	module.exports = Youtube;
}(module));<commit_msg>tyvm stackoverflow (Y)<commit_after>(function(module) {
	"use strict";

	var Youtube = {},
		embed = '';

	Youtube.parse = function(postContent, callback) {
		// http://stackoverflow.com/questions/7168987/
		postContent = postContent.replace(/<a href="(?:http:\/\/)?(?:www\.)?(?:youtube\.com|youtu\.be)\/(?:watch\?v=)?(.+)<\/a>/g, embed);
		callback(null, postContent);
	};

	module.exports = Youtube;
}(module));<|endoftext|>


<commit_before>var inherits = require('util').inherits;
var EventEmitter = require('events').EventEmitter;
var once = require('./util/once').once;

'use strict';

module.exports = {
  Characteristic: Characteristic
}

/**
 * Characteristic represents a particular typed variable that can be assigned to a Service. For instance, a
 * "Hue" Characteristic might store a 'float' value of type 'arcdegrees'. You could add the Hue Characteristic
 * to a Service in order to store that value. A particular Characteristic is distinguished from others by its
 * UUID. HomeKit provides a set of known Characteristic UUIDs defined in HomeKitTypes.js along with a
 * corresponding concrete subclass.
 *
 * You can also define custom Characteristics by providing your own UUID. Custom Characteristics can be added
 * to any native or custom Services, but Siri will likely not be able to work with these.
 *
 * Note that you can get the "value" of a Characteristic by accessing the "value" property directly, but this
 * is really a "cached value". If you want to fetch the latest value, which may involve doing some work, then
 * call getValue().
 *
 * @event 'get' => function(callback(err, newValue), context) { }
 *        Emitted when someone calls getValue() on this Characteristic and desires the latest non-cached
 *        value. If there are any listeners to this event, one of them MUST call the callback in order
 *        for the value to ever be delivered. The `context` object is whatever was passed in by the initiator
 *        of this event (for instance whomever called `getValue`).
 *
 * @event 'set' => function(newValue, callback(err, changedNewValue), context) { }
 *        Emitted when someone calls setValue() on this Characteristic with a desired new value. If there
 *        are any listeners to this event, one of them MUST call the callback in order for this.value to
 *        actually be set. Note that you can optionally set a *different* value than the one requested by
 *        passing in the changedNewValue argument to the callback. The `context` object is whatever was
 *        passed in by the initiator of this change (for instance whomever called `setValue`).
 *
 * @event 'change' => function({ oldValue, newValue, context }) { }
 *        Emitted after a change in our value has occurred. The new value will also be immediately accessible
 *        in this.value. The event object contains the new value as well as the context object originally
 *        passed in by the initiator of this change (if known).
 */

function Characteristic(displayName, UUID, props) {
  this.displayName = displayName;
  this.UUID = UUID;
  this.iid = null; // assigned by our containing Service
  this.value = null;
  this.props = props || {
    format: null,
    unit: null,
    minValue: null,
    maxValue: null,
    minStep: null,
    perms: []
  };
}

inherits(Characteristic, EventEmitter);

// Known HomeKit formats
Characteristic.Formats = {
  BOOL: 'bool',
  INT: 'int',
  FLOAT: 'float',
  STRING: 'string',
  ARRAY: 'array', // unconfirmed
  DICTIONARY: 'dictionary', // unconfirmed
  UINT8: 'uint8',
  UINT16: 'uint16',
  UINT32: 'uint32',
  UINT64: 'uint64',
  DATA: 'data', // unconfirmed
  TLV8: 'tlv8'
}

// Known HomeKit unit types
Characteristic.Units = {
  CELSIUS: 'celsius',
  FAHRENHEIT: 'fahrenheit',
  PERCENTAGE: 'percentage',
  ARC_DEGREE: 'arcdegrees',
  LUX: 'lux',
  SECONDS: 'seconds'
}

// Known HomeKit permission types
Characteristic.Perms = {
  READ: 'pr',
  WRITE: 'pw',
  NOTIFY: 'ev'
}

/**
 * Copies the given properties to our props member variable,
 * and returns 'this' for chaining.
 *
 * @param 'props' {
 *   format: <one of Characteristic.Formats>,
 *   unit: <one of Characteristic.Units>,
 *   minValue: <minimum value for numeric characteristics>,
 *   maxValue: <maximum value for numeric characteristics>,
 *   minStep: <smallest allowed increment for numeric characteristics>,
 *   perms: array of [Characteristic.Perms] like [Characteristic.Perms.READ, Characteristic.Perms.WRITE]
 * }
 */
Characteristic.prototype.setProps = function(props) {
  for (var key in (props || {}))
    if (Object.prototype.hasOwnProperty.call(props, key))
      this.props[key] = props[key];
  return this;
}

Characteristic.prototype.getValue = function(callback, context) {

  if (this.listeners('get').length > 0) {

    // allow a listener to handle the fetching of this value, and wait for completion
    this.emit('get', once(function(err, newValue) {

      if (err) {
        // pass the error along to our callback
        if (callback) callback(err);
      }
      else {
        // getting the value was a success; we can pass it along and also update our cached value
        var oldValue = this.value;
        this.value = newValue;
        if (callback) callback(null, newValue);

        // emit a change event if necessary
        if (oldValue !== newValue)
          this.emit('change', { oldValue:oldValue, newValue:newValue, context:context });
      }

    }.bind(this)), context);
  }
  else {

    // no one is listening to the 'get' event, so just return the cached value
    if (callback)
      callback(null, this.value);
  }
}

Characteristic.prototype.setValue = function(newValue, callback, context) {

  if (this.listeners('set').length > 0) {

    // allow a listener to handle the setting of this value, and wait for completion
    this.emit('set', newValue, once(function(err, changedNewValue) {

      if (err) {
        // pass the error along to our callback
        if (callback) callback(err);
      }
      else {
        // did the caller specify change value we should be setting?
        var actualNewValue = (typeof changedNewValue !== 'undefined') ? changedNewValue : newValue;

        // setting the value was a success; so we can cache it now
        var oldValue = this.value;
        this.value = actualNewValue;
        if (callback) callback();

        // emit a change event if necessary
        if (oldValue !== actualNewValue)
          this.emit('change', { oldValue:oldValue, newValue:actualNewValue, context:context });
      }

    }.bind(this)), context);

  }
  else {

    // no one is listening to the 'set' event, so just assign the value blindly
    var oldValue = this.value;
    this.value = newValue;
    if (callback) callback();

    // emit a change event if necessary
    if (oldValue !== newValue)
      this.emit('change', { oldValue:oldValue, newValue:newValue, context:context });
  }

  return this; // for chaining
}

Characteristic.prototype.getDefaultValue = function() {
  switch (this.format) {
    case Characteristic.Formats.BOOL: return false;
    case Characteristic.Formats.STRING: return null;
    case Characteristic.Formats.ARRAY: return []; // who knows!
    case Characteristic.Formats.DICTIONARY: return {}; // who knows!
    case Characteristic.Formats.DATA: return null; // who knows!
    case Characteristic.Formats.TLV8: return null; // who knows!
    default: return this.props.minValue || 0;
  }
}

Characteristic.prototype._assignID = function(identifierCache, accessoryName, serviceUUID, serviceSubtype) {

  // generate our IID based on our UUID
  this.iid = identifierCache.getIID(accessoryName, serviceUUID, serviceSubtype, this.UUID);
}

/**
 * Returns a JSON representation of this Accessory suitable for delivering to HAP clients.
 */
Characteristic.prototype.toHAP = function(opt) {

  var hap = {
    iid: this.iid,
    type: this.UUID,
    perms: this.props.perms,
    format: this.props.format,
    value: this.value,
    description: this.displayName

    // These properties used to be sent but do not seem to be used:
    //
    // maxLen: this.format === Characteristic.Formats.STRING ? 255 : 1,
    // events: false,
    // bonjour: false
  };

  // extra properties
  if (this.props.unit != null) hap.unit = this.props.unit;
  if (this.props.maxValue != null) hap.maxValue = this.props.maxValue;
  if (this.props.minValue != null) hap.minValue = this.props.minValue;
  if (this.props.minStep != null) hap.minStep = this.props.minStep;

  // if we're not readable, omit the "value" property - otherwise iOS will complain about non-compliance
  if (this.props.perms.indexOf(Characteristic.Perms.READ) == -1)
    delete hap.value;

  // delete the "value" property anyway if we were asked to
  if (opt && opt.omitValues)
    delete hap.value;

  return hap;
}
<commit_msg>Always send valid Characteristic value when possible<commit_after>var inherits = require('util').inherits;
var EventEmitter = require('events').EventEmitter;
var once = require('./util/once').once;

'use strict';

module.exports = {
  Characteristic: Characteristic
}

/**
 * Characteristic represents a particular typed variable that can be assigned to a Service. For instance, a
 * "Hue" Characteristic might store a 'float' value of type 'arcdegrees'. You could add the Hue Characteristic
 * to a Service in order to store that value. A particular Characteristic is distinguished from others by its
 * UUID. HomeKit provides a set of known Characteristic UUIDs defined in HomeKitTypes.js along with a
 * corresponding concrete subclass.
 *
 * You can also define custom Characteristics by providing your own UUID. Custom Characteristics can be added
 * to any native or custom Services, but Siri will likely not be able to work with these.
 *
 * Note that you can get the "value" of a Characteristic by accessing the "value" property directly, but this
 * is really a "cached value". If you want to fetch the latest value, which may involve doing some work, then
 * call getValue().
 *
 * @event 'get' => function(callback(err, newValue), context) { }
 *        Emitted when someone calls getValue() on this Characteristic and desires the latest non-cached
 *        value. If there are any listeners to this event, one of them MUST call the callback in order
 *        for the value to ever be delivered. The `context` object is whatever was passed in by the initiator
 *        of this event (for instance whomever called `getValue`).
 *
 * @event 'set' => function(newValue, callback(err, changedNewValue), context) { }
 *        Emitted when someone calls setValue() on this Characteristic with a desired new value. If there
 *        are any listeners to this event, one of them MUST call the callback in order for this.value to
 *        actually be set. Note that you can optionally set a *different* value than the one requested by
 *        passing in the changedNewValue argument to the callback. The `context` object is whatever was
 *        passed in by the initiator of this change (for instance whomever called `setValue`).
 *
 * @event 'change' => function({ oldValue, newValue, context }) { }
 *        Emitted after a change in our value has occurred. The new value will also be immediately accessible
 *        in this.value. The event object contains the new value as well as the context object originally
 *        passed in by the initiator of this change (if known).
 */

function Characteristic(displayName, UUID, props) {
  this.displayName = displayName;
  this.UUID = UUID;
  this.iid = null; // assigned by our containing Service
  this.value = null;
  this.props = props || {
    format: null,
    unit: null,
    minValue: null,
    maxValue: null,
    minStep: null,
    perms: []
  };
}

inherits(Characteristic, EventEmitter);

// Known HomeKit formats
Characteristic.Formats = {
  BOOL: 'bool',
  INT: 'int',
  FLOAT: 'float',
  STRING: 'string',
  ARRAY: 'array', // unconfirmed
  DICTIONARY: 'dictionary', // unconfirmed
  UINT8: 'uint8',
  UINT16: 'uint16',
  UINT32: 'uint32',
  UINT64: 'uint64',
  DATA: 'data', // unconfirmed
  TLV8: 'tlv8'
}

// Known HomeKit unit types
Characteristic.Units = {
  CELSIUS: 'celsius',
  FAHRENHEIT: 'fahrenheit',
  PERCENTAGE: 'percentage',
  ARC_DEGREE: 'arcdegrees',
  LUX: 'lux',
  SECONDS: 'seconds'
}

// Known HomeKit permission types
Characteristic.Perms = {
  READ: 'pr',
  WRITE: 'pw',
  NOTIFY: 'ev'
}

/**
 * Copies the given properties to our props member variable,
 * and returns 'this' for chaining.
 *
 * @param 'props' {
 *   format: <one of Characteristic.Formats>,
 *   unit: <one of Characteristic.Units>,
 *   minValue: <minimum value for numeric characteristics>,
 *   maxValue: <maximum value for numeric characteristics>,
 *   minStep: <smallest allowed increment for numeric characteristics>,
 *   perms: array of [Characteristic.Perms] like [Characteristic.Perms.READ, Characteristic.Perms.WRITE]
 * }
 */
Characteristic.prototype.setProps = function(props) {
  for (var key in (props || {}))
    if (Object.prototype.hasOwnProperty.call(props, key))
      this.props[key] = props[key];
  return this;
}

Characteristic.prototype.getValue = function(callback, context) {

  if (this.listeners('get').length > 0) {

    // allow a listener to handle the fetching of this value, and wait for completion
    this.emit('get', once(function(err, newValue) {

      if (err) {
        // pass the error along to our callback
        if (callback) callback(err);
      }
      else {
        // getting the value was a success; we can pass it along and also update our cached value
        var oldValue = this.value;
        this.value = newValue;
        if (callback) callback(null, newValue);

        // emit a change event if necessary
        if (oldValue !== newValue)
          this.emit('change', { oldValue:oldValue, newValue:newValue, context:context });
      }

    }.bind(this)), context);
  }
  else {

    // no one is listening to the 'get' event, so just return the cached value
    if (callback)
      callback(null, this.value);
  }
}

Characteristic.prototype.setValue = function(newValue, callback, context) {

  if (this.listeners('set').length > 0) {

    // allow a listener to handle the setting of this value, and wait for completion
    this.emit('set', newValue, once(function(err, changedNewValue) {

      if (err) {
        // pass the error along to our callback
        if (callback) callback(err);
      }
      else {
        // did the caller specify change value we should be setting?
        var actualNewValue = (typeof changedNewValue !== 'undefined') ? changedNewValue : newValue;

        // setting the value was a success; so we can cache it now
        var oldValue = this.value;
        this.value = actualNewValue;
        if (callback) callback();

        // emit a change event if necessary
        if (oldValue !== actualNewValue)
          this.emit('change', { oldValue:oldValue, newValue:actualNewValue, context:context });
      }

    }.bind(this)), context);

  }
  else {

    // no one is listening to the 'set' event, so just assign the value blindly
    var oldValue = this.value;
    this.value = newValue;
    if (callback) callback();

    // emit a change event if necessary
    if (oldValue !== newValue)
      this.emit('change', { oldValue:oldValue, newValue:newValue, context:context });
  }

  return this; // for chaining
}

Characteristic.prototype.getDefaultValue = function() {
  switch (this.format) {
    case Characteristic.Formats.BOOL: return false;
    case Characteristic.Formats.STRING: return null;
    case Characteristic.Formats.ARRAY: return []; // who knows!
    case Characteristic.Formats.DICTIONARY: return {}; // who knows!
    case Characteristic.Formats.DATA: return null; // who knows!
    case Characteristic.Formats.TLV8: return null; // who knows!
    default: return this.props.minValue || 0;
  }
}

Characteristic.prototype._assignID = function(identifierCache, accessoryName, serviceUUID, serviceSubtype) {

  // generate our IID based on our UUID
  this.iid = identifierCache.getIID(accessoryName, serviceUUID, serviceSubtype, this.UUID);
}

/**
 * Returns a JSON representation of this Accessory suitable for delivering to HAP clients.
 */
Characteristic.prototype.toHAP = function(opt) {

  // ensure our value fits within our constraints if present
  var value = this.value;
  if (this.props.minValue != null && value < this.props.minValue) value = this.props.minValue;
  if (this.props.maxValue != null && value > this.props.maxValue) value = this.props.maxValue;

  var hap = {
    iid: this.iid,
    type: this.UUID,
    perms: this.props.perms,
    format: this.props.format,
    value: value,
    description: this.displayName

    // These properties used to be sent but do not seem to be used:
    //
    // maxLen: this.format === Characteristic.Formats.STRING ? 255 : 1,
    // events: false,
    // bonjour: false
  };

  // extra properties
  if (this.props.unit != null) hap.unit = this.props.unit;
  if (this.props.maxValue != null) hap.maxValue = this.props.maxValue;
  if (this.props.minValue != null) hap.minValue = this.props.minValue;
  if (this.props.minStep != null) hap.minStep = this.props.minStep;

  // if we're not readable, omit the "value" property - otherwise iOS will complain about non-compliance
  if (this.props.perms.indexOf(Characteristic.Perms.READ) == -1)
    delete hap.value;

  // delete the "value" property anyway if we were asked to
  if (opt && opt.omitValues)
    delete hap.value;

  return hap;
}
<|endoftext|>


<commit_before>'use strict';

var meta = module.parent.require('./meta');
var user = module.parent.require('./user');

var winston = require('winston');

var plugin = {};

plugin.init = function (params, callback)	{
	winston.add(winston.transports.File, {
		level: 'debug',
		json: false,
		filename: 'my.log'
	});

	winston.info('This is info');
	winston.error('This is error');
}<commit_msg>Modified library.js<commit_after>// 'use strict';

// var plugin = {};

// plugin.init = function (params, callback)	{
// 	winston.add(winston.transports.File, {
// 		level: 'debug',
// 		json: false,
// 		filename: 'my.log'
// 	});

// 	winston.info('This is info');
// 	winston.error('This is error');
// }

(function (module)	{
	'use strict';

	var meta = module.parent.require('./meta');
	var user = module.parent.require('./user');

	var winston = require('winston');

	var init = function (params, callback)	{
		winston.add(winston.transports.File, {
			level: 'debug',
			json: false,
			filename: 'my.log'
		});

		winston.info('This is info');
		winston.error('This is error');
	}

}(module));<|endoftext|>


<commit_before>require('../support');

var expect = require('chai').expect;
var loopback = require('loopback');
var request = require('supertest');

var loopbackJsonSchema = require('../../index');
var ItemSchema = require('../../lib/domain/item-schema');
var jsonSchemaMiddleware = require('../../lib/http/json-schema.middleware');

var app = loopback();
app.set('restApiRoot', '/api');
app.use(app.get('restApiRoot'), jsonSchemaMiddleware());
loopbackJsonSchema.init(app);
app.installMiddleware();

describe('POST /:collection', function() {
    var itemResponse, jsonSchemaId, schemeAndAuthority;

    before(function (done) {
        ItemSchema.create({
            modelName: 'person',
            collectionName: 'people',
            title: 'Person',
            collectionTitle: 'People',
            type: 'object',
            properties: {}
        }, function(err, jsonSchema) {
            if (err) { throw err };
            jsonSchemaId = jsonSchema.id;
            done();
        });
    });

    before(function(done) {
        request(app)
            .post('/api/people')
            .set('Content-Type', 'application/json')
            .send('{"name": "Alice"}')
            .expect(200)
            .end(function (err, res) {
                if (err) { throw err };
                schemeAndAuthority = 'http://' + res.req._headers.host;
                itemResponse = res;
                done();
            });
    });

    it('should correlate the item with its schema', function() {
        var itemSchemaUrl = schemeAndAuthority + '/api/item-schemas/' + jsonSchemaId;
        expect(itemResponse.headers['link']).to.eq('<' + itemSchemaUrl + '>; rel=describedby');
        expect(itemResponse.headers['content-type']).to.eq('application/json; charset=utf-8; profile=' + itemSchemaUrl);
    });
});<commit_msg>[#9] Acceptance test POSTing item with unsupported Content-Type.<commit_after>require('../support');

var expect = require('chai').expect;
var loopback = require('loopback');
var request = require('supertest');

var loopbackJsonSchema = require('../../index');
var ItemSchema = require('../../lib/domain/item-schema');
var jsonSchemaMiddleware = require('../../lib/http/json-schema.middleware');

var app = loopback();
app.set('restApiRoot', '/api');
app.use(app.get('restApiRoot'), jsonSchemaMiddleware());
loopbackJsonSchema.init(app);
app.installMiddleware();

describe('POST /:collection', function() {
    var itemResponse, jsonSchemaId, schemeAndAuthority;

    describe('successfully', function() {
        before(function (done) {
            ItemSchema.create({
                modelName: 'person',
                collectionName: 'people',
                title: 'Person',
                collectionTitle: 'People',
                type: 'object',
                properties: {}
            }, function(err, jsonSchema) {
                if (err) { throw err };
                jsonSchemaId = jsonSchema.id;
                done();
            });
        });

        before(function(done) {
            request(app)
                .post('/api/people')
                .set('Content-Type', 'application/json')
                .send('{"name": "Alice"}')
                .end(function (err, res) {
                    if (err) { throw err };
                    schemeAndAuthority = 'http://' + res.req._headers.host;
                    itemResponse = res;
                    done();
                });
        });

        it('should return 200', function() {
            expect(itemResponse.status).to.eq(200);
        });

        it('should correlate the item with its schema', function() {
            var itemSchemaUrl = schemeAndAuthority + '/api/item-schemas/' + jsonSchemaId;
            expect(itemResponse.headers['link']).to.eq('<' + itemSchemaUrl + '>; rel=describedby');
            expect(itemResponse.headers['content-type']).to.eq('application/json; charset=utf-8; profile=' + itemSchemaUrl);
        });
    });

    describe('with unsupported Content-Type', function() {
        before(function(done) {
            request(app)
                .post('/api/people')
                .set('Content-Type', 'text/plain')
                .send('{"name": "Alice"}')
                .end(function (err, res) {
                    if (err) { throw err };
                    itemResponse = res;
                    done();
                });
        });

        it('should return 400', function() {
            expect(itemResponse.status).to.eq(400);
        });

        it('should return error message', function() {
            expect(itemResponse.text).to.eq('Unsupported Content-Type: <text/plain>.')
        });
    });
});<|endoftext|>