<commit_before>$(function(){
    var report_template = _.template($('#report-template').html());

    window.removeColumn = function() {
        geoid = $(this).attr('data-val');
        if (document.location.pathname.indexOf('/' + geoid) > 0) {
            document.location.pathname = document.location.pathname.replace(geoid + ',', '');
        } else {
            document.location.pathname = document.location.pathname.replace(',' + geoid, '');
        }
    }

    window.twistRow = function() {
        var show_child = $(this).is('.closed');
        twistRowHelper($(this), show_child);
        $(this).toggleClass('closed')
        $(this).toggleClass('open');
    }

    window.twistRowHelper = function(parent_row, show_me) {
        $.each($('tr[parent=' + $(parent_row).attr('id') + ']'), function(index, value){
            if(show_me){
                $(value).show();
            } else {
                $(value).hide();
            }

            var show_child = false;
            if($(value).is(':visible') && $(value).is('.has-children.open')) {
                show_child = true;
            }

            twistRowHelper(value, show_child);
        });
    }

    window.addRelatedState = function() {
        this_geoid = $(this).attr('data-val');
        state_geoid = this_geoid.slice(0,2);

        window.location.pathname = window.location.pathname.replace('.html', "," + state_geoid + '.html');
    }

    window.addRelatedCounty = function() {
        this_geoid = $(this).attr('data-val');
        county_geoid = this_geoid.slice(0,5);

        window.location.pathname = window.location.pathname.replace('.html', "," + county_geoid + '.html');
    }

    window.apiRequest = function(path, callback, handler) {
        $.ajax(API_URL + path, {
            dataType: "jsonp",
            jsonpCallback: callback,
            success: handler
        });
    }

    window.parseGeoids = function() {
        // Get url without hashbang
        var target = _.last(window.location.pathname.split("/"));

        // Ditch the .html
        target = _.first(target.split("."));

        // Split the list
        geoids = target.split(",");

        return geoids;
    }

    window.downloadData = function(format) {
        var url = "/data/" + window.geoids.join(",") + "." + format;

        if (window.location.hash != "") {
             url += "?tables=" + window.location.hash.substring(1);
        }

        window.location = url;
    }

    window.loadLabels = function() {
        $("#ajax-loader").show();

        apiRequest("/" + window.DATASET + "_labels.jsonp", "labels_" + window.DATASET, function(labels_data) {
            window.labels_data = labels_data;
            window.geoids = parseGeoids();

            window.tables = _.keys(labels_data["tables"])
            window.tables = _.sortBy(window.tables, function(table) {
                parts = table.match(/([A-Z]+)(\d+)([A-Z]+)?/);

                key = parts[1];
                num = parts[2];

                while (num.length < 3) {
                    num = '0' + num;
                }

                key += num;

                if (!_.isUndefined(parts[3])) {
                    key += parts[3];
                }

                return key;
            });

            _.each(window.tables, function(table) {
                var elem = $('

');
                $('#reports').append(elem);
            });

            geographies = new Array();

            _.each(geoids, function(geoid) {
                var state = geoid.substring(0, 2);

                apiRequest("/" + state + "/" + geoid + ".jsonp", "geoid_" + geoid, function(geography_data) {
                    geographies.push(geography_data);

                    // If all geographies have been loaded, make reports
                    if (geographies.length == geoids.length) {
                        // Sort geographies by url order since the return order isn't deterministic
                        window.geographies = _.sortBy(geographies, function(geography) {
                            return _.indexOf(geoids, geography["geoid"]);
                        });

                        // Browser handles displaying reports
                        $("#ajax-loader").hide();
                        startBrowser();
                    }
                });
            });
        });
    }

    window.makeReport = function(table, labelset, geoids, geographies) {
        var report = {
            'key': labelset["key"],
            'name': labelset['name'],
            'table': labelset["key"] + ". " + labelset['name'],
            'universe': labelset['universe'],
            'columns': [],
            'rows': []
        };

        var labels = _.sortBy(labelset["labels"], function(label) {
            return label["key"];
        });

        _.each(labels, function(label) {
            row = {
                "key": label["key"],
                "label": label,
                "data": []
            }

            _.each(geographies, function(geography) {
                var d = new Object();

                _.each(["2000", "2010", "delta", "pct_change"], function(year) {
                    try {
                        d[year] = geography["data"][year][table][label["key"]];
                    } catch(err) {
                    }
                });

                row["data"].push(d);
            });

            report["rows"].push(row);
        });

        _.each(geographies, function(geography) {
            var column_meta = {};
            column_name = geography["metadata"]["NAME"];

            if ($.inArray(geography["sumlev"], [SUMLEV_COUNTY, SUMLEV_COUNTY_SUBDIVISION, SUMLEV_PLACE, SUMLEV_TRACT]) >= 0) {
                state_fips = geography["metadata"]["STATE"];
                state_name = _.detect(window.query.mappings.states, function(state) {
                    return state[1] == state_fips;
                })[0];
                column_name += ", " + state_name;
            }

            column_meta["name"] = column_name;
            column_meta["geoid"] = geography["geoid"];
            column_meta["sumlev"] = geography["sumlev"];

            report["columns"].push(column_meta);
        });

        report["geoids"] = geoids;
        report["show_remove_button"] = (geoids.length > 1);

        return report;
    }

    window.floatFormat = function(n, decimals) {
        if (_.isString(n)) {
            try {
                n = parseFloat(n);
            } catch(err) {
                return "";
            }
        }

        if (_.isUndefined(n) || _.isNaN(n)) {
            return "";
        }

        if (n % 1 == 0) {
            return Math.round(n);
        }

        if (_.isUndefined(decimals)) {
            decimals = 1;
        }

        pow = Math.pow(10, decimals);

        return Math.round(n * pow) / pow;
    }

    window.toPercent = function(n) {
        if (_.isString(n)) {
            try {
                n = parseFloat(n);
            } catch(err) {
                return "";
            }
        }

        if (_.isUndefined(n) || _.isNaN(n)) {
            return "";
        }

        return n * 100;
    }

    window.intComma = function(n) {
        // Algorithm from Django template tags
        // http://code.djangoproject.com/svn/django/trunk/django/contrib/humanize/templatetags/humanize.py
        if (_.isUndefined(n) || _.isNaN(n)) {
            return "";
        }

        orig = n
        n = n.toString().replace(/^(-?\d+)(\d{3}\.?\d*)/g, "$1,$2");

        if (n == orig) {
            return n;
        } else {
            return intComma(n);
        }
    }

    window.renderReport = function(report) {
        var html = report_template(report);

        $('#report-wrapper-' + report["key"]).append(html);
    }

    // Add event hooks
    $('.report .button.remove-column').live("click", removeColumn);
    $('.report tr.row').live("click", window.twistRow);
    $('.report .button.add-related-state').live("click", window.addRelatedState);
    $('.report .button.add-related-county').live("click", window.addRelatedCounty);
    $('nav .csv').live("click", function () { window.downloadData("csv"); });
    $('nav .json').live("click", function () { window.downloadData("json"); });
    $('nav .kml').live("click", function () { window.downloadData("kml"); });

    // Table mouseover row highlighting.
    $('.report td').live('mouseover mouseleave', function(e) {
        if (e.type == 'mouseover') {
            status = ''
            $(this).addClass("selected");
            $(this).parent().addClass("highlight");
            status = $(this).parent().find('.label').text();
            if ($(this).index() > 0) {
                $("colgroup", $(this).parents("table")).eq($(this).index()).addClass("highlight"); //column
                status += ', ' + $($(this).parents("table").find('.locationdef')[Math.ceil($(this).index()/4) - 1]).clone().find('*').remove().end().text().trim();
                status += ', ' + $($(this).parents("table").find('.subhead')[$(this).index() - 1]).text().trim();
            }
            $('#status').show().text(status);
        } else {
            $(this).removeClass("selected");
            $(this).parent().removeClass('highlight');
            if ($(this).index() > 0)
                $("colgroup", $(this).parents("table")).eq($(this).index()).removeClass("highlight");
            $('#status').hide();
        }
    });

    // Kick-off
    loadLabels();
});
<commit_msg>Fixed geography removal bug. Closes #124.<commit_after>$(function(){
    var report_template = _.template($('#report-template').html());

    window.removeColumn = function() {
        geoid = $(this).attr('data-val');

        // First item in list
        if (document.location.pathname.indexOf('/' + geoid + ',') > 0) {
            document.location.pathname = document.location.pathname.replace('/' + geoid + ',', '/');
        // Any other item in list
        } else {
            document.location.pathname = document.location.pathname.replace(',' + geoid, '');
        }
    }

    window.twistRow = function() {
        var show_child = $(this).is('.closed');
        twistRowHelper($(this), show_child);
        $(this).toggleClass('closed')
        $(this).toggleClass('open');
    }

    window.twistRowHelper = function(parent_row, show_me) {
        $.each($('tr[parent=' + $(parent_row).attr('id') + ']'), function(index, value){
            if(show_me){
                $(value).show();
            } else {
                $(value).hide();
            }

            var show_child = false;
            if($(value).is(':visible') && $(value).is('.has-children.open')) {
                show_child = true;
            }

            twistRowHelper(value, show_child);
        });
    }

    window.addRelatedState = function() {
        this_geoid = $(this).attr('data-val');
        state_geoid = this_geoid.slice(0,2);

        window.location.pathname = window.location.pathname.replace('.html', "," + state_geoid + '.html');
    }

    window.addRelatedCounty = function() {
        this_geoid = $(this).attr('data-val');
        county_geoid = this_geoid.slice(0,5);

        window.location.pathname = window.location.pathname.replace('.html', "," + county_geoid + '.html');
    }

    window.apiRequest = function(path, callback, handler) {
        $.ajax(API_URL + path, {
            dataType: "jsonp",
            jsonpCallback: callback,
            success: handler
        });
    }

    window.parseGeoids = function() {
        // Get url without hashbang
        var target = _.last(window.location.pathname.split("/"));

        // Ditch the .html
        target = _.first(target.split("."));

        // Split the list
        geoids = target.split(",");

        return geoids;
    }

    window.downloadData = function(format) {
        var url = "/data/" + window.geoids.join(",") + "." + format;

        if (window.location.hash != "") {
             url += "?tables=" + window.location.hash.substring(1);
        }

        window.location = url;
    }

    window.loadLabels = function() {
        $("#ajax-loader").show();

        apiRequest("/" + window.DATASET + "_labels.jsonp", "labels_" + window.DATASET, function(labels_data) {
            window.labels_data = labels_data;
            window.geoids = parseGeoids();

            window.tables = _.keys(labels_data["tables"])
            window.tables = _.sortBy(window.tables, function(table) {
                parts = table.match(/([A-Z]+)(\d+)([A-Z]+)?/);

                key = parts[1];
                num = parts[2];

                while (num.length < 3) {
                    num = '0' + num;
                }

                key += num;

                if (!_.isUndefined(parts[3])) {
                    key += parts[3];
                }

                return key;
            });

            _.each(window.tables, function(table) {
                var elem = $('

');
                $('#reports').append(elem);
            });

            geographies = new Array();

            _.each(geoids, function(geoid) {
                var state = geoid.substring(0, 2);

                apiRequest("/" + state + "/" + geoid + ".jsonp", "geoid_" + geoid, function(geography_data) {
                    geographies.push(geography_data);

                    // If all geographies have been loaded, make reports
                    if (geographies.length == geoids.length) {
                        // Sort geographies by url order since the return order isn't deterministic
                        window.geographies = _.sortBy(geographies, function(geography) {
                            return _.indexOf(geoids, geography["geoid"]);
                        });

                        // Browser handles displaying reports
                        $("#ajax-loader").hide();
                        startBrowser();
                    }
                });
            });
        });
    }

    window.makeReport = function(table, labelset, geoids, geographies) {
        var report = {
            'key': labelset["key"],
            'name': labelset['name'],
            'table': labelset["key"] + ". " + labelset['name'],
            'universe': labelset['universe'],
            'columns': [],
            'rows': []
        };

        var labels = _.sortBy(labelset["labels"], function(label) {
            return label["key"];
        });

        _.each(labels, function(label) {
            row = {
                "key": label["key"],
                "label": label,
                "data": []
            }

            _.each(geographies, function(geography) {
                var d = new Object();

                _.each(["2000", "2010", "delta", "pct_change"], function(year) {
                    try {
                        d[year] = geography["data"][year][table][label["key"]];
                    } catch(err) {
                    }
                });

                row["data"].push(d);
            });

            report["rows"].push(row);
        });

        _.each(geographies, function(geography) {
            var column_meta = {};
            column_name = geography["metadata"]["NAME"];

            if ($.inArray(geography["sumlev"], [SUMLEV_COUNTY, SUMLEV_COUNTY_SUBDIVISION, SUMLEV_PLACE, SUMLEV_TRACT]) >= 0) {
                state_fips = geography["metadata"]["STATE"];
                state_name = _.detect(window.query.mappings.states, function(state) {
                    return state[1] == state_fips;
                })[0];
                column_name += ", " + state_name;
            }

            column_meta["name"] = column_name;
            column_meta["geoid"] = geography["geoid"];
            column_meta["sumlev"] = geography["sumlev"];

            report["columns"].push(column_meta);
        });

        report["geoids"] = geoids;
        report["show_remove_button"] = (geoids.length > 1);

        return report;
    }

    window.floatFormat = function(n, decimals) {
        if (_.isString(n)) {
            try {
                n = parseFloat(n);
            } catch(err) {
                return "";
            }
        }

        if (_.isUndefined(n) || _.isNaN(n)) {
            return "";
        }

        if (n % 1 == 0) {
            return Math.round(n);
        }

        if (_.isUndefined(decimals)) {
            decimals = 1;
        }

        pow = Math.pow(10, decimals);

        return Math.round(n * pow) / pow;
    }

    window.toPercent = function(n) {
        if (_.isString(n)) {
            try {
                n = parseFloat(n);
            } catch(err) {
                return "";
            }
        }

        if (_.isUndefined(n) || _.isNaN(n)) {
            return "";
        }

        return n * 100;
    }

    window.intComma = function(n) {
        // Algorithm from Django template tags
        // http://code.djangoproject.com/svn/django/trunk/django/contrib/humanize/templatetags/humanize.py
        if (_.isUndefined(n) || _.isNaN(n)) {
            return "";
        }

        orig = n
        n = n.toString().replace(/^(-?\d+)(\d{3}\.?\d*)/g, "$1,$2");

        if (n == orig) {
            return n;
        } else {
            return intComma(n);
        }
    }

    window.renderReport = function(report) {
        var html = report_template(report);

        $('#report-wrapper-' + report["key"]).append(html);
    }

    // Add event hooks
    $('.report .button.remove-column').live("click", removeColumn);
    $('.report tr.row').live("click", window.twistRow);
    $('.report .button.add-related-state').live("click", window.addRelatedState);
    $('.report .button.add-related-county').live("click", window.addRelatedCounty);
    $('nav .csv').live("click", function () { window.downloadData("csv"); });
    $('nav .json').live("click", function () { window.downloadData("json"); });
    $('nav .kml').live("click", function () { window.downloadData("kml"); });

    // Table mouseover row highlighting.
    $('.report td').live('mouseover mouseleave', function(e) {
        if (e.type == 'mouseover') {
            status = ''
            $(this).addClass("selected");
            $(this).parent().addClass("highlight");
            status = $(this).parent().find('.label').text();
            if ($(this).index() > 0) {
                $("colgroup", $(this).parents("table")).eq($(this).index()).addClass("highlight"); //column
                status += ', ' + $($(this).parents("table").find('.locationdef')[Math.ceil($(this).index()/4) - 1]).clone().find('*').remove().end().text().trim();
                status += ', ' + $($(this).parents("table").find('.subhead')[$(this).index() - 1]).text().trim();
            }
            $('#status').show().text(status);
        } else {
            $(this).removeClass("selected");
            $(this).parent().removeClass('highlight');
            if ($(this).index() > 0)
                $("colgroup", $(this).parents("table")).eq($(this).index()).removeClass("highlight");
            $('#status').hide();
        }
    });

    // Kick-off
    loadLabels();
});
<|endoftext|>


<commit_before>// Note that these functions are only valid for square boards.

const simple = ['AB', 'AW', 'AE', 'B', 'CR', 'DD', 'MA', 'SL', 'SQ', 'TR', 'VW', 'W'];
const pointWithText = ['LB'];
const arrowish = ['AR', 'LN'];

const alpha = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
const alphaRev = 'ZYXWVUTSRQPONMLKJIHGFEDCBAzyxwvutsrqponmlkjihgfedcba';

exports.rotatePointClockwise = function(point, size) {

	// returns null on failure ; point is something like "aa"

	if (typeof point !== 'string') return null;
	if (typeof size !== 'number') return null;
	if (size < 1 || size > 52) return null;
	if (point.length !== 2) return null;

	let index0 = alpha.indexOf(point[0]);
	let index1 = alpha.indexOf(point[1]);

	if (index0 === -1 || index1 === -1) return null;

	let rev = alphaRev.slice(52 - size);
	return rev[index1] + point[0];
};

exports.rotateRectClockwise = function(rect, size) {

	// returns null on failure ; rect is something like "aa:cc"

	if (typeof size !== 'number') return null;
	if (typeof rect !== 'string') return null;
	if (size < 1 || size > 52) return null;
	if (rect.length !== 5) return null;
	if (rect[2] !== ':') return null;

	let first = rect.slice(0, 2);
	let second = rect.slice(3, 5);

	first = exports.rotatePointClockwise(first, size);
	second = exports.rotatePointClockwise(second, size);

	if (first === null || second === null) {
		return null;
	}

	return second[0] + first[1] + ":" + first[0] + second[1];
};

exports.rotateArrowClockwise = function(arrow, size) {

	// returns null on failure ; arrow is something like "aa:cc"

	if (typeof size !== 'number') return null;
	if (typeof arrow !== 'string') return null;
	if (size < 1 || size > 52) return null;
	if (arrow.length !== 5) return null;
	if (arrow[2] !== ':') return null;

	let first = arrow.slice(0, 2);
	let second = arrow.slice(3, 5);

	first = exports.rotatePointClockwise(first, size);
	second = exports.rotatePointClockwise(second, size);

	if (first === null || second === null) {
		return null;
	}

	return first + ":" + second;
};

exports.rotateNodeClockwise = function(node, size) {

	// Given a node and a board size, rotates all relevant properties in the node.
	// Does NOT do anything about the board though.

	if (typeof size !== 'number') {
		return;
	}

	// 'simple' cases are either a single point (e.g. "aa") or a rect (e.g. "aa:cc")

	for (let k = 0; k < simple.length; k++) {

		let key = simple[k];

		if (node[key] !== undefined) {

			let values = node[key];

			for (let v = 0; v < values.length; v++) {

				let value = values[v];

				if (value.length === 2) {

					let result = exports.rotatePointClockwise(value, size);
					if (result) {
						values[v] = result;
					}

				} else if (value.length === 5 && value[2] === ":") {

					let result = exports.rotateRectClockwise(value, size);
					if (result) {
						values[v] = result;
					}
				}
			}
		}
	}

	// 'pointWithText' means something like "aa:hi there"

	for (let k = 0; k < pointWithText.length; k++) {

		let key = pointWithText[k];

		if (node[key] !== undefined) {

			let values = node[key];

			for (let v = 0; v < values.length; v++) {

				let value = values[v];

				if (value[2] === ':') {

					let point = value.slice(0, 2);

					point = exports.rotatePointClockwise(point, size);
					if (point) {
						values[v] = point + value.slice(2);
					}

				}
			}
		}
	}

	// 'arrowish' things are formatted like rects, but with no requirement of topleft:bottomright

	for (let k = 0; k < arrowish.length; k++) {

		let key = arrowish[k];

		if (node[key] !== undefined) {

			let values = node[key];

			for (let v = 0; v < values.length; v++) {

				let value = values[v];

				if (value[2] === ':') {

					let result = exports.rotateArrowClockwise(value, size);
					if (result) {
						values[v] = result;
					}

				}
			}
		}
	}
};

<commit_msg>add TB, TW<commit_after>// Note that these functions are only valid for square boards.

const simple = ['AB', 'AW', 'AE', 'B', 'CR', 'DD', 'MA', 'SL', 'SQ', 'TB', 'TR', 'TW', 'VW', 'W'];
const pointWithText = ['LB'];
const arrowish = ['AR', 'LN'];

const alpha = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
const alphaRev = 'ZYXWVUTSRQPONMLKJIHGFEDCBAzyxwvutsrqponmlkjihgfedcba';

exports.rotatePointClockwise = function(point, size) {

	// returns null on failure ; point is something like "aa"

	if (typeof point !== 'string') return null;
	if (typeof size !== 'number') return null;
	if (size < 1 || size > 52) return null;
	if (point.length !== 2) return null;

	let index0 = alpha.indexOf(point[0]);
	let index1 = alpha.indexOf(point[1]);

	if (index0 === -1 || index1 === -1) return null;

	let rev = alphaRev.slice(52 - size);
	return rev[index1] + point[0];
};

exports.rotateRectClockwise = function(rect, size) {

	// returns null on failure ; rect is something like "aa:cc"

	if (typeof size !== 'number') return null;
	if (typeof rect !== 'string') return null;
	if (size < 1 || size > 52) return null;
	if (rect.length !== 5) return null;
	if (rect[2] !== ':') return null;

	let first = rect.slice(0, 2);
	let second = rect.slice(3, 5);

	first = exports.rotatePointClockwise(first, size);
	second = exports.rotatePointClockwise(second, size);

	if (first === null || second === null) {
		return null;
	}

	return second[0] + first[1] + ":" + first[0] + second[1];
};

exports.rotateArrowClockwise = function(arrow, size) {

	// returns null on failure ; arrow is something like "aa:cc"

	if (typeof size !== 'number') return null;
	if (typeof arrow !== 'string') return null;
	if (size < 1 || size > 52) return null;
	if (arrow.length !== 5) return null;
	if (arrow[2] !== ':') return null;

	let first = arrow.slice(0, 2);
	let second = arrow.slice(3, 5);

	first = exports.rotatePointClockwise(first, size);
	second = exports.rotatePointClockwise(second, size);

	if (first === null || second === null) {
		return null;
	}

	return first + ":" + second;
};

exports.rotateNodeClockwise = function(node, size) {

	// Given a node and a board size, rotates all relevant properties in the node.
	// Does NOT do anything about the board though.

	if (typeof size !== 'number') {
		return;
	}

	// 'simple' cases are either a single point (e.g. "aa") or a rect (e.g. "aa:cc")

	for (let k = 0; k < simple.length; k++) {

		let key = simple[k];

		if (node[key] !== undefined) {

			let values = node[key];

			for (let v = 0; v < values.length; v++) {

				let value = values[v];

				if (value.length === 2) {

					let result = exports.rotatePointClockwise(value, size);
					if (result) {
						values[v] = result;
					}

				} else if (value.length === 5 && value[2] === ":") {

					let result = exports.rotateRectClockwise(value, size);
					if (result) {
						values[v] = result;
					}
				}
			}
		}
	}

	// 'pointWithText' means something like "aa:hi there"

	for (let k = 0; k < pointWithText.length; k++) {

		let key = pointWithText[k];

		if (node[key] !== undefined) {

			let values = node[key];

			for (let v = 0; v < values.length; v++) {

				let value = values[v];

				if (value[2] === ':') {

					let point = value.slice(0, 2);

					point = exports.rotatePointClockwise(point, size);
					if (point) {
						values[v] = point + value.slice(2);
					}

				}
			}
		}
	}

	// 'arrowish' things are formatted like rects, but with no requirement of topleft:bottomright

	for (let k = 0; k < arrowish.length; k++) {

		let key = arrowish[k];

		if (node[key] !== undefined) {

			let values = node[key];

			for (let v = 0; v < values.length; v++) {

				let value = values[v];

				if (value[2] === ':') {

					let result = exports.rotateArrowClockwise(value, size);
					if (result) {
						values[v] = result;
					}

				}
			}
		}
	}
};

<|endoftext|>


<commit_before>var global_context = ActiveSupport.getGlobalContext();
var ie = !!(global_context.attachEvent && !global_context.opera);

/**
 * ActiveSupport.Element
 * ActiveSupport.Element is a simple DOM manipulation library that does not modify the built in Element object. All ActiveSupport.Element methods take an Element object (and not a string) as their first argument. ActiveSupport.Element is available inside ActiveView classes as the second argument:
 *
 *     var MyClass = ActiveView.create(function(builder,dom){
 *         var link = builder.a({href:'#'},'Text');
 *         dom.addClassName(link,'active');
 *         dom.getWidth(link);
 *         return builder.div(link);
 *     });
 *
 * The implementation of event obeserver's differs from Prototype's since it does not modify the Element object. Your observer receives three arguments, the Event object, a function that will stop the event when called, and a function that will unregister the observer.
 *
 *     var dom = ActiveSupport.Element;
 *     dom.observe(link,'click',function(event,stop,unregister){
 *         //do stuff
 *         stop();
 *     });
 *
 * ActiveSupport.Element also supports the a similar event to Prototype's dom:ready:
 *
 *     dom.observe(document,'ready',function(){
 *         //...
 *     });
 **/
ActiveSupport.Element = {
    ieAttributeTranslations: {
        'class': 'className',
        'checked': 'defaultChecked',
        'usemap': 'useMap',
        'for': 'htmlFor',
        'readonly': 'readOnly',
        'colspan': 'colSpan',
        'bgcolor': 'bgColor',
        'cellspacing': 'cellSpacing',
        'cellpadding': 'cellPadding'
    },
    ieAttributeTranslationSniffingCache: {},
    /**
     * ActiveSupport.Element.keyCodes -> Object
     * Contains the following:
     *
     * - KEY_BACKSPACE
     * - KEY_TAB
     * - KEY_RETURN
     * - KEY_ESC
     * - KEY_LEFT
     * - KEY_UP
     * - KEY_RIGHT
     * - KEY_DOWN
     * - KEY_DELETE
     * - KEY_HOME
     * - KEY_END
     * - KEY_PAGEUP
     * - KEY_PAGEDOWN
     * - KEY_INSERT
     **/
    keyCodes: {
        KEY_BACKSPACE: 8,
        KEY_TAB:       9,
        KEY_RETURN:   13,
        KEY_ESC:      27,
        KEY_LEFT:     37,
        KEY_UP:       38,
        KEY_RIGHT:    39,
        KEY_DOWN:     40,
        KEY_DELETE:   46,
        KEY_HOME:     36,
        KEY_END:      35,
        KEY_PAGEUP:   33,
        KEY_PAGEDOWN: 34,
        KEY_INSERT:   45
    },
    cache: {},
    /**
     * ActiveSupport.Element.create(tag_name,attributes_hash) -> Element
     **/
    create: function create(tag_name,attributes)
    {
        attributes = attributes || {};
        tag_name = tag_name.toLowerCase();
        var element;
        if(ie && (attributes.name || (tag_name == 'input' && attributes.type)))
        {
            //ie needs these attributes to be written in the string passed to createElement
            tag = '<' + tag_name;
            if(attributes.name)
            {
                tag += ' name="' + attributes.name + '"';
            }
            if(tag_name == 'input' && attributes.type)
            {
                tag += ' type="' + attributes.type + '"';
            }
            tag += '>';
            delete attributes.name;
            delete attributes.type;
            element = ActiveSupport.Element.extend(global_context.document.createElement(tag));
        }
        else
        {
            if(!ActiveSupport.Element.cache[tag_name])
            {
                ActiveSupport.Element.cache[tag_name] = ActiveSupport.Element.extend(global_context.document.createElement(tag_name));
            }
            element = ActiveSupport.Element.cache[tag_name].cloneNode(false);
        }
        ActiveSupport.Element.writeAttribute(element,attributes);
        return element;
    },
    extend: function extend(element)
    {
        return element;
    },
    /**
     * ActiveSupport.Element.clear(element) -> Element
     **/
    clear: function clear(element)
    {
        while(element.firstChild)
        {
            element.removeChild(element.firstChild);
        }
        return element;
    },
    /**
     * ActiveSupport.Element.hide(element) -> Element
     **/
    hide: function hide(element)
    {
        element.style.display = 'none';
        return element;
    },
    /**
     * ActiveSupport.Element.show(element) -> Element
     **/
    show: function show(element)
    {
        element.style.display = '';
        return element;
    },
    /**
     * ActiveSupport.Element.remove(element) -> Element
     **/
    remove: function remove(element)
    {
        element.parentNode.removeChild(element);
        return element;
    },
    /**
     * ActiveSupport.Element.insert(element,content[,position]) -> Element
     * - element (Element)
     * - content (String | Number | Element)
     * - position (String): "top", "bottom", "before", "after"
     * Note that this element does not identically mimic Prototype's Element.prototype.insert
     **/
    insert: function insert(element,content,position)
    {
        if(content && typeof(content.getElement) == 'function')
        {
            content = content.getElement();
        }
        if(ActiveSupport.Object.isArray(content))
        {
            for(var i = 0; i < content.length; ++i)
            {
                ActiveSupport.Element.insert(element,content[i],position);
            }
        }
        else
        {
            if(!content || !content.nodeType || content.nodeType !== 1)
            {
                content = global_context.document.createTextNode(String(content));
            }
            if(!position)
            {
                position = 'bottom';
            }
            switch(position)
            {
                case 'top': element.insertBefore(content,element.firstChild); break;
                case 'bottom': element.appendChild(content); break;
                case 'before': element.parentNode.insertBefore(content,element); break;
                case 'after': element.parentNode.insertBefore(content,element.nextSibling); break;
            }
        }
        return element;
    },
    /**
     * ActiveSupport.Element.update(element,content[,position]) -> Element
     * Works exactly like update, but calls ActiveSupport.Element.clear() on the element first.
     **/
    update: function update(element,content,position)
    {
        ActiveSupport.Element.clear(element);
        ActiveSupport.Element.insert(element,content,position);
        return element;
    },
    /**
     * ActiveSupport.Element.writeAttribute(element,name,value) -> Element
     * ActiveSupport.Element.writeAttribute(element,attributes_hash) -> Element
     **/
    writeAttribute: function writeAttribute(element,name,value)
    {
        var transitions = {
            className: 'class',
            htmlFor:   'for'
        };
        var attributes = {};
        if(typeof name === 'object')
        {
            attributes = name;
        }
        else
        {
            attributes[name] = typeof(value) === 'undefined' ? true : value;
        }
        for(var attribute_name in attributes)
        {
            name = transitions[attribute_name] || attribute_name;
            // check if things need to be remapped for IE (Some stuff has been fixed when IE > 7)
            if(ie && ActiveSupport.Element.ieAttributeTranslations[name])
            {
                if(name in ActiveSupport.Element.ieAttributeTranslationSniffingCache)
                {
                    if(ActiveSupport.Element.ieAttributeTranslationSniffingCache[name])
                    {
                        name = ActiveSupport.Element.ieAttributeTranslationSniffingCache[name];
                    }
                }
                else
                {
                    var test_element = ActiveSupport.getGlobalContext().document.createElement("div");
                    test_element.setAttribute(name,"test");
                    if(test_element[ActiveSupport.Element.ieAttributeTranslations[name]] !== "test") {
                        test_element.setAttribute(ActiveSupport.Element.ieAttributeTranslations[name],"test");
                        ActiveSupport.Element.ieAttributeTranslationSniffingCache[name] = test_element[ActiveSupport.Element.ieAttributeTranslations[name]] === "test";
                        if(ActiveSupport.Element.ieAttributeTranslationSniffingCache[name])
                        {
                            name = ActiveSupport.Element.ieAttributeTranslations[name];
                        }
                    }
                }
            }
            value = attributes[attribute_name];
            if(value === false || value === null)
            {
                element.removeAttribute(name);
            }
            else if(value === true)
            {
                element.setAttribute(name,name);
            }
            else
            {
                if(name == 'style')
                {
                    element.style.cssText = value;
                }
                else
                {
                    element.setAttribute(name,value);
                }
            }
        }
        return element;
    },
    /**
     * ActiveSupport.Element.hasClassName(element,class_name) -> Boolean
     **/
    hasClassName: function hasClassName(element,class_name)
    {
        if(!element)
        {
            return false;
        }
        var element_class_name = element.className;
        return (element_class_name.length > 0 && (element_class_name == class_name || new RegExp("(^|\\s)" + class_name + "(\\s|$)").test(element_class_name)));
    },
    /**
     * ActiveSupport.Element.addClassName(element,class_name) -> Element
     **/
    addClassName: function addClassName(element,class_name)
    {
        if(!element)
        {
            return false;
        }
        if(!ActiveSupport.Element.hasClassName(element,class_name))
        {
            element.className += (element.className ? ' ' : '') + class_name;
        }
        return element;
    },
    /**
     * ActiveSupport.Element.removeClassName(element,class_name) -> Element
     **/
    removeClassName: function removeClassName(element,class_name)
    {
        if(!element)
        {
            return false;
        }
        element.className = element.className.replace(new RegExp("(^|\\s+)" + class_name + "(\\s+|$)"),' ').replace(/^\s+/, '').replace(/\s+$/, '');
        return element;
    },
    getDimensions: function getDimensions(element)
    {
        var display = element.style.display;
        if(!display)
        {
            var css = document.defaultView.getComputedStyle(element,null);
            display = css ? css.display : null;
        }
        //safari bug
        if(display != 'none' && display != null)
        {
            return {
                width: element.offsetWidth,
                height: element.offsetHeight
            };
        }
        var element_style = element.style;
        var original_visibility = element_style.visibility;
        var original_position = element_style.position;
        var original_display = element_style.display;
        element_style.visibility = 'hidden';
        element_style.position = 'absolute';
        element_style.display = 'block';
        var original_width = element.clientWidth;
        var original_height = element.clientHeight;
        element_style.display = original_display;
        element_style.position = original_position;
        element_style.visibility = original_visibility;
        return {
            width: original_width,
            height: original_height
        };
    },
    /**
     * ActiveSupport.Element.getWidth(element) -> Number
     **/
    getWidth: function getWidth(element)
    {
        return ActiveSupport.Element.getDimensions(element).width;
    },
    /**
     * ActiveSupport.Element.getHeight(element) -> Number
     **/
    getHeight: function getHeight(element)
    {
        return ActiveSupport.Element.getDimensions(element).height;
    },
    documentReadyObservers: [],
    /**
     * ActiveSupport.Element.observe(element,event_name,callback[,context]) -> Function
     * - element (Element): The DOM element to observe.
     * - event_name (String): The name of the event, in all lower case, without the "on" prefix — e.g., "click" (not "onclick").
     * - callback (Function): The function to call when the event occurs.
     * - context (Object): The context to bind the callback to. Any additional arguments after context will be curried onto the callback.
     * This implementation of event observation is loosely based on Prototype's, but instead of adding element.stopObserving() and event.stop()
     * methods to the respective Element and Event objects, an event stopping callback and an event handler unregistration callback are passed
     * into your event handler.
     *
     *     ActiveSupport.Element.observe(element,'click',function(event,stop,unregister){
     *         stop();
     *         unregister();
     *     },this);
     *
     *     //Prototype equivelent:
     *
     *     var my_handler = element.observe('click',function(event){
     *         event.stop();
     *         element.stopObserving('click',my_handler);
     *     }.bind(this));
     *
     * dom:ready support is also built in:
     *
     *     ActiveSupport.Element.observe(document,'ready',function(){});
     *
     * If the above call was made after the document 'ready' event had already fired, the callback would be called immediately.
     **/
    observe: function observe(element,event_name,callback,context)
    {
        callback = ActiveSupport.Function.bindAndCurryFromArgumentsAboveIndex(callback || function(){},arguments,3);
        //dom:ready support
        if(element == ActiveSupport.getGlobalContext().document && event_name == 'ready')
        {
            if(ActiveSupport.Element.documentReadyObservers == null)
            {
                //ActiveSupport.Element.documentReadyObservers will be null if the document is ready
                //if so, trigger the observer now
                callback();
            }
            else
            {
                ActiveSupport.Element.documentReadyObservers.push(callback);
            }
            return;
        }

        //create callback wrapper
        var callback_wrapper = function callback_wrapper(event){
            if(!event)
            {
                event = window.event;
            }
            return callback(
                event,
                //event.srcElement ? (event.srcElement.nodeType == 3 ? event.srcElement.parentNode : event.srcElement) : null,
                function stop_callback(){
                    event.cancelBubble = true;
                    event.returnValue = false;
                    if(event.preventDefault)
                    {
                        event.preventDefault();
                    }
                    if(event.stopPropagation)
                    {
                        event.stopPropagation();
                    }
                },function remove_event_listener(){
                    if(element.removeEventListener)
                    {
                        element.removeEventListener(event_name,callback_wrapper,false);
                    }
                    else
                    {
                        element.detachEvent("on" + event_name,callback_wrapper);
                    }
                }
            );
        };

        //attach event listener
        if(element.addEventListener)
        {
            element.addEventListener(event_name,callback_wrapper,false);
        }
        else
        {
            element.attachEvent('on' + event_name,callback_wrapper);
        }

        return callback_wrapper;
    }
};

/*
Ported from Prototype.js usage:

    ActiveSupport.Element.observe(document,'ready',function(){

    });
*/
(function() {
  /* Support for the DOMContentLoaded event is based on work by Dan Webb,
     Matthias Miller, Dean Edwards, John Resig, and Diego Perini. */

  var timer;
  var loaded = false;

  function fire_content_loaded_event()
  {
      if(loaded)
      {
          return;
      }
      if(timer)
      {
          window.clearTimeout(timer);
      }
      loaded = true;
      if(ActiveSupport.Element.documentReadyObservers.length > 0)
      {
          for(var i = 0; i < ActiveSupport.Element.documentReadyObservers.length; ++i)
          {
              ActiveSupport.Element.documentReadyObservers[i]();
          }
          ActiveSupport.Element.documentReadyObservers = null;
      }
  };

  function check_ready_state(event,stop,stop_observing)
  {
      if(document.readyState === 'complete')
      {
          stop_observing();
          fire_content_loaded_event();
      }
  };

  function poll_do_scroll()
  {
      try
      {
          document.documentElement.doScroll('left');
      }
      catch(e)
      {
          timer = window.setTimeout(poll_do_scroll);
          return;
      }
      fire_content_loaded_event();
  };

  if(document.addEventListener)
  {
      document.addEventListener('DOMContentLoaded',fire_content_loaded_event,false);
  }
  else
  {
      ActiveSupport.Element.observe(document,'readystatechange',check_ready_state);
      if(window == top)
      {
          timer = window.setTimeout(poll_do_scroll);
      }
  }

  ActiveSupport.Element.observe(window,'load',fire_content_loaded_event);
})();

//Ajax Library integration
(function(){
    //Prototype
    if(global_context.Prototype && global_context.Prototype.Browser && global_context.Prototype.Browser.IE && global_context.Element && global_context.Element.extend)
    {
        ActiveSupport.Element.extend = function extendForPrototype(element){
          return Element.extend(element);
        };
    };
    //MooTools
    if(global_context.MooTools && global_context.Browser && global_context.Browser.Engine.trident && global_context.document.id)
    {
        ActiveSupport.Element.extend = function extendForMooTools(element){
            return global_context.document.id(element);
        };
    }
})();<commit_msg>IE8 fix<commit_after>var global_context = ActiveSupport.getGlobalContext();
var ie = !!(global_context.attachEvent && !global_context.opera);

/**
 * ActiveSupport.Element
 * ActiveSupport.Element is a simple DOM manipulation library that does not modify the built in Element object. All ActiveSupport.Element methods take an Element object (and not a string) as their first argument. ActiveSupport.Element is available inside ActiveView classes as the second argument:
 *
 *     var MyClass = ActiveView.create(function(builder,dom){
 *         var link = builder.a({href:'#'},'Text');
 *         dom.addClassName(link,'active');
 *         dom.getWidth(link);
 *         return builder.div(link);
 *     });
 *
 * The implementation of event obeserver's differs from Prototype's since it does not modify the Element object. Your observer receives three arguments, the Event object, a function that will stop the event when called, and a function that will unregister the observer.
 *
 *     var dom = ActiveSupport.Element;
 *     dom.observe(link,'click',function(event,stop,unregister){
 *         //do stuff
 *         stop();
 *     });
 *
 * ActiveSupport.Element also supports the a similar event to Prototype's dom:ready:
 *
 *     dom.observe(document,'ready',function(){
 *         //...
 *     });
 **/
ActiveSupport.Element = {
    ieAttributeTranslations: {
        'class': 'className',
        'checked': 'defaultChecked',
        'usemap': 'useMap',
        'for': 'htmlFor',
        'readonly': 'readOnly',
        'colspan': 'colSpan',
        'bgcolor': 'bgColor',
        'cellspacing': 'cellSpacing',
        'cellpadding': 'cellPadding'
    },
    ieAttributeTranslationSniffingCache: {},
    /**
     * ActiveSupport.Element.keyCodes -> Object
     * Contains the following:
     *
     * - KEY_BACKSPACE
     * - KEY_TAB
     * - KEY_RETURN
     * - KEY_ESC
     * - KEY_LEFT
     * - KEY_UP
     * - KEY_RIGHT
     * - KEY_DOWN
     * - KEY_DELETE
     * - KEY_HOME
     * - KEY_END
     * - KEY_PAGEUP
     * - KEY_PAGEDOWN
     * - KEY_INSERT
     **/
    keyCodes: {
        KEY_BACKSPACE: 8,
        KEY_TAB:       9,
        KEY_RETURN:   13,
        KEY_ESC:      27,
        KEY_LEFT:     37,
        KEY_UP:       38,
        KEY_RIGHT:    39,
        KEY_DOWN:     40,
        KEY_DELETE:   46,
        KEY_HOME:     36,
        KEY_END:      35,
        KEY_PAGEUP:   33,
        KEY_PAGEDOWN: 34,
        KEY_INSERT:   45
    },
    cache: {},
    /**
     * ActiveSupport.Element.create(tag_name,attributes_hash) -> Element
     **/
    create: function create(tag_name,attributes)
    {
        attributes = attributes || {};
        tag_name = tag_name.toLowerCase();
        var element;
        if(ie && (attributes.name || (tag_name == 'input' && attributes.type)))
        {
            //ie needs these attributes to be written in the string passed to createElement
            tag = '<' + tag_name;
            if(attributes.name)
            {
                tag += ' name="' + attributes.name + '"';
            }
            if(tag_name == 'input' && attributes.type)
            {
                tag += ' type="' + attributes.type + '"';
            }
            tag += '>';
            delete attributes.name;
            delete attributes.type;
            element = ActiveSupport.Element.extend(global_context.document.createElement(tag));
        }
        else
        {
            if(!ActiveSupport.Element.cache[tag_name])
            {
                ActiveSupport.Element.cache[tag_name] = ActiveSupport.Element.extend(global_context.document.createElement(tag_name));
            }
            element = ActiveSupport.Element.cache[tag_name].cloneNode(false);
        }
        ActiveSupport.Element.writeAttribute(element,attributes);
        return element;
    },
    extend: function extend(element)
    {
        return element;
    },
    /**
     * ActiveSupport.Element.clear(element) -> Element
     **/
    clear: function clear(element)
    {
        while(element.firstChild)
        {
            element.removeChild(element.firstChild);
        }
        return element;
    },
    /**
     * ActiveSupport.Element.hide(element) -> Element
     **/
    hide: function hide(element)
    {
        element.style.display = 'none';
        return element;
    },
    /**
     * ActiveSupport.Element.show(element) -> Element
     **/
    show: function show(element)
    {
        element.style.display = '';
        return element;
    },
    /**
     * ActiveSupport.Element.remove(element) -> Element
     **/
    remove: function remove(element)
    {
        element.parentNode.removeChild(element);
        return element;
    },
    /**
     * ActiveSupport.Element.insert(element,content[,position]) -> Element
     * - element (Element)
     * - content (String | Number | Element)
     * - position (String): "top", "bottom", "before", "after"
     * Note that this element does not identically mimic Prototype's Element.prototype.insert
     **/
    insert: function insert(element,content,position)
    {
        if(content && typeof(content.getElement) == 'function')
        {
            content = content.getElement();
        }
        if(ActiveSupport.Object.isArray(content))
        {
            for(var i = 0; i < content.length; ++i)
            {
                ActiveSupport.Element.insert(element,content[i],position);
            }
        }
        else
        {
            if(!content || !content.nodeType || content.nodeType !== 1)
            {
                content = global_context.document.createTextNode(String(content));
            }
            if(!position)
            {
                position = 'bottom';
            }
            switch(position)
            {
                case 'top': element.insertBefore(content,element.firstChild); break;
                case 'bottom': element.appendChild(content); break;
                case 'before': element.parentNode.insertBefore(content,element); break;
                case 'after': element.parentNode.insertBefore(content,element.nextSibling); break;
            }
        }
        return element;
    },
    /**
     * ActiveSupport.Element.update(element,content[,position]) -> Element
     * Works exactly like update, but calls ActiveSupport.Element.clear() on the element first.
     **/
    update: function update(element,content,position)
    {
        ActiveSupport.Element.clear(element);
        ActiveSupport.Element.insert(element,content,position);
        return element;
    },
    /**
     * ActiveSupport.Element.writeAttribute(element,name,value) -> Element
     * ActiveSupport.Element.writeAttribute(element,attributes_hash) -> Element
     **/
    writeAttribute: function writeAttribute(element,name,value)
    {
        var transitions = {
            className: 'class',
            htmlFor:   'for'
        };
        var attributes = {};
        if(typeof name === 'object')
        {
            attributes = name;
        }
        else
        {
            attributes[name] = typeof(value) === 'undefined' ? true : value;
        }
        for(var attribute_name in attributes)
        {
            name = transitions[attribute_name] || attribute_name;
            // check if things need to be remapped for IE (Some stuff has been fixed when IE > 7)
            if(ie && ActiveSupport.Element.ieAttributeTranslations[name])
            {
                if(name in ActiveSupport.Element.ieAttributeTranslationSniffingCache)
                {
                    if(ActiveSupport.Element.ieAttributeTranslationSniffingCache[name])
                    {
                        name = ActiveSupport.Element.ieAttributeTranslations[name];
                    }
                }
                else
                {
                    var test_element = ActiveSupport.getGlobalContext().document.createElement("div");
                    test_element.setAttribute(name,"test");
                    if(test_element[ActiveSupport.Element.ieAttributeTranslations[name]] !== "test") {
                        test_element.setAttribute(ActiveSupport.Element.ieAttributeTranslations[name],"test");
                        ActiveSupport.Element.ieAttributeTranslationSniffingCache[name] = test_element[ActiveSupport.Element.ieAttributeTranslations[name]] === "test";
                        if(ActiveSupport.Element.ieAttributeTranslationSniffingCache[name])
                        {
                            name = ActiveSupport.Element.ieAttributeTranslations[name];
                        }
                    }
                }
            }
            value = attributes[attribute_name];
            if(value === false || value === null)
            {
                element.removeAttribute(name);
            }
            else if(value === true)
            {
                element.setAttribute(name,name);
            }
            else
            {
                if(name == 'style')
                {
                    element.style.cssText = value;
                }
                else
                {
                    element.setAttribute(name,value);
                }
            }
        }
        return element;
    },
    /**
     * ActiveSupport.Element.hasClassName(element,class_name) -> Boolean
     **/
    hasClassName: function hasClassName(element,class_name)
    {
        if(!element)
        {
            return false;
        }
        var element_class_name = element.className;
        return (element_class_name.length > 0 && (element_class_name == class_name || new RegExp("(^|\\s)" + class_name + "(\\s|$)").test(element_class_name)));
    },
    /**
     * ActiveSupport.Element.addClassName(element,class_name) -> Element
     **/
    addClassName: function addClassName(element,class_name)
    {
        if(!element)
        {
            return false;
        }
        if(!ActiveSupport.Element.hasClassName(element,class_name))
        {
            element.className += (element.className ? ' ' : '') + class_name;
        }
        return element;
    },
    /**
     * ActiveSupport.Element.removeClassName(element,class_name) -> Element
     **/
    removeClassName: function removeClassName(element,class_name)
    {
        if(!element)
        {
            return false;
        }
        element.className = element.className.replace(new RegExp("(^|\\s+)" + class_name + "(\\s+|$)"),' ').replace(/^\s+/, '').replace(/\s+$/, '');
        return element;
    },
    getDimensions: function getDimensions(element)
    {
        var display = element.style.display;
        if(!display)
        {
            var css = document.defaultView.getComputedStyle(element,null);
            display = css ? css.display : null;
        }
        //safari bug
        if(display != 'none' && display != null)
        {
            return {
                width: element.offsetWidth,
                height: element.offsetHeight
            };
        }
        var element_style = element.style;
        var original_visibility = element_style.visibility;
        var original_position = element_style.position;
        var original_display = element_style.display;
        element_style.visibility = 'hidden';
        element_style.position = 'absolute';
        element_style.display = 'block';
        var original_width = element.clientWidth;
        var original_height = element.clientHeight;
        element_style.display = original_display;
        element_style.position = original_position;
        element_style.visibility = original_visibility;
        return {
            width: original_width,
            height: original_height
        };
    },
    /**
     * ActiveSupport.Element.getWidth(element) -> Number
     **/
    getWidth: function getWidth(element)
    {
        return ActiveSupport.Element.getDimensions(element).width;
    },
    /**
     * ActiveSupport.Element.getHeight(element) -> Number
     **/
    getHeight: function getHeight(element)
    {
        return ActiveSupport.Element.getDimensions(element).height;
    },
    documentReadyObservers: [],
    /**
     * ActiveSupport.Element.observe(element,event_name,callback[,context]) -> Function
     * - element (Element): The DOM element to observe.
     * - event_name (String): The name of the event, in all lower case, without the "on" prefix — e.g., "click" (not "onclick").
     * - callback (Function): The function to call when the event occurs.
     * - context (Object): The context to bind the callback to. Any additional arguments after context will be curried onto the callback.
     * This implementation of event observation is loosely based on Prototype's, but instead of adding element.stopObserving() and event.stop()
     * methods to the respective Element and Event objects, an event stopping callback and an event handler unregistration callback are passed
     * into your event handler.
     *
     *     ActiveSupport.Element.observe(element,'click',function(event,stop,unregister){
     *         stop();
     *         unregister();
     *     },this);
     *
     *     //Prototype equivelent:
     *
     *     var my_handler = element.observe('click',function(event){
     *         event.stop();
     *         element.stopObserving('click',my_handler);
     *     }.bind(this));
     *
     * dom:ready support is also built in:
     *
     *     ActiveSupport.Element.observe(document,'ready',function(){});
     *
     * If the above call was made after the document 'ready' event had already fired, the callback would be called immediately.
     **/
    observe: function observe(element,event_name,callback,context)
    {
        callback = ActiveSupport.Function.bindAndCurryFromArgumentsAboveIndex(callback || function(){},arguments,3);
        //dom:ready support
        if(element == ActiveSupport.getGlobalContext().document && event_name == 'ready')
        {
            if(ActiveSupport.Element.documentReadyObservers == null)
            {
                //ActiveSupport.Element.documentReadyObservers will be null if the document is ready
                //if so, trigger the observer now
                callback();
            }
            else
            {
                ActiveSupport.Element.documentReadyObservers.push(callback);
            }
            return;
        }

        //create callback wrapper
        var callback_wrapper = function callback_wrapper(event){
            if(!event)
            {
                event = window.event;
            }
            return callback(
                event,
                //event.srcElement ? (event.srcElement.nodeType == 3 ? event.srcElement.parentNode : event.srcElement) : null,
                function stop_callback(){
                    event.cancelBubble = true;
                    event.returnValue = false;
                    if(event.preventDefault)
                    {
                        event.preventDefault();
                    }
                    if(event.stopPropagation)
                    {
                        event.stopPropagation();
                    }
                },function remove_event_listener(){
                    if(element.removeEventListener)
                    {
                        element.removeEventListener(event_name,callback_wrapper,false);
                    }
                    else
                    {
                        element.detachEvent("on" + event_name,callback_wrapper);
                    }
                }
            );
        };

        //attach event listener
        if(element.addEventListener)
        {
            element.addEventListener(event_name,callback_wrapper,false);
        }
        else
        {
            element.attachEvent('on' + event_name,callback_wrapper);
        }

        return callback_wrapper;
    }
};

/*
Ported from Prototype.js usage:

    ActiveSupport.Element.observe(document,'ready',function(){

    });
*/
(function() {
  /* Support for the DOMContentLoaded event is based on work by Dan Webb,
     Matthias Miller, Dean Edwards, John Resig, and Diego Perini. */

  var timer;
  var loaded = false;

  function fire_content_loaded_event()
  {
      if(loaded)
      {
          return;
      }
      if(timer)
      {
          window.clearTimeout(timer);
      }
      loaded = true;
      if(ActiveSupport.Element.documentReadyObservers.length > 0)
      {
          for(var i = 0; i < ActiveSupport.Element.documentReadyObservers.length; ++i)
          {
              ActiveSupport.Element.documentReadyObservers[i]();
          }
          ActiveSupport.Element.documentReadyObservers = null;
      }
  };

  function check_ready_state(event,stop,stop_observing)
  {
      if(document.readyState === 'complete')
      {
          stop_observing();
          fire_content_loaded_event();
      }
  };

  function poll_do_scroll()
  {
      try
      {
          document.documentElement.doScroll('left');
      }
      catch(e)
      {
          timer = window.setTimeout(poll_do_scroll);
          return;
      }
      fire_content_loaded_event();
  };

  if(document.addEventListener)
  {
      document.addEventListener('DOMContentLoaded',fire_content_loaded_event,false);
  }
  else
  {
      ActiveSupport.Element.observe(document,'readystatechange',check_ready_state);
      if(window == top)
      {
          timer = window.setTimeout(poll_do_scroll);
      }
  }

  ActiveSupport.Element.observe(window,'load',fire_content_loaded_event);
})();

//Ajax Library integration
(function(){
    //Prototype
    if(global_context.Prototype && global_context.Prototype.Browser && global_context.Prototype.Browser.IE && global_context.Element && global_context.Element.extend)
    {
        ActiveSupport.Element.extend = function extendForPrototype(element){
          return Element.extend(element);
        };
    };
    //MooTools
    if(global_context.MooTools && global_context.Browser && global_context.Browser.Engine.trident && global_context.document.id)
    {
        ActiveSupport.Element.extend = function extendForMooTools(element){
            return global_context.document.id(element);
        };
    }
})();<|endoftext|>


<commit_before>/*
Name:         europeana.js
Description:  Unofficial node.js module for the Europeana API
Author:       Franklin van de Meent - https://frankl.in
Source:       https://github.com/fvdm/nodejs-europeana
Feedback:     https://github.com/fvdm/nodejs-europeana/issues
License:      Public Domain / Unlicense (see UNLICENSE file)
              (https://github.com/fvdm/nodejs-europeana/raw/master/UNLICENSE)
*/

var http = require ('http') .request;
var querystring = require ('querystring');
module.exports = {};

// config
module.exports.timeout = 5000;
module.exports.apikey = null;

// errors
// http://www.europeana.eu/portal/api-working-with-api.html#Error-Codes

var errors = {
  400: 'The request sent by the client was syntactically incorrect',
  401: 'Authentication credentials were missing or authentication failed.',
  404: 'The requested record was not found.',
  429: 'The request could be served because the application has reached its usage limit.',
  500: 'Internal Server Error. Something has gone wrong, please report to us.'
};

// get one item
// http://www.europeana.eu/portal/api-record-json.html
module.exports.record = function (id, profile, callback) {
  if (typeof profile === 'function') {
    var callback = profile;
    var profile = 'full';
  }
  talk ('record/'+ id, {profile: profile}, callback);
};

// search items
// http://www.europeana.eu/portal/api-search-json.html
module.exports.search = function (query, vars, callback) {
  if (typeof vars === 'function') {
    var callback = vars;
    var vars = {};
  }
  vars.query = query;
  talk ('search', vars, callback);
};

// livesearch suggestions
// http://www.europeana.eu/portal/api-suggestions-json.html
module.exports.suggestions = function (query, rows, callback) {
  if (typeof rows === 'function') {
    var callback = rows;
    var rows = 10;
  }
  talk ('suggestions', {query: query, rows: rows}, callback);
};

// communicate with the API
function talk (path, fields, callback) {
  if (typeof fields === 'function') {
    var callback = fields;
    var fields = {};
  }

  // prevent multiple callbacks
  var complete = false;
  function doCallback (err, res, meta) {
    if (!complete) {
      complete = true;
      callback (err || null, res || null, meta || null);
    }
  }

  // check API key
  if (!module.exports.apikey) {
    doCallback (new Error ('apikey missing'));
    return;
  }

  // build request
  fields.wskey = module.exports.apikey;

  var query = '?'+ querystring.stringify (fields);

  var options = {
    host: 'europeana.eu',
    path: '/api/v2/'+ path +'.json'+ query,
    method: 'GET',
    headers: {
      'User-Agent': 'europeana.js (https://bitbucket.org/fvdm/nodejs-europeana)'
    }
  };

  var request = http (options);

  // request failed
  request.on ('error', function (error) {
    var err = new Error ('request failed');
    err.error = error;
    doCallback (err);
  });

  // request timeout
  request.on ('socket', function (socket) {
    socket.setTimeout (module.exports.timeout);
    socket.on ('timeout', function () {
      request.abort ();
      doCallback (new Error ('request timeout'));
    });
  });

  // response
  request.on ('response', function (response) {
    var data = '';

    // too early disconnected
    response.on ('close', function () {
      doCallback (new Error ('request dropped'));
    });

    // receiving data
    response.on ('data', function (ch) {
      data += ch;
    });

    // process response data
    response.on ('end', function () {
      data = data.trim ();

      if (response.statusCode !== 200) {
        var er = new Error ('API error');
        er.code = response.statusCode;
        er.error = errors [response.statusCode];

        if (!!~data.indexOf ('
HTTP Status ')) {
          er.error = data.replace (/.*description<\/b> (.+)<\/u><\/p>.*/, '$1');
        }
        doCallback (er);
      } else {
        try {
          // json
          data = JSON.parse (data);
        } catch (reason) {
          // can't read this
          var er = new Error ('invalid response');
          er.error = reason;
          er.data = data;
          doCallback (er);
        }

        if (data && !data.success && data.error) {
          var er = new Error ('API error');
          er.error = data.error;
          doCallback (er);
        } else if (data) {
          doCallback (null, data);
        } else {
          doCallback (new Error ('unknown error'));
        }
      }
    })
  })

  // do it
  request.end ();
}
<commit_msg>Shorter User-Agent<commit_after>/*
Name:         europeana.js
Description:  Unofficial node.js module for the Europeana API
Author:       Franklin van de Meent - https://frankl.in
Source:       https://github.com/fvdm/nodejs-europeana
Feedback:     https://github.com/fvdm/nodejs-europeana/issues
License:      Public Domain / Unlicense (see UNLICENSE file)
              (https://github.com/fvdm/nodejs-europeana/raw/master/UNLICENSE)
*/

var http = require ('http') .request;
var querystring = require ('querystring');
module.exports = {};

// config
module.exports.timeout = 5000;
module.exports.apikey = null;

// errors
// http://www.europeana.eu/portal/api-working-with-api.html#Error-Codes

var errors = {
  400: 'The request sent by the client was syntactically incorrect',
  401: 'Authentication credentials were missing or authentication failed.',
  404: 'The requested record was not found.',
  429: 'The request could be served because the application has reached its usage limit.',
  500: 'Internal Server Error. Something has gone wrong, please report to us.'
};

// get one item
// http://www.europeana.eu/portal/api-record-json.html
module.exports.record = function (id, profile, callback) {
  if (typeof profile === 'function') {
    var callback = profile;
    var profile = 'full';
  }
  talk ('record/'+ id, {profile: profile}, callback);
};

// search items
// http://www.europeana.eu/portal/api-search-json.html
module.exports.search = function (query, vars, callback) {
  if (typeof vars === 'function') {
    var callback = vars;
    var vars = {};
  }
  vars.query = query;
  talk ('search', vars, callback);
};

// livesearch suggestions
// http://www.europeana.eu/portal/api-suggestions-json.html
module.exports.suggestions = function (query, rows, callback) {
  if (typeof rows === 'function') {
    var callback = rows;
    var rows = 10;
  }
  talk ('suggestions', {query: query, rows: rows}, callback);
};

// communicate with the API
function talk (path, fields, callback) {
  if (typeof fields === 'function') {
    var callback = fields;
    var fields = {};
  }

  // prevent multiple callbacks
  var complete = false;
  function doCallback (err, res, meta) {
    if (!complete) {
      complete = true;
      callback (err || null, res || null, meta || null);
    }
  }

  // check API key
  if (!module.exports.apikey) {
    doCallback (new Error ('apikey missing'));
    return;
  }

  // build request
  fields.wskey = module.exports.apikey;

  var query = '?'+ querystring.stringify (fields);

  var options = {
    host: 'europeana.eu',
    path: '/api/v2/'+ path +'.json'+ query,
    method: 'GET',
    headers: {
      'User-Agent': 'europeana.js'
    }
  };

  var request = http (options);

  // request failed
  request.on ('error', function (error) {
    var err = new Error ('request failed');
    err.error = error;
    doCallback (err);
  });

  // request timeout
  request.on ('socket', function (socket) {
    socket.setTimeout (module.exports.timeout);
    socket.on ('timeout', function () {
      request.abort ();
      doCallback (new Error ('request timeout'));
    });
  });

  // response
  request.on ('response', function (response) {
    var data = '';

    // too early disconnected
    response.on ('close', function () {
      doCallback (new Error ('request dropped'));
    });

    // receiving data
    response.on ('data', function (ch) {
      data += ch;
    });

    // process response data
    response.on ('end', function () {
      data = data.trim ();

      if (response.statusCode !== 200) {
        var er = new Error ('API error');
        er.code = response.statusCode;
        er.error = errors [response.statusCode];

        if (!!~data.indexOf ('
HTTP Status ')) {
          er.error = data.replace (/.*description<\/b> (.+)<\/u><\/p>.*/, '$1');
        }
        doCallback (er);
      } else {
        try {
          // json
          data = JSON.parse (data);
        } catch (reason) {
          // can't read this
          var er = new Error ('invalid response');
          er.error = reason;
          er.data = data;
          doCallback (er);
        }

        if (data && !data.success && data.error) {
          var er = new Error ('API error');
          er.error = data.error;
          doCallback (er);
        } else if (data) {
          doCallback (null, data);
        } else {
          doCallback (new Error ('unknown error'));
        }
      }
    })
  })

  // do it
  request.end ();
}
<|endoftext|>


<commit_before>var config = require('../config');

var express = require('express');
var _ = require('lodash');
var request = require('request');
var fs = require('fs');
var xml2js = require('xml2js');

var async = require('async')

var router = express.Router();

var MongoClient = require('mongodb').MongoClient
var mongo_url = config.mongo + config.db;

require('../models/reactions.js');
require('../models/definition.js');
require('../models/votes.js');

// TO DO: GET list of reactions
router.get('/', function(req, res, next) {
  var example = ["UTERINE HAEMORRHAGE","BLOOD CULTURE POSITIVE","CONVULSION","DEPRESSED LEVEL OF CONSCIOUSNESS","ELECTROENCEPHALOGRAM ABNORMAL","FUNGAL INFECTION","HYPOTENSION","HYPOXIA","MENTAL STATUS CHANGES","MULTI-ORGAN FAILURE","PANCREATITIS","SEPSIS","SPUTUM CULTURE POSITIVE","BRADYCARDIA","EMBOLISM","ILEUS PARALYTIC","INTESTINAL ISCHAEMIA","OXYGEN SATURATION DECREASED","RENAL FAILURE ACUTE","RENAL TUBULAR NECROSIS","AIDS ENCEPHALOPATHY"];
  res.json(example);
});

// TO DO: Post new or update reaction definition
router.post('/', function(req, res, next) {
  // Validate that incoming request is ok... and not a duplicate
  //res.json({todo: 'post reaction definition'});

  var reaction = new Reaction();

  if(Object.keys(req.body).length != 1 ||
     req.body.reaction == null ||
     typeof req.body.reaction != "string") {
    var err = new Error();
    err.status = 400;
    err.error = "Invalid Request Body";
    err.message = "Either the incorrect number of attributes were provided or" +
                  " the 'reaction' attribute could not be found, or the" +
                  " 'reaction' attribute wasn't properly formatted.";
    next(err);
  }

  // Tries to find reaction in collection (returns the record if found)
  var findReaction = function(db, callback) {
    var collection = db.collection('reactions');
    collection.findOne({'reaction': req.body.reaction}, function(err, reaction) {
      callback(reaction);
    });
  }

  // Inserts reaction into Mongo
  var insertReaction = function(db, callback) {
    var collection = db.collection('reactions');
      collection.insert([
        reaction
      ], function(err, result) {
        callback(result);
      });

  }

  // Retrieves definition available from Merriam Webster Medical Dictionary API
  var getTermDefinitionFromMerriamWebsterMedical = function (callback) {

    var url = "http://www.dictionaryapi.com/api/v1/references/medical/xml/" + encodeURIComponent(reaction.reaction.toLowerCase()) + "?key=" + config.dictionaryapi_key;

    request(url, function (error, response, body) {

      if (!error && response.statusCode == 200) {

        var parser = new xml2js.Parser({ignoreAttrs: false});

        parser.parseString(body, function (err, result) {

          if (result) {

          _.forEach(result.entry_list.entry, function(v,k) {

            if (v.hw[0] == reaction.reaction.toLowerCase()) {

                if (v.def) {

                  if (Array.isArray(v.def[0].sensb[0].sens)) {

                      _.forEach(v.def[0].sensb, function (v,k) {

                        if (v.sens[0].dt[0]['_']) {

                          if (typeof v.sens[0].dt[0]['_'] == "string") {

                            console.log('adding');

                            var definition = new Definition;
                            var dt = new Date();

                            definition.definition = v.sens[0].dt[0]['_'];
                            definition.source = 'dictionaryapi.com';
                            definition.created_at = dt.getTime();
                            definition.created_by = "";
                            definition.votes = new Votes();

                            reaction.addDefinition(definition);

                          }

                        } else {
                          var def = {};
                          if (typeof v.sens[0].dt[0] == "string") {
                              console.log('adding');
                            var definition = new Definition;
                            var dt = new Date();

                            definition.definition = v.sens[0].dt[0];
                            definition.source = 'dictionaryapi.com';
                            definition.created_at = dt.getTime();
                            definition.created_by = "";
                            definition.votes = new Votes();
                            reaction.addDefinition(definition);

                          }

                        }

                      });

                    } else {

                      if (typeof v.def[0].sensb[0].sens[0].dt == "string") {
                          console.log('adding');
                        var definition = new Definition;
                        var dt = new Date();

                        definition.definition = v.def[0].sensb[0].sens[0].dt;
                        definition.source = 'dictionaryapi.com';
                        definition.created_at = dt.getTime();
                        definition.created_by = "";
                        definition.votes = new Votes();
                        reaction.addDefinition(definition);

                      }

                      }

                    }

                }
            });
            }

          });

          callback(null);

      } else {
        callback(null);
      }
    })
  }

  // Retrieves definition available from Wordnik API
  var getDefinitionFromWordnik = function (callback) {
    var url = "http://api.wordnik.com:80/v4/word.json/" + encodeURIComponent(reaction.reaction.toLowerCase()) + "/definitions?limit=200&includeRelated=true&useCanonical=false&includeTags=false&api_key=" + config.wordnikapi_key ;

    request(url, function (error, response, body) {
      if (!error && response.statusCode == 200) {
        var deflist = JSON.parse(body);
        if (deflist[0]) {
          _.forEach(deflist, function(v,k) {

            var definition = new Definition;

            if (v.partOfSpeech == "noun") {
              var dt = new Date();

              definition.definition = v.text;
              definition.source = 'wordnik.com';
              definition.created_at = dt.getTime();
              definition.created_by = "";
              definition.votes = new Votes();

              reaction.addDefinition(definition);
            }

          });

        }
       callback();

      } else {
        callback();
      }
    })

  }

  // - Establish connection to mongo
  // - Check to see if reaction exists
  // - If it does not exist, find definitions and insert

  MongoClient.connect(mongo_url, function(err, db) {
    findReaction(db, function(result) {

      if(result) {
        var err = new Error();
        err.status = 400;
        err.error = "Duplicate Reaction";
        err.message = "The reaction that you are trying to create already exists" +
                      " and cannot be created again.";

        next(err);
      } else {

        var dt = new Date();

        reaction.reaction = req.body.reaction;
        reaction.definitions = [];
        reaction.created_at = dt.getTime();
        reaction.created_by = "";

        async.series([
            function(callback){
              getTermDefinitionFromMerriamWebsterMedical(function (result) {
                console.log(reaction);
                callback(null);
              });
            },
            function(callback){
              getDefinitionFromWordnik(function (result) {
                console.log(reaction);
                callback(null);
              });
            }
        ],

        // insert into mongo
        function(err, results){

          insertReaction(db, function(result) {
              res.json(reaction);
          });

        });

      }
   });

 });

});

// TO DO: Get reaction defintion
router.get('/:id', function(req, res, next) {
  var id = req.params.id;
  var example = JSON.parse('{"reaction":"' + id + '","terms":[{"term":"urosepsis","text":"sepsis caused by bacteria from the urinary tract invading the bloodstream","attributionText":"from Wiktionary, Creative Commons Attribution/Share-Alike License","source":"wordnik"},{"term":"urosepsis","text":"sepsis caused by bacteria from the urinary tract invading the bloodstream","attributionText":"from Wiktionary, Creative Commons Attribution/Share-Alike License","source":"wordnik"}]}');
  res.json(example);
});

// TO DO: Put reaction defintion
router.put('/:id', function(req, res, next) {
  var id = req.params.id;
  var example = JSON.parse('{"reaction":"' + id + '","terms":[{"term":"urosepsis","text":"sepsis caused by bacteria from the urinary tract invading the bloodstream","attributionText":"from Wiktionary, Creative Commons Attribution/Share-Alike License","source":"wordnik"},{"term":"urosepsis","text":"sepsis caused by bacteria from the urinary tract invading the bloodstream","attributionText":"from Wiktionary, Creative Commons Attribution/Share-Alike License","source":"wordnik"}]}');
  res.json(example);
});

// TO DO: Delete reaction definition
router.delete('/:id', function(req, res, next) {
  res.json({todo: 'delete reaction definition ' +  req.params.id});
});

module.exports = router;
<commit_msg>added db.close to properly close mongodb connections (root cause) and also added err validation following connection to resond with HTTP500 if error does occur during db connection<commit_after>var config = require('../config');

var express = require('express');
var _ = require('lodash');
var request = require('request');
var fs = require('fs');
var xml2js = require('xml2js');

var async = require('async')

var router = express.Router();

var MongoClient = require('mongodb').MongoClient
var mongo_url = config.mongo + config.db;

require('../models/reactions.js');
require('../models/definition.js');
require('../models/votes.js');

// TO DO: GET list of reactions
router.get('/', function(req, res, next) {
  var example = ["UTERINE HAEMORRHAGE","BLOOD CULTURE POSITIVE","CONVULSION","DEPRESSED LEVEL OF CONSCIOUSNESS","ELECTROENCEPHALOGRAM ABNORMAL","FUNGAL INFECTION","HYPOTENSION","HYPOXIA","MENTAL STATUS CHANGES","MULTI-ORGAN FAILURE","PANCREATITIS","SEPSIS","SPUTUM CULTURE POSITIVE","BRADYCARDIA","EMBOLISM","ILEUS PARALYTIC","INTESTINAL ISCHAEMIA","OXYGEN SATURATION DECREASED","RENAL FAILURE ACUTE","RENAL TUBULAR NECROSIS","AIDS ENCEPHALOPATHY"];
  res.json(example);
});

// TO DO: Post new or update reaction definition
router.post('/', function(req, res, next) {
  // Validate that incoming request is ok... and not a duplicate
  //res.json({todo: 'post reaction definition'});

  var reaction = new Reaction();

  if(Object.keys(req.body).length != 1 ||
     req.body.reaction == null ||
     typeof req.body.reaction != "string") {
    var err = new Error();
    err.status = 400;
    err.error = "Invalid Request Body";
    err.message = "Either the incorrect number of attributes were provided or" +
                  " the 'reaction' attribute could not be found, or the" +
                  " 'reaction' attribute wasn't properly formatted.";
    next(err);
  }

  // Tries to find reaction in collection (returns the record if found)
  var findReaction = function(db, callback) {
    var collection = db.collection('reactions');
    collection.findOne({'reaction': req.body.reaction}, function(err, reaction) {
      callback(reaction);
    });
  }

  // Inserts reaction into Mongo
  var insertReaction = function(db, callback) {
    var collection = db.collection('reactions');
      collection.insert([
        reaction
      ], function(err, result) {
        callback(result);
      });

  }

  // Retrieves definition available from Merriam Webster Medical Dictionary API
  var getTermDefinitionFromMerriamWebsterMedical = function (callback) {

    var url = "http://www.dictionaryapi.com/api/v1/references/medical/xml/" + encodeURIComponent(reaction.reaction.toLowerCase()) + "?key=" + config.dictionaryapi_key;

    request(url, function (error, response, body) {

      if (!error && response.statusCode == 200) {

        var parser = new xml2js.Parser({ignoreAttrs: false});

        parser.parseString(body, function (err, result) {

          if (result) {

          _.forEach(result.entry_list.entry, function(v,k) {

            if (v.hw[0] == reaction.reaction.toLowerCase()) {

                if (v.def) {

                  if (Array.isArray(v.def[0].sensb[0].sens)) {

                      _.forEach(v.def[0].sensb, function (v,k) {

                        if (v.sens[0].dt[0]['_']) {

                          if (typeof v.sens[0].dt[0]['_'] == "string") {

                            console.log('adding');

                            var definition = new Definition;
                            var dt = new Date();

                            definition.definition = v.sens[0].dt[0]['_'];
                            definition.source = 'dictionaryapi.com';
                            definition.created_at = dt.getTime();
                            definition.created_by = "";
                            definition.votes = new Votes();

                            reaction.addDefinition(definition);

                          }

                        } else {
                          var def = {};
                          if (typeof v.sens[0].dt[0] == "string") {
                              console.log('adding');
                            var definition = new Definition;
                            var dt = new Date();

                            definition.definition = v.sens[0].dt[0];
                            definition.source = 'dictionaryapi.com';
                            definition.created_at = dt.getTime();
                            definition.created_by = "";
                            definition.votes = new Votes();
                            reaction.addDefinition(definition);

                          }

                        }

                      });

                    } else {

                      if (typeof v.def[0].sensb[0].sens[0].dt == "string") {
                          console.log('adding');
                        var definition = new Definition;
                        var dt = new Date();

                        definition.definition = v.def[0].sensb[0].sens[0].dt;
                        definition.source = 'dictionaryapi.com';
                        definition.created_at = dt.getTime();
                        definition.created_by = "";
                        definition.votes = new Votes();
                        reaction.addDefinition(definition);

                      }

                      }

                    }

                }
            });
            }

          });

          callback(null);

      } else {
        callback(null);
      }
    })
  }

  // Retrieves definition available from Wordnik API
  var getDefinitionFromWordnik = function (callback) {
    var url = "http://api.wordnik.com:80/v4/word.json/" + encodeURIComponent(reaction.reaction.toLowerCase()) + "/definitions?limit=200&includeRelated=true&useCanonical=false&includeTags=false&api_key=" + config.wordnikapi_key ;

    request(url, function (error, response, body) {
      if (!error && response.statusCode == 200) {
        var deflist = JSON.parse(body);
        if (deflist[0]) {
          _.forEach(deflist, function(v,k) {

            var definition = new Definition;

            if (v.partOfSpeech == "noun") {
              var dt = new Date();

              definition.definition = v.text;
              definition.source = 'wordnik.com';
              definition.created_at = dt.getTime();
              definition.created_by = "";
              definition.votes = new Votes();

              reaction.addDefinition(definition);
            }

          });

        }
       callback();

      } else {
        callback();
      }
    })

  }

  // - Establish connection to mongo
  // - Check to see if reaction exists
  // - If it does not exist, find definitions and insert

  MongoClient.connect(mongo_url, function(err, db) {

    if (err) {
      var err = new Error();
      err.status = 500;
      err.error = "Internal Error";
      next(err);
    }

    if (!err) {

      findReaction(db, function(result) {

        if(result) {
          var err = new Error();
          err.status = 400;
          err.error = "Duplicate Reaction";
          err.message = "The reaction that you are trying to create already exists" +
                        " and cannot be created again.";

          next(err);
        } else {

          var dt = new Date();

          reaction.reaction = req.body.reaction;
          reaction.definitions = [];
          reaction.created_at = dt.getTime();
          reaction.created_by = "";

          async.series([
              function(callback){
                getTermDefinitionFromMerriamWebsterMedical(function (result) {
                  console.log(reaction);
                  callback(null);
                });
              },
              function(callback){
                getDefinitionFromWordnik(function (result) {
                  console.log(reaction);
                  callback(null);
                });
              }
          ],

          // insert into mongo
          function(err, results){

            insertReaction(db, function(result) {
                res.json(reaction);
            });

          });

        }
     });

    }
    db.close();
 });

});

// TO DO: Get reaction defintion
router.get('/:id', function(req, res, next) {
  var id = req.params.id;
  var example = JSON.parse('{"reaction":"' + id + '","terms":[{"term":"urosepsis","text":"sepsis caused by bacteria from the urinary tract invading the bloodstream","attributionText":"from Wiktionary, Creative Commons Attribution/Share-Alike License","source":"wordnik"},{"term":"urosepsis","text":"sepsis caused by bacteria from the urinary tract invading the bloodstream","attributionText":"from Wiktionary, Creative Commons Attribution/Share-Alike License","source":"wordnik"}]}');
  res.json(example);
});

// TO DO: Put reaction defintion
router.put('/:id', function(req, res, next) {
  var id = req.params.id;
  var example = JSON.parse('{"reaction":"' + id + '","terms":[{"term":"urosepsis","text":"sepsis caused by bacteria from the urinary tract invading the bloodstream","attributionText":"from Wiktionary, Creative Commons Attribution/Share-Alike License","source":"wordnik"},{"term":"urosepsis","text":"sepsis caused by bacteria from the urinary tract invading the bloodstream","attributionText":"from Wiktionary, Creative Commons Attribution/Share-Alike License","source":"wordnik"}]}');
  res.json(example);
});

// TO DO: Delete reaction definition
router.delete('/:id', function(req, res, next) {
  res.json({todo: 'delete reaction definition ' +  req.params.id});
});

module.exports = router;
<|endoftext|>