<commit_before>iD.ui.RawTagEditor = function(context) {
    var event = d3.dispatch('change'),
        taginfo = iD.taginfo(),
        showBlank = false,
        state,
        preset,
        tags,
        id;

    function rawTagEditor(selection) {
        var count = Object.keys(tags).filter(function(d) { return d; }).length;

        selection.call(iD.ui.Disclosure()
            .title(t('inspector.all_tags') + ' (' + count + ')')
            .expanded(iD.ui.RawTagEditor.expanded || preset.isFallback())
            .on('toggled', toggled)
            .content(content));

        function toggled(expanded) {
            iD.ui.RawTagEditor.expanded = expanded;
            if (expanded) {
                selection.node().parentNode.scrollTop += 200;
            }
        }
    }

    function content($wrap) {
        var entries = d3.entries(tags);

        if (!entries.length || showBlank) {
            showBlank = false;
            entries.push({key: '', value: ''});
        }

        var $list = $wrap.selectAll('.tag-list')
            .data([0]);

        $list.enter().append('ul')
            .attr('class', 'tag-list');

        var $newTag = $wrap.selectAll('.add-tag')
            .data([0]);

        var $enter = $newTag.enter().append('button')
            .attr('class', 'add-tag');

        $enter.append('span')
            .attr('class', 'icon plus light');

        $newTag.on('click', addTag);

        var $items = $list.selectAll('li')
            .data(entries, function(d) { return d.key; });

        // Enter

        $enter = $items.enter().append('li')
            .attr('class', 'tag-row cf');

        $enter.append('div')
            .attr('class', 'key-wrap')
            .append('input')
            .property('type', 'text')
            .attr('class', 'key')
            .attr('maxlength', 255);

        $enter.append('div')
            .attr('class', 'input-wrap-position')
            .append('input')
            .property('type', 'text')
            .attr('class', 'value')
            .attr('maxlength', 255);

        $enter.append('button')
            .attr('tabindex', -1)
            .attr('class', 'remove minor')
            .append('span')
            .attr('class', 'icon delete');

        $enter.each(bindTypeahead);

        // Update

        $items.order();

        $items.each(function(tag) {
            var reference = iD.ui.TagReference({key: tag.key});

            if (state === 'hover') {
                reference.showing(false);
            }

            d3.select(this)
                .call(reference.button)
                .call(reference.body);
        });

        $items.select('input.key')
            .value(function(d) { return d.key; })
            .on('blur', keyChange)
            .on('change', keyChange);

        $items.select('input.value')
            .value(function(d) { return d.value; })
            .on('blur', valueChange)
            .on('change', valueChange)
            .on('keydown.push-more', pushMore);

        $items.select('button.remove')
            .on('click', removeTag);

        $items.exit()
            .remove();

        function pushMore() {
            if (d3.event.keyCode === 9 && !d3.event.shiftKey &&
                $list.selectAll('li:last-child input.value').node() === this) {
                addTag();
            }
        }

        function bindTypeahead() {
            var row = d3.select(this),
                key = row.selectAll('input.key'),
                value = row.selectAll('input.value');

            function sort(value, data) {
                var sameletter = [],
                    other = [];
                for (var i = 0; i < data.length; i++) {
                    if (data[i].value.substring(0, value.length) === value) {
                        sameletter.push(data[i]);
                    } else {
                        other.push(data[i]);
                    }
                }
                return sameletter.concat(other);
            }

            key.call(d3.combobox()
                .fetcher(function(value, callback) {
                    taginfo.keys({
                        debounce: true,
                        geometry: context.geometry(id),
                        query: value
                    }, function(err, data) {
                        if (!err) callback(sort(value, data));
                    });
                }));

            value.call(d3.combobox()
                .fetcher(function(value, callback) {
                    taginfo.values({
                        debounce: true,
                        key: key.value(),
                        geometry: context.geometry(id),
                        query: value
                    }, function(err, data) {
                        if (!err) callback(sort(value, data));
                    });
                }));
        }

        function keyChange(d) {
            var tag = {};
            tag[d.key] = undefined;
            tag[this.value] = d.value;
            d.key = this.value; // Maintain DOM identity through the subsequent update.
            event.change(tag);
        }

        function valueChange(d) {
            var tag = {};
            tag[d.key] = this.value;
            event.change(tag);
        }

        function removeTag(d) {
            var tag = {};
            tag[d.key] = undefined;
            event.change(tag);
        }

        function addTag() {
            // Wrapped in a setTimeout in case it's being called from a blur
            // handler. Without the setTimeout, the call to `content` would
            // wipe out the pending value change.
            setTimeout(function() {
                showBlank = true;
                content($wrap);
                $list.selectAll('li:last-child input.key').node().focus();
            }, 0);
        }
    }

    rawTagEditor.state = function(_) {
        if (!arguments.length) return state;
        state = _;
        return rawTagEditor;
    };

    rawTagEditor.preset = function(_) {
        if (!arguments.length) return preset;
        preset = _;
        return rawTagEditor;
    };

    rawTagEditor.tags = function(_) {
        if (!arguments.length) return tags;
        tags = _;
        return rawTagEditor;
    };

    rawTagEditor.entityID = function(_) {
        if (!arguments.length) return id;
        id = _;
        return rawTagEditor;
    };

    return d3.rebind(rawTagEditor, event, 'on');
};
<commit_msg>Trim tag keys, and prevent duplicate tag keys (#2043)<commit_after>iD.ui.RawTagEditor = function(context) {
    var event = d3.dispatch('change'),
        taginfo = iD.taginfo(),
        showBlank = false,
        state,
        preset,
        tags,
        id;

    function rawTagEditor(selection) {
        var count = Object.keys(tags).filter(function(d) { return d; }).length;

        selection.call(iD.ui.Disclosure()
            .title(t('inspector.all_tags') + ' (' + count + ')')
            .expanded(iD.ui.RawTagEditor.expanded || preset.isFallback())
            .on('toggled', toggled)
            .content(content));

        function toggled(expanded) {
            iD.ui.RawTagEditor.expanded = expanded;
            if (expanded) {
                selection.node().parentNode.scrollTop += 200;
            }
        }
    }

    function content($wrap) {
        var entries = d3.entries(tags);

        if (!entries.length || showBlank) {
            showBlank = false;
            entries.push({key: '', value: ''});
        }

        var $list = $wrap.selectAll('.tag-list')
            .data([0]);

        $list.enter().append('ul')
            .attr('class', 'tag-list');

        var $newTag = $wrap.selectAll('.add-tag')
            .data([0]);

        var $enter = $newTag.enter().append('button')
            .attr('class', 'add-tag');

        $enter.append('span')
            .attr('class', 'icon plus light');

        $newTag.on('click', addTag);

        var $items = $list.selectAll('li')
            .data(entries, function(d) { return d.key; });

        // Enter

        $enter = $items.enter().append('li')
            .attr('class', 'tag-row cf');

        $enter.append('div')
            .attr('class', 'key-wrap')
            .append('input')
            .property('type', 'text')
            .attr('class', 'key')
            .attr('maxlength', 255);

        $enter.append('div')
            .attr('class', 'input-wrap-position')
            .append('input')
            .property('type', 'text')
            .attr('class', 'value')
            .attr('maxlength', 255);

        $enter.append('button')
            .attr('tabindex', -1)
            .attr('class', 'remove minor')
            .append('span')
            .attr('class', 'icon delete');

        $enter.each(bindTypeahead);

        // Update

        $items.order();

        $items.each(function(tag) {
            var reference = iD.ui.TagReference({key: tag.key});

            if (state === 'hover') {
                reference.showing(false);
            }

            d3.select(this)
                .call(reference.button)
                .call(reference.body);
        });

        $items.select('input.key')
            .value(function(d) { return d.key; })
            .on('blur', keyChange)
            .on('change', keyChange);

        $items.select('input.value')
            .value(function(d) { return d.value; })
            .on('blur', valueChange)
            .on('change', valueChange)
            .on('keydown.push-more', pushMore);

        $items.select('button.remove')
            .on('click', removeTag);

        $items.exit()
            .remove();

        function pushMore() {
            if (d3.event.keyCode === 9 && !d3.event.shiftKey &&
                $list.selectAll('li:last-child input.value').node() === this) {
                addTag();
            }
        }

        function bindTypeahead() {
            var row = d3.select(this),
                key = row.selectAll('input.key'),
                value = row.selectAll('input.value');

            function sort(value, data) {
                var sameletter = [],
                    other = [];
                for (var i = 0; i < data.length; i++) {
                    if (data[i].value.substring(0, value.length) === value) {
                        sameletter.push(data[i]);
                    } else {
                        other.push(data[i]);
                    }
                }
                return sameletter.concat(other);
            }

            key.call(d3.combobox()
                .fetcher(function(value, callback) {
                    taginfo.keys({
                        debounce: true,
                        geometry: context.geometry(id),
                        query: value
                    }, function(err, data) {
                        if (!err) callback(sort(value, data));
                    });
                }));

            value.call(d3.combobox()
                .fetcher(function(value, callback) {
                    taginfo.values({
                        debounce: true,
                        key: key.value(),
                        geometry: context.geometry(id),
                        query: value
                    }, function(err, data) {
                        if (!err) callback(sort(value, data));
                    });
                }));
        }

        function keyChange(d) {
            var kOld = d.key.trim(),
                kNew = this.value.trim(),
                tag = {};

            if (kNew && kNew !== kOld) {
                var a = kNew.split(/_(\d+)$/),
                    base = a[0],
                    suffix = (a.length > 1) ? parseInt(a[1]): 1;
                while (tags[kNew]) {  // rename key if already in use
                    kNew = base + '_' + suffix++;
                }
            }
            tag[kOld] = undefined;
            tag[kNew] = d.value;
            d.key = kNew; // Maintain DOM identity through the subsequent update.
            this.value = kNew;
            event.change(tag);
        }

        function valueChange(d) {
            var tag = {};
            tag[d.key] = this.value;
            event.change(tag);
        }

        function removeTag(d) {
            var tag = {};
            tag[d.key] = undefined;
            event.change(tag);
        }

        function addTag() {
            // Wrapped in a setTimeout in case it's being called from a blur
            // handler. Without the setTimeout, the call to `content` would
            // wipe out the pending value change.
            setTimeout(function() {
                showBlank = true;
                content($wrap);
                $list.selectAll('li:last-child input.key').node().focus();
            }, 0);
        }
    }

    rawTagEditor.state = function(_) {
        if (!arguments.length) return state;
        state = _;
        return rawTagEditor;
    };

    rawTagEditor.preset = function(_) {
        if (!arguments.length) return preset;
        preset = _;
        return rawTagEditor;
    };

    rawTagEditor.tags = function(_) {
        if (!arguments.length) return tags;
        tags = _;
        return rawTagEditor;
    };

    rawTagEditor.entityID = function(_) {
        if (!arguments.length) return id;
        id = _;
        return rawTagEditor;
    };

    return d3.rebind(rawTagEditor, event, 'on');
};
<|endoftext|>


<commit_before>/*
 * grunt-goog-webfont-dl
 * https://github.com/optimalisatie/grunt-goog-webfont-dl
 *
 * Copyright (c) 2014 Jan Jaap Hakvoort / info@optimalisatie.nl
 * Licensed under the MIT license.
 */

'use strict';

module.exports = function(grunt) {

    // Please see the Grunt documentation for more information regarding task
    // creation: http://gruntjs.com/creating-tasks

    var _ = require('lodash');
    var path = require('path');

    grunt.registerMultiTask('goog-webfont-dl', 'Grunt wrapper for goog-webfont-dl (Google WebFont Downloader)', function() {
        var done = this.async();
        // Merge task-specific and/or target-specific options with these defaults.
        var options = this.options({
            ttf: true,
            eot: true,
            woff: true,
            woff2: true,
            svg: true,
            fontname: '',
            fontstyles: '',
            fontdest: '',
            cssdest: '',
            cssprefix: '',
            subset: ''
        });

        if (options.fontname === '') {
            grunt.fail.warn('Fontname must not be empty.');
            return;
        }

        /*grunt.file.write( options.outputfile, content );*/

        var args = [];
        var all = true;
        var types = [];
        _.each(['ttf','eot','woff','woff2','svg'], function(value, key) {
            if (options[value] === false) {
                all = false;
            } else {
                types.push(value);
            }
        });
        if (all) {
            args.push('--all');
        } else {
            _.each(types, function(value, key) {
                switch(value) {
                    default:
                        args.push('--'+value);
                    break;
                }
            });
        }

        // Loop through the options and add them to args
        // Omit urls from the options to be passed through
        _.each(options, function(value, key) {
            if (value === '') {
                return;
            }
            switch (key) {
                case "fontname":
                    if (!_.isString(value)) {
                        grunt.fail.warn('Invalid Google font-name.');
                    }
                    args.push('--font');
                    args.push(value);
                    break;
                case "fontstyles":
                    if (!_.isString(value)) {
                        grunt.fail.warn('Invalid Google font-styles.');
                    }
                    args.push('--styles');
                    args.push(value);
                    break;
                case "fontdest":
                    if (!_.isString(value)) {
                        grunt.fail.warn('Invalid font files destination.');
                    }
                    args.push('--destination');
                    args.push(value);
                    break;
                case "cssdest":
                    if (!_.isString(value)) {
                        grunt.fail.warn('Invalid CSS file destination.');
                    }
                    args.push('--out');
                    args.push(value);
                    break;
                case "cssprefix":
                    if (!_.isString(value)) {
                        grunt.fail.warn('Invalid CSS prefix.');
                    }
                    args.push('--prefix');
                    args.push(value);
                    break;
                case "subset":
                    if (!_.isString(value)) {
                        grunt.fail.warn('Invalid character subset.');
                    }
                    args.push('--subset');
                    args.push(value);
                    break;
            }
        });

        //grunt.log.writeln(args);
        grunt.log.write('Start download of Google WebFont sourcefiles...');

        grunt.util.spawn({
            cmd: 'node_modules/grunt-goog-webfont-dl/node_modules/goog-webfont-dl/index.js',
            args: args
        }, function(err, result, code) {

            //grunt.log.write(JSON.stringify(result));
            if (err) {
                if (err.toString() === 'Error' && result.stdout !== '') {
                    grunt.log.error(result.stdout);
                }
                done(err);
                return;
            }

            grunt.log.ok();

            done();
        });

    });

};
<commit_msg>Path for new NPM dependencies layout<commit_after>/*
 * grunt-goog-webfont-dl
 * https://github.com/optimalisatie/grunt-goog-webfont-dl
 *
 * Copyright (c) 2014 Jan Jaap Hakvoort / info@optimalisatie.nl
 * Licensed under the MIT license.
 */

'use strict';

module.exports = function(grunt) {

    // Please see the Grunt documentation for more information regarding task
    // creation: http://gruntjs.com/creating-tasks

    var _ = require('lodash');
    var path = require('path');

    grunt.registerMultiTask('goog-webfont-dl', 'Grunt wrapper for goog-webfont-dl (Google WebFont Downloader)', function() {
        var done = this.async();
        // Merge task-specific and/or target-specific options with these defaults.
        var options = this.options({
            ttf: true,
            eot: true,
            woff: true,
            woff2: true,
            svg: true,
            fontname: '',
            fontstyles: '',
            fontdest: '',
            cssdest: '',
            cssprefix: '',
            subset: ''
        });

        if (options.fontname === '') {
            grunt.fail.warn('Fontname must not be empty.');
            return;
        }

        /*grunt.file.write( options.outputfile, content );*/

        var args = [];
        var all = true;
        var types = [];
        _.each(['ttf','eot','woff','woff2','svg'], function(value, key) {
            if (options[value] === false) {
                all = false;
            } else {
                types.push(value);
            }
        });
        if (all) {
            args.push('--all');
        } else {
            _.each(types, function(value, key) {
                switch(value) {
                    default:
                        args.push('--'+value);
                    break;
                }
            });
        }

        // Loop through the options and add them to args
        // Omit urls from the options to be passed through
        _.each(options, function(value, key) {
            if (value === '') {
                return;
            }
            switch (key) {
                case "fontname":
                    if (!_.isString(value)) {
                        grunt.fail.warn('Invalid Google font-name.');
                    }
                    args.push('--font');
                    args.push(value);
                    break;
                case "fontstyles":
                    if (!_.isString(value)) {
                        grunt.fail.warn('Invalid Google font-styles.');
                    }
                    args.push('--styles');
                    args.push(value);
                    break;
                case "fontdest":
                    if (!_.isString(value)) {
                        grunt.fail.warn('Invalid font files destination.');
                    }
                    args.push('--destination');
                    args.push(value);
                    break;
                case "cssdest":
                    if (!_.isString(value)) {
                        grunt.fail.warn('Invalid CSS file destination.');
                    }
                    args.push('--out');
                    args.push(value);
                    break;
                case "cssprefix":
                    if (!_.isString(value)) {
                        grunt.fail.warn('Invalid CSS prefix.');
                    }
                    args.push('--prefix');
                    args.push(value);
                    break;
                case "subset":
                    if (!_.isString(value)) {
                        grunt.fail.warn('Invalid character subset.');
                    }
                    args.push('--subset');
                    args.push(value);
                    break;
            }
        });

        //grunt.log.writeln(args);
        grunt.log.write('Start download of Google WebFont sourcefiles...');

        grunt.util.spawn({
            cmd: require.resolve('goog-webfont-dl'),
            args: args
        }, function(err, result, code) {

            //grunt.log.write(JSON.stringify(result));
            if (err) {
                if (err.toString() === 'Error' && result.stdout !== '') {
                    grunt.log.error(result.stdout);
                }
                done(err);
                return;
            }

            grunt.log.ok();

            done();
        });

    });

};
<|endoftext|>


<commit_before>/*global NB:true NB$:true $:true rangy:true alert:true wgxpath:true jQuery:true getElementCSSSelector:true console:true */
(function(GLOB){

    if ("NB$" in window){
        var $ = NB$;
    }

    var $str        = "NB$" in window ? "NB$" : "jQuery";
    var cssApplier = null;
    var lastClicked = {set: [], clicked: null};

    var restore = function(loc) {
        var sel = rangy.getSelection();
        sel.restoreCharacterRanges(getElementsByXPath(document, loc.path1)[0],
                                   [{backward: loc.path2 === "true",
                                               characterRange: {
                                               start: loc.offset1,
                                               end: loc.offset2
                                           }
                                       }]);
        placeAnnotation(sel, loc);
    };

    var restoreBatch = function(object, callback) {
        var start = (new Date()).getTime();
        var current;

        for (var key in object) {
            current = (new Date()).getTime();
            if (current - start > 150) {
                window.setTimeout(restoreBatch, 10, object, callback);
                return;
            }

            restore(object[key]);
            delete object[key];
        }

        callback();
    };

    GLOB.html = {
        id: "docviewHtml5"
    };

    GLOB.html.init = function () {

        var countChildChars = function(_char, _this) {
            var char = _char;
            $(' > *', _this).each(function() {
                $(this).attr("data_char", char);
                countChildChars(char, this);
                char += ($(this).text()).length;
            });
        };

        countChildChars(0, $("body")[0]);

        $.concierge.addListeners(GLOB.html,{
                page: function(evt){
                   // _render();
                },
                note_hover: function(event){
                    $(".nb-comment-highlight[id_item="+event.value+"]").addClass("hovered");
                },
                note_out: function(event){
                    $(".nb-comment-highlight[id_item="+event.value+"]").removeClass("hovered");
                },
                visibility: function(event){
                    console.warn("TODO", event);
                },
                select_thread: function(event){
                    $(".nb-comment-highlight.selected").removeClass("selected");
                    $(".nb-comment-highlight[id_item="+event.value+"]").addClass("selected");
                    $("body").animate({scrollTop: $(".nb-comment-highlight[id_item="+event.value+"]").offset().top - $(window).height() / 4});
                }
            },
            GLOB.html.id);

        rangy.init();

        // Wrap elements with nb-comment-fresh which is then selected by jQuery and operated on properly;
        // the styled element must have an nb-comment-highlight class.
        cssApplier = rangy.createCssClassApplier("nb-comment-fresh", { normalize: true });

        // Initialize Highlighting Event
        $("#content").mouseup(function (event) {
                var sel = rangy.getSelection();

                if (sel.isCollapsed){
                    return;
                }

                if (sel.containsNode($(".nb_sidebar")[0], true)) {
                    return;
                }

                // must call before applyToRanges, otherwise sel is gone
                var element = event.target;

                if ($(element).hasClass("nb-comment-highlight")) {
                    element = ($(element).parents("*:not(.nb-comment-highlight)"))[0];
                }

                var range = sel.saveCharacterRanges(element);

                var target = getElementXPath(element);

                insertPlaceholderAnnotation(sel);

                $.concierge.trigger({
                    type: "new_thread",
                    value: {
                        html5range:{
                            path1: target,
                            path2: range[0].backward,
                            offset1: range[0].characterRange.start,
                            offset2: range[0].characterRange.end,
                            apparent_height: $(".nb-placeholder").parents("*[data_char]").attr("data_char") + range[0].characterRange.start
                        }
                    }
                });

            });

        GLOB.pers.store.register({
            update: function (action, payload, items_fieldname) {
                var key;

                if (items_fieldname === "draft") {
                    var draft;
                    for (draft in payload.diff) { break; }

                    if (action === "remove") {
                        $(".nb-comment-highlight.nb-placeholder[id_item=" + draft + "]").contents().unwrap();
                    } else if (action === "add") {
                        $(".nb-comment-highlight.nb-placeholder").attr("id_item", draft);
                    }

                }

                if (action === "remove" && items_fieldname === "location") {
                    for (key in payload.diff) {
                        $(".nb-comment-highlight[id_item=" + key + "]").contents().unwrap();
                    }
                }

                if (action === "add" && items_fieldname === "html5location") {
                    restoreBatch($.extend(true, {}, payload.diff), function(){ });
                }

        }}, {html5location: null, draft: null, location: null});

        // fix IE XPath implementation
        wgxpath.install();
    };

    // must be called only on inner-most element
    var hasConflicts = function (element) {
        return ($(element).parents(".nb-comment-highlight").length > 0);
    };

    // TODO: refactor such that there is more code re-use between placeAnnotation
    // on the one hand, and insert/activatePlaceholderAnnotation on the other.
    var placeAnnotation = function (selection, loc) {
        var uid = loc.id_location;

        // apply nb-comment-fresh to ranges
        cssApplier.applyToSelection(selection);
        selection.removeAllRanges();

        // jQuery Treatment
        $("span.nb-comment-fresh.nb-comment-highlight").removeClass("nb-comment-fresh").wrapInner('');
        $("span.nb-comment-fresh")
            .addClass("nb-comment-highlight")
            .removeClass("nb-comment-fresh")
            .attr("id_item", uid)
            .hover(
                function(){$.concierge.trigger({type:"note_hover", value: uid});},
                function(){$.concierge.trigger({type:"note_out", value: uid});})
            .click(
                function (event) {
                    if (!rangy.getSelection().isCollapsed){ return;}

                    if (hasConflicts(this)) {
                        var ids = [];
                        var id = 0;

                        ids.push($(this).attr("id_item"));

                        $(this).parents(".nb-comment-highlight").each(function () {
                                ids.push($(this).attr("id_item"));
                        });

                        if ($(lastClicked.set).not(ids).length === 0 && $(ids).not(lastClicked.set).length === 0) {
                            for (id = 0; id < ids.length; id++) {
                                if (ids[id] === lastClicked.clicked) { break; }
                            }
                            id = (id + 1) % ids.length;
                        }

                        $.concierge.trigger({type:"select_thread", value: ids[id]});
                        lastClicked = {set: ids, clicked: ids[id]};

                    } else {
                        $.concierge.trigger({type:"select_thread", value: uid});
                        lastClicked = {set: [uid], clicked: uid};
                    }
                    event.stopPropagation();
            });
    };

    var insertPlaceholderAnnotation = function (selection) {
        // apply nb-comment-fresh to ranges
        cssApplier.applyToSelection(selection);
        selection.removeAllRanges();

        // jQuery Treatment
        $("span.nb-comment-fresh.nb-comment-highlight").removeClass("nb-comment-fresh").wrapInner('');
        $("span.nb-comment-fresh")
            .addClass("nb-comment-highlight")
            .addClass("nb-placeholder")
            .removeClass("nb-comment-fresh");
    };

    GLOB.html.clearAnnotations = function () {
        $(".nb-comment-highlight").contents().unwrap();
    };

    var trim = function (text) {
        return text.replace(/^\s*|\s*$/g, "");
    };

    var trimLeft = function (text) {
        return text.replace(/^\s+/, "");
    };

    var trimRight = function (text) {
        return text.replace(/\s+$/, "");
    };

    // ************************************************************************************************
    // XPath

    /**
     * Gets an XPath for an element which describes its hierarchical location.
     */
    var getElementXPath = function (element) {
        if (element && element.id){
            return '//*[@id="' + element.id + '"]';
        }
        else{
            return getElementTreeXPath(element);
        }
    };

    var getElementTreeXPath = function (element) {
        var paths = [];

        // Use nodeName (instead of localName) so namespace prefix is included (if any).
        for (; element && element.nodeType === 1; element = element.parentNode) {
            var index = 0;
            for (var sibling = element.previousSibling; sibling; sibling = sibling.previousSibling) {
                // Ignore document type declaration.
                if (sibling.nodeType === Node.DOCUMENT_TYPE_NODE){
                    continue;
                }

                if (sibling.nodeName === element.nodeName){
                    ++index;
                }
            }

            var tagName = element.nodeName.toLowerCase();
            var pathIndex = (index ? "[" + (index + 1) + "]" : "");
            paths.splice(0, 0, tagName + pathIndex);
        }

        return paths.length ? "/" + paths.join("/") : null;
    };

    var getElementCSSPath = function (element) {
        var paths = [];

        for (; element && element.nodeType === 1; element = element.parentNode) {
            var selector = getElementCSSSelector(element);
            paths.splice(0, 0, selector);
        }

        return paths.length ? paths.join(" ") : null;
    };

    var cssToXPath = function (rule) {
        var regElement = /^([#.]?)([a-z0-9\\*_\-]*)((\|)([a-z0-9\\*_\-]*))?/i;
        var regAttr1 = /^\[([^\]]*)\]/i;
        var regAttr2 = /^\[\s*([^~=\s]+)\s*(~?=)\s*"([^"]+)"\s*\]/i; //";
    var regPseudo = /^:([a-z_\-])+/i;
    var regCombinator = /^(\s*[>+\s])?/i;
    var regComma = /^\s*,/i;

    var index = 1;
    var parts = ["//", "*"];
    var lastRule = null;

    while (rule.length && rule !== lastRule) {
        lastRule = rule;

        // Trim leading whitespace
        rule = trim(rule);
        if (!rule.length){
            break;}

        // Match the element identifier
        var m = regElement.exec(rule);
        if (m) {
            if (!m[1]) {
                // XXXjoe Namespace ignored for now
                if (m[5]){
                    parts[index] = m[5];}
                else{
                    parts[index] = m[2];}
            }
            else if (m[1] === '#'){
                parts.push("[@id='" + m[2] + "']");}
            else if (m[1] === '.'){
                parts.push("[contains(concat(' ',normalize-space(@class),' '), ' " + m[2] + " ')]");
            }
            rule = rule.substr(m[0].length);
        }

        // Match attribute selectors
        m = regAttr2.exec(rule);
        if (m) {
            if (m[2] === "~="){
                parts.push("[contains(@" + m[1] + ", '" + m[3] + "')]");}
            else{
                parts.push("[@" + m[1] + "='" + m[3] + "']");
            }
            rule = rule.substr(m[0].length);
        }
        else {
            m = regAttr1.exec(rule);
            if (m) {
                parts.push("[@" + m[1] + "]");
                rule = rule.substr(m[0].length);
            }
        }

        // Skip over pseudo-classes and pseudo-elements, which are of no use to us
        m = regPseudo.exec(rule);
        while (m) {
            rule = rule.substr(m[0].length);
            m = regPseudo.exec(rule);
        }

        // Match combinators
        m = regCombinator.exec(rule);
        if (m && m[0].length) {
            if (m[0].indexOf(">") !== -1){
                parts.push("/");}
            else if (m[0].indexOf("+") !== -1){
                parts.push("/following-sibling::");}
            else{
                parts.push("//");
            }
            index = parts.length;
            parts.push("*");
            rule = rule.substr(m[0].length);
        }

        m = regComma.exec(rule);
        if (m) {
            parts.push(" | ", "//", "*");
            index = parts.length - 1;
            rule = rule.substr(m[0].length);
        }
    }

    var xpath = parts.join("");
    return xpath;
    };

var getElementsBySelector = function (doc, css) {
    var xpath = cssToXPath(css);
    return getElementsByXPath(doc, xpath);
};

var getElementsByXPath = function (doc, xpath) {
    var nodes = [];

    try {
        var result = doc.evaluate(xpath, doc, null, XPathResult.ANY_TYPE, null);
        for (var item = result.iterateNext() ; item; item = result.iterateNext()){
            nodes.push(item);}
    }
    catch (exc) {
        // Invalid xpath expressions make their way here sometimes.  If that happens,
        // we still want to return an empty set without an exception.
    }

    return nodes;
};

var getRuleMatchingElements = function (rule, doc) {
    var css = rule.selectorText;
    var xpath = cssToXPath(css);
    return getElementsByXPath(doc, xpath);
};
        jQuery(function(){NB.html.init();});

})(NB);
<commit_msg>Remove placeholder annotation if the second draft is 'rejected'<commit_after>/*global NB:true NB$:true $:true rangy:true alert:true wgxpath:true jQuery:true getElementCSSSelector:true console:true */
(function(GLOB){

    if ("NB$" in window){
        var $ = NB$;
    }

    var $str        = "NB$" in window ? "NB$" : "jQuery";
    var cssApplier = null;
    var lastClicked = {set: [], clicked: null};

    var restore = function(loc) {
        var sel = rangy.getSelection();
        sel.restoreCharacterRanges(getElementsByXPath(document, loc.path1)[0],
                                   [{backward: loc.path2 === "true",
                                               characterRange: {
                                               start: loc.offset1,
                                               end: loc.offset2
                                           }
                                       }]);
        placeAnnotation(sel, loc);
    };

    var restoreBatch = function(object, callback) {
        var start = (new Date()).getTime();
        var current;

        for (var key in object) {
            current = (new Date()).getTime();
            if (current - start > 150) {
                window.setTimeout(restoreBatch, 10, object, callback);
                return;
            }

            restore(object[key]);
            delete object[key];
        }

        callback();
    };

    GLOB.html = {
        id: "docviewHtml5"
    };

    GLOB.html.init = function () {

        var countChildChars = function(_char, _this) {
            var char = _char;
            $(' > *', _this).each(function() {
                $(this).attr("data_char", char);
                countChildChars(char, this);
                char += ($(this).text()).length;
            });
        };

        countChildChars(0, $("body")[0]);

        $.concierge.addListeners(GLOB.html,{
                page: function(evt){
                   // _render();
                },
                note_hover: function(event){
                    $(".nb-comment-highlight[id_item="+event.value+"]").addClass("hovered");
                },
                note_out: function(event){
                    $(".nb-comment-highlight[id_item="+event.value+"]").removeClass("hovered");
                },
                visibility: function(event){
                    console.warn("TODO", event);
                },
                select_thread: function(event){
                    $(".nb-comment-highlight.selected").removeClass("selected");
                    $(".nb-comment-highlight[id_item="+event.value+"]").addClass("selected");
                    $("body").animate({scrollTop: $(".nb-comment-highlight[id_item="+event.value+"]").offset().top - $(window).height() / 4});
                }
            },
            GLOB.html.id);

        rangy.init();

        // Wrap elements with nb-comment-fresh which is then selected by jQuery and operated on properly;
        // the styled element must have an nb-comment-highlight class.
        cssApplier = rangy.createCssClassApplier("nb-comment-fresh", { normalize: true });

        // Initialize Highlighting Event
        $("#content").mouseup(function (event) {
                var sel = rangy.getSelection();

                if (sel.isCollapsed){
                    return;
                }

                if (sel.containsNode($(".nb_sidebar")[0], true)) {
                    return;
                }

                // must call before applyToRanges, otherwise sel is gone
                var element = event.target;

                if ($(element).hasClass("nb-comment-highlight")) {
                    element = ($(element).parents("*:not(.nb-comment-highlight)"))[0];
                }

                var range = sel.saveCharacterRanges(element);

                var target = getElementXPath(element);

                insertPlaceholderAnnotation(sel);

                $.concierge.trigger({
                    type: "new_thread",
                    value: {
                        html5range:{
                            path1: target,
                            path2: range[0].backward,
                            offset1: range[0].characterRange.start,
                            offset2: range[0].characterRange.end,
                            apparent_height: $(".nb-placeholder").parents("*[data_char]").attr("data_char") + range[0].characterRange.start
                        }
                    }
                });

            });

        GLOB.pers.store.register({
            update: function (action, payload, items_fieldname) {
                var key;

                if (items_fieldname === "draft") {
                    var draft;
                    for (draft in payload.diff) { break; }

                    if (action === "remove") {
                        $(".nb-comment-highlight.nb-placeholder[id_item=" + draft + "]").contents().unwrap();
                    } else if (action === "add") {
                        $(".nb-comment-highlight.nb-placeholder").attr("id_item", draft);
                    }

                }

                if (action === "remove" && items_fieldname === "location") {
                    for (key in payload.diff) {
                        $(".nb-comment-highlight[id_item=" + key + "]").contents().unwrap();
                    }
                }

                if (action === "add" && items_fieldname === "html5location") {
                    restoreBatch($.extend(true, {}, payload.diff), function(){ });
                }

        }}, {html5location: null, draft: null, location: null});

        // fix IE XPath implementation
        wgxpath.install();
    };

    // must be called only on inner-most element
    var hasConflicts = function (element) {
        return ($(element).parents(".nb-comment-highlight").length > 0);
    };

    // TODO: refactor such that there is more code re-use between placeAnnotation
    // on the one hand, and insert/activatePlaceholderAnnotation on the other.
    var placeAnnotation = function (selection, loc) {
        var uid = loc.id_location;

        // apply nb-comment-fresh to ranges
        cssApplier.applyToSelection(selection);
        selection.removeAllRanges();

        // jQuery Treatment
        $("span.nb-comment-fresh.nb-comment-highlight").removeClass("nb-comment-fresh").wrapInner('');
        $("span.nb-comment-fresh")
            .addClass("nb-comment-highlight")
            .removeClass("nb-comment-fresh")
            .attr("id_item", uid)
            .hover(
                function(){$.concierge.trigger({type:"note_hover", value: uid});},
                function(){$.concierge.trigger({type:"note_out", value: uid});})
            .click(
                function (event) {
                    if (!rangy.getSelection().isCollapsed){ return;}

                    if (hasConflicts(this)) {
                        var ids = [];
                        var id = 0;

                        ids.push($(this).attr("id_item"));

                        $(this).parents(".nb-comment-highlight").each(function () {
                                ids.push($(this).attr("id_item"));
                        });

                        if ($(lastClicked.set).not(ids).length === 0 && $(ids).not(lastClicked.set).length === 0) {
                            for (id = 0; id < ids.length; id++) {
                                if (ids[id] === lastClicked.clicked) { break; }
                            }
                            id = (id + 1) % ids.length;
                        }

                        $.concierge.trigger({type:"select_thread", value: ids[id]});
                        lastClicked = {set: ids, clicked: ids[id]};

                    } else {
                        $.concierge.trigger({type:"select_thread", value: uid});
                        lastClicked = {set: [uid], clicked: uid};
                    }
                    event.stopPropagation();
            });
    };

    var insertPlaceholderAnnotation = function (selection) {
        // apply nb-comment-fresh to ranges
        cssApplier.applyToSelection(selection);
        selection.removeAllRanges();

        // jQuery Treatment
        $("span.nb-comment-fresh.nb-comment-highlight").removeClass("nb-comment-fresh").wrapInner('');
        $("span.nb-comment-fresh")
            .addClass("nb-comment-highlight")
            .addClass("nb-placeholder")
            .removeClass("nb-comment-fresh")
            .attr("id_item", 0);

        // remove placeholder comment after 0.25 seconds if we do not receive a "draft created" event (i.e. the concierge
        // did not allow the creation of the draft). We check this by seeing if id_item is still 0.
        window.setTimeout(function() {
            $(".nb-placeholder[id_item=0]").contents().unwrap();
        }, 250);
    };

    GLOB.html.clearAnnotations = function () {
        $(".nb-comment-highlight").contents().unwrap();
    };

    var trim = function (text) {
        return text.replace(/^\s*|\s*$/g, "");
    };

    var trimLeft = function (text) {
        return text.replace(/^\s+/, "");
    };

    var trimRight = function (text) {
        return text.replace(/\s+$/, "");
    };

    // ************************************************************************************************
    // XPath

    /**
     * Gets an XPath for an element which describes its hierarchical location.
     */
    var getElementXPath = function (element) {
        if (element && element.id){
            return '//*[@id="' + element.id + '"]';
        }
        else{
            return getElementTreeXPath(element);
        }
    };

    var getElementTreeXPath = function (element) {
        var paths = [];

        // Use nodeName (instead of localName) so namespace prefix is included (if any).
        for (; element && element.nodeType === 1; element = element.parentNode) {
            var index = 0;
            for (var sibling = element.previousSibling; sibling; sibling = sibling.previousSibling) {
                // Ignore document type declaration.
                if (sibling.nodeType === Node.DOCUMENT_TYPE_NODE){
                    continue;
                }

                if (sibling.nodeName === element.nodeName){
                    ++index;
                }
            }

            var tagName = element.nodeName.toLowerCase();
            var pathIndex = (index ? "[" + (index + 1) + "]" : "");
            paths.splice(0, 0, tagName + pathIndex);
        }

        return paths.length ? "/" + paths.join("/") : null;
    };

    var getElementCSSPath = function (element) {
        var paths = [];

        for (; element && element.nodeType === 1; element = element.parentNode) {
            var selector = getElementCSSSelector(element);
            paths.splice(0, 0, selector);
        }

        return paths.length ? paths.join(" ") : null;
    };

    var cssToXPath = function (rule) {
        var regElement = /^([#.]?)([a-z0-9\\*_\-]*)((\|)([a-z0-9\\*_\-]*))?/i;
        var regAttr1 = /^\[([^\]]*)\]/i;
        var regAttr2 = /^\[\s*([^~=\s]+)\s*(~?=)\s*"([^"]+)"\s*\]/i; //";
    var regPseudo = /^:([a-z_\-])+/i;
    var regCombinator = /^(\s*[>+\s])?/i;
    var regComma = /^\s*,/i;

    var index = 1;
    var parts = ["//", "*"];
    var lastRule = null;

    while (rule.length && rule !== lastRule) {
        lastRule = rule;

        // Trim leading whitespace
        rule = trim(rule);
        if (!rule.length){
            break;}

        // Match the element identifier
        var m = regElement.exec(rule);
        if (m) {
            if (!m[1]) {
                // XXXjoe Namespace ignored for now
                if (m[5]){
                    parts[index] = m[5];}
                else{
                    parts[index] = m[2];}
            }
            else if (m[1] === '#'){
                parts.push("[@id='" + m[2] + "']");}
            else if (m[1] === '.'){
                parts.push("[contains(concat(' ',normalize-space(@class),' '), ' " + m[2] + " ')]");
            }
            rule = rule.substr(m[0].length);
        }

        // Match attribute selectors
        m = regAttr2.exec(rule);
        if (m) {
            if (m[2] === "~="){
                parts.push("[contains(@" + m[1] + ", '" + m[3] + "')]");}
            else{
                parts.push("[@" + m[1] + "='" + m[3] + "']");
            }
            rule = rule.substr(m[0].length);
        }
        else {
            m = regAttr1.exec(rule);
            if (m) {
                parts.push("[@" + m[1] + "]");
                rule = rule.substr(m[0].length);
            }
        }

        // Skip over pseudo-classes and pseudo-elements, which are of no use to us
        m = regPseudo.exec(rule);
        while (m) {
            rule = rule.substr(m[0].length);
            m = regPseudo.exec(rule);
        }

        // Match combinators
        m = regCombinator.exec(rule);
        if (m && m[0].length) {
            if (m[0].indexOf(">") !== -1){
                parts.push("/");}
            else if (m[0].indexOf("+") !== -1){
                parts.push("/following-sibling::");}
            else{
                parts.push("//");
            }
            index = parts.length;
            parts.push("*");
            rule = rule.substr(m[0].length);
        }

        m = regComma.exec(rule);
        if (m) {
            parts.push(" | ", "//", "*");
            index = parts.length - 1;
            rule = rule.substr(m[0].length);
        }
    }

    var xpath = parts.join("");
    return xpath;
    };

var getElementsBySelector = function (doc, css) {
    var xpath = cssToXPath(css);
    return getElementsByXPath(doc, xpath);
};

var getElementsByXPath = function (doc, xpath) {
    var nodes = [];

    try {
        var result = doc.evaluate(xpath, doc, null, XPathResult.ANY_TYPE, null);
        for (var item = result.iterateNext() ; item; item = result.iterateNext()){
            nodes.push(item);}
    }
    catch (exc) {
        // Invalid xpath expressions make their way here sometimes.  If that happens,
        // we still want to return an empty set without an exception.
    }

    return nodes;
};

var getRuleMatchingElements = function (rule, doc) {
    var css = rule.selectorText;
    var xpath = cssToXPath(css);
    return getElementsByXPath(doc, xpath);
};
        jQuery(function(){NB.html.init();});

})(NB);
<|endoftext|>


<commit_before>
// Dependencies
var vows = require('vows'),
    assert = require('assert'),
    FileSystem = require('fs');

/**
 *
Unit tests for the node-config library.  To run type:

 *
npm test

 *
Or, in a project that uses node-config:

 *
npm test config

 *
 * @class ConfigTest
 */

var CONFIG, override;
vows.describe('Test suite for node-config')
.addBatch({
  'Library initialization': {
    topic : function () {
      // Change the configuration directory for testing
      process.env.NODE_CONFIG_DIR = __dirname + '/config';

      // Hardcode $NODE_ENV=test for testing
      process.env.NODE_ENV='test';

      // Test for multi-instance applications
      process.env.NODE_APP_INSTANCE='3';

      // Test $NODE_CONFIG environment and --NODE_CONFIG command line parameter
      process.env.NODE_CONFIG='{"EnvOverride":{"parm3":"overridden from $NODE_CONFIG","parm4":100}}';
      process.argv.push('--NODE_CONFIG={"EnvOverride":{"parm5":"overridden from --NODE_CONFIG","parm6":101}}');

      // Test Environment Variable Substitution
      override = 'CUSTOM VALUE FROM JSON ENV MAPPING';
      process.env.CUSTOM_JSON_ENVIRONMENT_VAR = override;

      CONFIG = requireUncached('../lib/config');

      return CONFIG;

    },
    'Config library is available': function() {
      assert.isObject(CONFIG);
    },
    'Config extensions are included with the library': function() {
      assert.isFunction(CONFIG.util.cloneDeep);
    }
  },
})
.addBatch({
  'Configuration file Tests': {
    'Loading configurations from a JS module is correct': function() {
      assert.equal(CONFIG.Customers.dbHost, 'base');
      assert.equal(CONFIG.TestModule.parm1, 'value1');
    },

    'Loading configurations from a JSON file is correct': function() {
      assert.equal(CONFIG.AnotherModule.parm1, 'value1');
    },

    'Loading configurations from a .yaml YAML file is correct': function() {
      assert.equal(CONFIG.AnotherModule.parm2, 'value2');
    },

    'Loading configurations from a .yml YAML file is correct': function() {
      assert.equal(CONFIG.AnotherModule.parm2yml, 'value2yml');
    },

    'Loading configurations from a Coffee-Script file is correct': function() {
      assert.equal(CONFIG.AnotherModule.parm3, 'value3');
    },

    'Loading configurations from a CSON file is correct': function() {
      assert.equal(CONFIG.AnotherModule.parm4, 'value4');
    },

    'Loading configurations from a .properties file is correct': function() {
      assert.equal(CONFIG.AnotherModule.parm5, 'value5');
    },

    'Loading configurations from a JSON5 file is correct': function() {
      assert.equal(CONFIG.AnotherModule.parm6, 'value6');
    },

    'Loading configurations from a TOML file is correct': function() {
      assert.equal(CONFIG.AnotherModule.parm7, 'value7');
    },

    'Loading configurations from a Hjson file is correct': function() {
      assert.equal(CONFIG.AnotherModule.parm8, 'value8');
    },

    'Loading configurations from an environment file is correct': function() {
      assert.equal(CONFIG.Customers.dbPort, '5999');
    },

    'Loading configurations from the local file is correct': function() {
      assert.equal(CONFIG.Customers.dbPassword, 'real password');
    },

    'Loading configurations from the local environment file is correct': function() {
      assert.equal(CONFIG.Customers.dbPassword2, 'another password');
      assert.deepEqual(CONFIG.Customers.lang, ['en','de','es']);
    }

  },

  'Immutability': {
    'Correct mute setup var': function () {
      assert.equal(CONFIG.MuteThis, 'hello');
    },

    'Mutation sticks': function () {
      CONFIG.MuteThis = 'world';
      assert.equal(CONFIG.MuteThis, 'world');
    },

    'No mutation after the first get()': function () {
      assert.equal(CONFIG.get('MuteThis'), 'world');
      CONFIG.MuteThis = 'backToHello';
      assert.equal(CONFIG.MuteThis, 'world');
    }
  },

  'Configurations from the $NODE_CONFIG environment variable': {
    'Configuration can come from the $NODE_CONFIG environment': function() {
      assert.equal(CONFIG.EnvOverride.parm3, 'overridden from $NODE_CONFIG');
    },

    'Type correct configurations from $NODE_CONFIG': function() {
      assert.equal(CONFIG.EnvOverride.parm4, 100);
    }

  },

  'Configurations from the --NODE_CONFIG command line': {
    'Configuration can come from the --NODE_CONFIG command line argument': function() {
      assert.equal(CONFIG.EnvOverride.parm5, 'overridden from --NODE_CONFIG');
    },

    'Type correct configurations from --NODE_CONFIG': function() {
      assert.equal(CONFIG.EnvOverride.parm6, 101);
    }

  },

  'Configurations from custom environment variables': {
    // only testing the `custom-environment-variables.json` now
    // NOT testing unset environment variable because of module caching (CONFIG would have to be recreated
    // NOT testing absence of `custom-environment-variables.json` because current tests don't mess with the filesystem
    'Configuration can come from an environment variable mapped in custom_environment_variables.json': function () {
      assert.equal(CONFIG.get('customEnvironmentVariables.mappedBy.json'), override);
    }
  },

 'Assuring a configuration property can be hidden': {
    topic: function() {
      var object = {
        item1: 23,
        subObject: {
      	  item2: "hello"
        }
      };
      return object;
    },

    'The makeHidden() method is available': function() {
      assert.isFunction(CONFIG.util.makeHidden);
    },

    'The test object (before hiding) is correct': function(object) {
      assert.isTrue(JSON.stringify(object) == '{"item1":23,"subObject":{"item2":"hello"}}');
    },

    'The test object (after hiding) is correct': function(object) {
      CONFIG.util.makeHidden(object, 'item1');
      assert.isTrue(JSON.stringify(object) == '{"subObject":{"item2":"hello"}}');
    },

    'The hidden property is readable, and has not changed': function(object) {
      assert.isTrue(JSON.stringify(object) == '{"subObject":{"item2":"hello"}}');
      assert.isTrue(object.item1 == 23);
    }

  },

  'Assuring a configuration property can be made immutable': {
    topic: function() {

      CONFIG.util.makeImmutable(CONFIG.TestModule, 'parm1');
      CONFIG.TestModule.parm1 = "setToThis";
      return CONFIG.TestModule.parm1;
    },

    'The makeImmutable() method is available': function() {
      assert.isFunction(CONFIG.util.makeImmutable);
    },

    'Correctly unable to change an immutable configuration': function(value) {
      assert.isTrue(value != "setToThis");
    },

    'Left the original value intact after attempting the change': function(value) {
      assert.equal(value, "value1");
    }
  },

  'get() tests': {
    'The function exists': function() {
      assert.isFunction(CONFIG.get);
    },
    'A top level item is returned': function() {
      assert.isTrue(typeof CONFIG.get('TestModule') === 'object');
    },
    'A sub level item is returned': function() {
      assert.equal(CONFIG.get('Customers.dbHost'), 'base');
    },
    'get is attached deeply': function() {
      assert.equal(CONFIG.Customers.get('dbHost'), 'base');
    },
    'An extended property accessor remains a getter': function() {
      assert.equal(CONFIG.get('customerDbPort'), '5999');
    },
    'A cloned property accessor remains a getter': function() {
      assert.equal(CONFIG.Customers.get('dbString'), 'override_from_runtime_json:5999');
    },
    'A cloned property accessor is made immutable': function() {
      var random1 = CONFIG.Customers.get('random'),
          random2 = CONFIG.Customers.get('random');

      assert.equal(random1, random2);
    },
    'A proper exception is thrown on mis-spellings': function() {
      assert.throws(
        function () { CONFIG.get('mis.spelled'); },
        /Configuration property "mis.spelled" is not defined/
      );
    },
    'An exception is thrown on non-objects': function() {
      assert.throws(
          function () { CONFIG.get('Testmodule.misspelled'); },
          /Configuration property "Testmodule.misspelled" is not defined/
      );
    },
    'get(undefined) throws an exception': function() {
      assert.throws(
          function () { CONFIG.get(undefined); },
          /Calling config.get with null or undefined argument/
      );
    },
    'get(null) throws an exception': function() {
      assert.throws(
          function () { CONFIG.get(null); },
          /Calling config.get with null or undefined argument/
      );
    },
    "get('') throws an exception": function() {
      assert.throws(
          function () { CONFIG.get(''); },
          /Configuration property "" is not defined/
      );
    },
  },

  'has() tests': {
    'The function exists': function() {
      assert.isFunction(CONFIG.has);
    },
    'A top level item can be tested': function() {
      assert.isTrue(CONFIG.has('TestModule'));
    },
    'A sub level item can be tested': function() {
      assert.isTrue(CONFIG.has('Customers.dbHost'));
    },
    'has is attached deeply': function() {
      assert.isTrue(CONFIG.Customers.has('dbHost'));
    },
    'Correctly identifies not having element': function() {
      assert.isTrue(!CONFIG.Customers.has('dbHosx'));
    },
    'Correctly identifies not having element (deep)': function() {
      assert.isTrue(!CONFIG.has('Customers.dbHosx'));
    },
    'has(undefined) returns false': function() {
      assert.isFalse(CONFIG.has(undefined));
    },
    "has(null) returns false": function() {
      assert.isFalse(CONFIG.has(null));
    },
    "has('') returns false": function() {
      assert.isFalse(CONFIG.has(''));
    },
  },

  'Configuration for module developers': {
    topic: function() {

      // Set some parameters for the test module
      return CONFIG.util.setModuleDefaults("TestModule", {
        parm1: 1000, parm2: 2000, parm3: 3000
      });
    },

    'The setModuleDefaults() method is available': function() {
      assert.isFunction(CONFIG.util.setModuleDefaults);
    },

    'The module config is in the CONFIG object': function(moduleConfig) {
      assert.isObject(CONFIG.TestModule);
      assert.deepEqual(CONFIG.TestModule, moduleConfig);
    },

    'Local configurations are mixed in': function(moduleConfig) {
      assert.equal(moduleConfig.parm1, "value1");
    },

    'Defaults remain intact unless overridden': function(moduleConfig) {
      assert.equal(moduleConfig.parm2, 2000);
    }
  },
})
.export(module);

//
// Because require'ing config creates and caches a global singleton,
// We have to invalidate the cache to build new object based on the environment variables above
function requireUncached(module){
   delete require.cache[require.resolve(module)];
   return require(module);
}

<commit_msg>Add test-case for #243<commit_after>
// Dependencies
var vows = require('vows'),
    assert = require('assert'),
    FileSystem = require('fs');

/**
 *
Unit tests for the node-config library.  To run type:

 *
npm test

 *
Or, in a project that uses node-config:

 *
npm test config

 *
 * @class ConfigTest
 */

var CONFIG, override;
vows.describe('Test suite for node-config')
.addBatch({
  'Library initialization': {
    topic : function () {
      // Change the configuration directory for testing
      process.env.NODE_CONFIG_DIR = __dirname + '/config';

      // Hardcode $NODE_ENV=test for testing
      process.env.NODE_ENV='test';

      // Test for multi-instance applications
      process.env.NODE_APP_INSTANCE='3';

      // Test $NODE_CONFIG environment and --NODE_CONFIG command line parameter
      process.env.NODE_CONFIG='{"EnvOverride":{"parm3":"overridden from $NODE_CONFIG","parm4":100}}';
      process.argv.push('--NODE_CONFIG={"EnvOverride":{"parm5":"overridden from --NODE_CONFIG","parm6":101}}');

      // Test Environment Variable Substitution
      override = 'CUSTOM VALUE FROM JSON ENV MAPPING';
      process.env.CUSTOM_JSON_ENVIRONMENT_VAR = override;

      CONFIG = requireUncached('../lib/config');

      return CONFIG;

    },
    'Config library is available': function() {
      assert.isObject(CONFIG);
    },
    'Config extensions are included with the library': function() {
      assert.isFunction(CONFIG.util.cloneDeep);
    }
  },
})
.addBatch({
  'Configuration file Tests': {
    'Loading configurations from a JS module is correct': function() {
      assert.equal(CONFIG.Customers.dbHost, 'base');
      assert.equal(CONFIG.TestModule.parm1, 'value1');
    },

    'Loading configurations from a JSON file is correct': function() {
      assert.equal(CONFIG.AnotherModule.parm1, 'value1');
    },

    'Loading configurations from a .yaml YAML file is correct': function() {
      assert.equal(CONFIG.AnotherModule.parm2, 'value2');
    },

    'Loading configurations from a .yml YAML file is correct': function() {
      assert.equal(CONFIG.AnotherModule.parm2yml, 'value2yml');
    },

    'Loading configurations from a Coffee-Script file is correct': function() {
      assert.equal(CONFIG.AnotherModule.parm3, 'value3');
    },

    'Loading configurations from a CSON file is correct': function() {
      assert.equal(CONFIG.AnotherModule.parm4, 'value4');
    },

    'Loading configurations from a .properties file is correct': function() {
      assert.equal(CONFIG.AnotherModule.parm5, 'value5');
    },

    'Loading configurations from a JSON5 file is correct': function() {
      assert.equal(CONFIG.AnotherModule.parm6, 'value6');
    },

    'Loading configurations from a TOML file is correct': function() {
      assert.equal(CONFIG.AnotherModule.parm7, 'value7');
    },

    'Loading configurations from a Hjson file is correct': function() {
      assert.equal(CONFIG.AnotherModule.parm8, 'value8');
    },

    'Loading configurations from an environment file is correct': function() {
      assert.equal(CONFIG.Customers.dbPort, '5999');
    },

    'Loading configurations from the local file is correct': function() {
      assert.equal(CONFIG.Customers.dbPassword, 'real password');
    },

    'Loading configurations from the local environment file is correct': function() {
      assert.equal(CONFIG.Customers.dbPassword2, 'another password');
      assert.deepEqual(CONFIG.Customers.lang, ['en','de','es']);
    }

  },

  'Immutability': {
    'Correct mute setup var': function () {
      assert.equal(CONFIG.MuteThis, 'hello');
    },

    'Mutation sticks': function () {
      CONFIG.MuteThis = 'world';
      assert.equal(CONFIG.MuteThis, 'world');
    },

    'No mutation after the first get()': function () {
      assert.equal(CONFIG.get('MuteThis'), 'world');
      CONFIG.MuteThis = 'backToHello';
      assert.equal(CONFIG.MuteThis, 'world');
    }
  },

  'Configurations from the $NODE_CONFIG environment variable': {
    'Configuration can come from the $NODE_CONFIG environment': function() {
      assert.equal(CONFIG.EnvOverride.parm3, 'overridden from $NODE_CONFIG');
    },

    'Type correct configurations from $NODE_CONFIG': function() {
      assert.equal(CONFIG.EnvOverride.parm4, 100);
    }

  },

  'Configurations from the --NODE_CONFIG command line': {
    'Configuration can come from the --NODE_CONFIG command line argument': function() {
      assert.equal(CONFIG.EnvOverride.parm5, 'overridden from --NODE_CONFIG');
    },

    'Type correct configurations from --NODE_CONFIG': function() {
      assert.equal(CONFIG.EnvOverride.parm6, 101);
    }

  },

  'Configurations from custom environment variables': {
    // only testing the `custom-environment-variables.json` now
    // NOT testing unset environment variable because of module caching (CONFIG would have to be recreated
    // NOT testing absence of `custom-environment-variables.json` because current tests don't mess with the filesystem
    'Configuration can come from an environment variable mapped in custom_environment_variables.json': function () {
      assert.equal(CONFIG.get('customEnvironmentVariables.mappedBy.json'), override);
    }
  },

 'Assuring a configuration property can be hidden': {
    topic: function() {
      var object = {
        item1: 23,
        subObject: {
      	  item2: "hello"
        }
      };
      return object;
    },

    'The makeHidden() method is available': function() {
      assert.isFunction(CONFIG.util.makeHidden);
    },

    'The test object (before hiding) is correct': function(object) {
      assert.isTrue(JSON.stringify(object) == '{"item1":23,"subObject":{"item2":"hello"}}');
    },

    'The test object (after hiding) is correct': function(object) {
      CONFIG.util.makeHidden(object, 'item1');
      assert.isTrue(JSON.stringify(object) == '{"subObject":{"item2":"hello"}}');
    },

    'The hidden property is readable, and has not changed': function(object) {
      assert.isTrue(JSON.stringify(object) == '{"subObject":{"item2":"hello"}}');
      assert.isTrue(object.item1 == 23);
    }

  },

  'Assuring a configuration property can be made immutable': {
    topic: function() {

      CONFIG.util.makeImmutable(CONFIG.TestModule, 'parm1');
      CONFIG.TestModule.parm1 = "setToThis";
      return CONFIG.TestModule.parm1;
    },

    'The makeImmutable() method is available': function() {
      assert.isFunction(CONFIG.util.makeImmutable);
    },

    'Correctly unable to change an immutable configuration': function(value) {
      assert.isTrue(value != "setToThis");
    },

    'Left the original value intact after attempting the change': function(value) {
      assert.equal(value, "value1");
    }
  },

  'get() tests': {
    'The function exists': function() {
      assert.isFunction(CONFIG.get);
    },
    'A top level item is returned': function() {
      assert.isTrue(typeof CONFIG.get('TestModule') === 'object');
    },
    'A sub level item is returned': function() {
      assert.equal(CONFIG.get('Customers.dbHost'), 'base');
    },
    'get is attached deeply': function() {
      assert.equal(CONFIG.Customers.get('dbHost'), 'base');
    },
    'An extended property accessor remains a getter': function() {
      assert.equal(CONFIG.get('customerDbPort'), '5999');
    },
    'A cloned property accessor remains a getter': function() {
      assert.equal(CONFIG.Customers.get('dbString'), 'override_from_runtime_json:5999');
    },
    'A cloned property accessor is made immutable': function() {
      var random1 = CONFIG.Customers.get('random'),
          random2 = CONFIG.Customers.get('random');

      assert.equal(random1, random2);
    },
    'A proper exception is thrown on mis-spellings': function() {
      assert.throws(
        function () { CONFIG.get('mis.spelled'); },
        /Configuration property "mis.spelled" is not defined/
      );
    },
    'An exception is thrown on non-objects': function() {
      assert.throws(
          function () { CONFIG.get('Testmodule.misspelled'); },
          /Configuration property "Testmodule.misspelled" is not defined/
      );
    },
    'get(undefined) throws an exception': function() {
      assert.throws(
          function () { CONFIG.get(undefined); },
          /Calling config.get with null or undefined argument/
      );
    },
    'get(null) throws an exception': function() {
      assert.throws(
          function () { CONFIG.get(null); },
          /Calling config.get with null or undefined argument/
      );
    },
    "get('') throws an exception": function() {
      assert.throws(
          function () { CONFIG.get(''); },
          /Configuration property "" is not defined/
      );
    },
  },

  'has() tests': {
    'The function exists': function() {
      assert.isFunction(CONFIG.has);
    },
    'A top level item can be tested': function() {
      assert.isTrue(CONFIG.has('TestModule'));
    },
    'A sub level item can be tested': function() {
      assert.isTrue(CONFIG.has('Customers.dbHost'));
    },
    'has is attached deeply': function() {
      assert.isTrue(CONFIG.Customers.has('dbHost'));
    },
    'Correctly identifies not having element': function() {
      assert.isTrue(!CONFIG.Customers.has('dbHosx'));
    },
    'Correctly identifies not having element (deep)': function() {
      assert.isTrue(!CONFIG.has('Customers.dbHosx'));
    },
    'has(undefined) returns false': function() {
      assert.isFalse(CONFIG.has(undefined));
    },
    "has(null) returns false": function() {
      assert.isFalse(CONFIG.has(null));
    },
    "has('') returns false": function() {
      assert.isFalse(CONFIG.has(''));
    },
  },

  'Configuration for module developers': {
    topic: function() {

      // Set some parameters for the test module
      return CONFIG.util.setModuleDefaults("TestModule", {
        parm1: 1000, parm2: 2000, parm3: 3000,
        nested: {
          param4: 4000,
          param5: 5000
        }
      });
    },

    'The setModuleDefaults() method is available': function() {
      assert.isFunction(CONFIG.util.setModuleDefaults);
    },

    'The module config is in the CONFIG object': function(moduleConfig) {
      assert.isObject(CONFIG.TestModule);
      assert.deepEqual(CONFIG.TestModule, moduleConfig);
    },

    'Local configurations are mixed in': function(moduleConfig) {
      assert.equal(moduleConfig.parm1, "value1");
    },

    'Defaults remain intact unless overridden': function(moduleConfig) {
      assert.equal(moduleConfig.parm2, 2000);
    },

    'Prototypes are applied by setModuleDefaults even if no previous config exists for the module': function() {
      var BKTestModuleDefaults = {
        parm1: 1000, parm2: 2000, parm3: 3000,
        nested: {
          param4: 4000,
          param5: 5000
        }
      };

      CONFIG.util.setModuleDefaults("BKTestModule", BKTestModuleDefaults);

      var testModuleConfig = CONFIG.get('BKTestModule');

      assert.deepEqual(BKTestModuleDefaults.nested, testModuleConfig.get('nested'));
    }
  },
})
.export(module);

//
// Because require'ing config creates and caches a global singleton,
// We have to invalidate the cache to build new object based on the environment variables above
function requireUncached(module){
   delete require.cache[require.resolve(module)];
   return require(module);
}

<|endoftext|>


<commit_before>'use strict';

var _ = require('underscore');

var settings = {};

settings.cookieSecret = process.env.COOKIE_SECRET;

settings.movesClientId = '9uuys57PvQY7G2rFn2uBqPi6uoFabcTT';
settings.movesSecret = process.env.MOVES_SECRET;

settings.movesToken = process.env.MOVES_TOKEN; // if a token is in the environment for demonstration purposes

settings.movesAPIUrl = 'https://api.moves-app.com/api/v1';
settings.movesAuthUrl = 'https://api.moves-app.com/oauth/v1';

module.exports = settings;
<commit_msg>Move client id to environment variable<commit_after>'use strict';

var _ = require('underscore');

var settings = {};

settings.cookieSecret = process.env.COOKIE_SECRET;

settings.movesClientId = process.env.MOVES_CLIENT;
settings.movesSecret = process.env.MOVES_SECRET;

settings.movesToken = process.env.MOVES_TOKEN; // if a token is in the environment for demonstration purposes

settings.movesAPIUrl = 'https://api.moves-app.com/api/v1';
settings.movesAuthUrl = 'https://api.moves-app.com/oauth/v1';

module.exports = settings;
<|endoftext|>


<commit_before>/*
  Calculate milk yield and solids adjusted for parity requiring estimates for parameters of Wood's lactation curve.

  REFERENCES

  Wood, P.D.P. 1980.
  Breed variations in the shape of the lactation curve of cattle and their implications for efficiency,
  Animal Science, Volume 31, Issue 02, 1980, pp 133-141

  Tyrrell, H.F. et al. 1965.
  Prediction of the Energy Value of Cow's Milk,
  Journal of Dairy Science, Volume 48, Issue 9, 1215 - 1223

  DLG-Information 1/2006.
  Schätzung der Futteraufnahme bei der Milchkuh,
  DLG-Arbeitskreis Futter und Fütterung

  LICENSE

  Copyright 2014 Jan Vaillant   <jan.vaillant@zalf.de>
  Copyright 2014 Lisa Baldinger <lisa.baldinger@boku.ac.at>

  Distributed under the MIT License. See accompanying file LICENSE or copy at http://opensource.org/licenses/MIT

  Any publication for which this file or a derived work is used must include an a reference to the article:

  Baldinger, L., Vaillant, J., Zollitsch, W., Rinne, M. (2014) SOLID-DSS - Eine online-Anwendung zur verbesserten
  Abstimmung von Grundfutterangebot und -bedarf auf biologisch wirtschaftenden Low Input Milchviehbetrieben.
  In: Wiesinger, K., Cais, K., Obermaier, S. (eds), Angewandte Forschung und Beratung für den ökologischen Landbau in
  Bayern: Öko-Landbau-Tag 2014 am 9. April 2014 in Triesdorf; Tagungsband. LfL, Freising-Weihenstephan, pp. 19-22.
*/

var dairy = dairy || {};

dairy.milk = (function () {

var pow = Math.pow
  , exp = Math.exp
  ;

function is_null_or_undefined (x) {
  return x === null || x === undefined;
}

/*
  DLG (1/2006), Tabelle 7

  Typical lactation milk yield data per yield level [day, milk yield]. May be used to estimate Wood's lactation curve
  parameters (e.g. with lmfit).
*/
var data = {
  '6500': [
    [20 , 29],
    [40 , 30],
    [60 , 28],
    [100, 25],
    [150, 22],
    [200, 19],
    [250, 15],
    [300, 13],
    [350, 12]
  ],
  '7500': [
    [20 , 32],
    [40 , 33],
    [60 , 32],
    [100, 29],
    [150, 26],
    [200, 22],
    [250, 18],
    [300, 15],
    [350, 13]
  ],
  '8500': [
    [20 , 36],
    [40 , 37],
    [60 , 36],
    [100, 32],
    [150, 28],
    [200, 25],
    [250, 21],
    [300, 17],
    [350, 15]
  ],
  '9500': [
    [20 , 39],
    [40 , 40],
    [60 , 39],
    [100, 36],
    [150, 32],
    [200, 28],
    [250, 24],
    [300, 19],
    [350, 16]
  ],
  '10500': [
    [20 , 42],
    [40 , 44],
    [60 , 43],
    [100, 39],
    [150, 35],
    [200, 31],
    [250, 27],
    [300, 21],
    [350, 18]
  ],
  '11500': [
    [20 , 45],
    [40 , 48],
    [60 , 47],
    [100, 43],
    [150, 38],
    [200, 35],
    [250, 30],
    [300, 24],
    [350, 21]
  ]
};

/*
  Wood (1980)

  Prediction of milk yield adjusted for parity. Parameter adjustment (b & c) from p.137, table 4.

  milk  [kg]      Milk yield in week n
  a     [-]       Scale factor
  b     [-]       Shape constant
  c     [-]       Shape constant
  n     [week]    Week of lactation
  p     [#]       Parity, defaults to parity > 2
  scale [kg kg-1] Scale initial milk yield of heifers as pc of cows of parity > 2 (default 0.75)
*/

var milk = function (a, b, c, n, p, scale) {

  var milk = 0;

  if (is_null_or_undefined(scale))
    scale = 0.75;

  if (p === 1)
    milk = scale * a * pow(n, b - 0.0374) * exp((c + 0.0092) * n);
  else if (p === 2)
    milk = a * pow(n, b - 0.0253) * exp((c + 0.0000) * n);
  else /* defaults to parity > 2 */
    milk = a * pow(n, b + 0.0460) * exp((c - 0.0052) * n);

  return milk;

};

/*
  Calculate Wood a parameter for fat.

  We don't want to mess around with integrating incomplete gamma functions. Therefore we approximate with a numeric
  integration and calculate the a parameter from

    fat_average = a * (integrate pow(n, b) * exp(c * n) for n from 0 to 305/7) / 305/7
    a = fat_average / ((integrate pow(n, b) * exp(c * n) for n from 0 to 305/7) / 305/7)

  If we calculate a here we only need % fat as an input parameter which is usually available.

  fat_a   [%]     Wood fat scale parameter
  fat_avg [%]     fat average
  p       [#]     Parity, defaults to parity > 2
  n_mx    [week]  Week of maximum milk yield
*/

var fat_a = function (fat_avg, p, n_mx) {

  return fat_avg / fat_avg_305(1, p, n_mx);

};

/*
  Calculate Wood a parameter for protein.

  We don't want to mess around with integrating incomplete gamma functions. Therefore we approximate with a numeric
  integration and calculate the a parameter from

    protein_average = a * (integrate pow(n, b) * exp(c * n) for n from 0 to 305/7) / 305/7
    a = protein_average / ((integrate pow(n, b) * exp(c * n) for n from 0 to 305/7) / 305/7)

  If we calculate a here we only need % protein as an input parameter which is usually available.

  protein_a   [%]     Wood protein scale parameter
  protein_avg [%]     protein average
  p           [#]     Parity, defaults to parity > 2
  n_mx        [week]  Week of maximum milk yield
*/

var protein_a = function (protein_avg, p, n_mx) {

  return protein_avg / protein_avg_305(1, p, n_mx);

};

/*
  Wood (1980)

  Prediction of milk fat adjusted for parity. Parameter adjustment (b & c) from p.137, table 4. It is assumed that the
  fat minimum occurs six weeks after milk yield peaks.

  fat   [%]     Percent milk fat in week n
  a     [-]     Scale factor
  n     [week]  Week of lactation
  p     [#]     Parity, defaults to parity > 2
  n_mx  [week]  Week of maximum milk yield
*/

var fat = function (a, n, p, n_mx) {

  var fat = 0
    , b = -0.1230 /* shape constant */
    , c = 0.0104  /* shape constant */
    ;

  if (p === 1)
    b += 0.0168;
  else if (p === 2)
    b += 0.0320;
  else /* defaults to parity > 2 */
    b += -0.0078;

  /* adjust for week of milk peak */
  c = -(b / (4 + n_mx));
  b = -((6 + n_mx) * c);

  fat = a * pow(n, b) * exp(c * n);

  return fat;

};

/*
  Wood (1980)

  Prediction of milk protein adjusted for parity. Parameter adjustment (b & c) from p.137, table 4. It is assumed that
  the protein minimum occurs six weeks after milk yield peaks.

  protein [%]     Percent milk protein in week n
  a       [-]     Scale factor
  n       [week]  Week of lactation
  p       [#]     Parity, defaults to parity > 2
  n_mx    [week]  Week of maximum milk yield
*/

var protein = function (a, n, p, n_mx) {

  var protein = 0
    , b = -0.1274 /* shape constant */
    , c = 0.0107  /* shape constant */
    ;

  if (p === 1)
    b += 0.0200;
  else if (p === 2)
    b += 0.0025;
  else /* defaults to parity > 2 */
    b += -0.0136;

  /* adjust for week of milk peak */
  c = -(b / (4 + n_mx));
  b = -((6 + n_mx) * c);

  protein = a * pow(n, b) * exp(c * n);

  return protein;

};

/*
  Wood (1980)

  Day of maximum milk yield: x/dx (a * x^(b-1) * exp(c * x) * (b + c*x)) = 0 -> -b/c

  d_mx  [day]   day max milk
  b     [-]     Shape constant
  c     [-]     Shape constant
  p     [#]     Parity, defaults to parity > 2

*/

var d_mx = function (b, c, p) {

  /* in weeks */
  var n_mx = 0;

  if (p === 1)
    n_mx = -((b - 0.0374) / (c + 0.0092));
  else if (p === 2)
    n_mx = -((b - 0.0253) / (c + 0.0000));
  else /* defaults to parity > 2 */
    n_mx = -((b + 0.0460) / (c - 0.0052));

  return n_mx * 7;

};

/*
  305 days milk yield.

  milk_305  [kg]      Total milk yield in 305 days
  a         [-]       Scale factor
  b         [-]       Shape constant
  c         [-]       Shape constant
  p         [#]       Parity, defaults to parity > 2
  scale     [kg kg-1] Scale initial milk yield of heifers as pc of cows of parity > 2 (default 0.75)
*/

var milk_305 = function (a, b, c, p, scale) {

  var milk_305 = 0;

  for (var day = 1; day < 306; day++)
    milk_305 += milk(a, b, c, day / 7, p, scale);

  return milk_305;

};

/*
  Average 305 days milk fat percent.

  fat_avg_305 [%]     Average fat yield
  a           [-]     Scale factor
  p           [#]     Parity, defaults to parity > 2
  n_mx        [week]  Week of maximum milk yield
*/

var fat_avg_305 = function (a, p, n_mx) {

  var fat_avg_305 = 0;

  for (var day = 1; day < 306; day++)
     fat_avg_305 += fat(a, day / 7, p, n_mx);

  return fat_avg_305 / 305;

};

/*
  Average 305 days milk protein percent.

  protein_avg_305 [%]     Average protein yield
  a               [-]     Scale factor
  p               [#]     Parity, defaults to parity > 2
  n_mx            [week]  Week of maximum milk yield
*/

var protein_avg_305 = function (a, p, n_mx) {

  var protein_avg_305 = 0;

  for (var day = 1; day < 306; day++)
     protein_avg_305 += protein(a, day / 7, p, n_mx);

  return protein_avg_305 / 305;

};

/*
  Tyrrell (1965)

  Energy corrected milk. Corrected to F_target and P_target.

  ECM       [kg]  Energy corrected milk
  F_target  [%]   ECM fat target
  P_target  [%]   ECM protein target
  F         [%]   Fat in milk
  P         [%]   Protein in milk
  M         [kg]  Milk
*/

var ECM = function (F_target, P_target, F, P, M) {

  /* E [kcal lb-1] energy of one lb milk, Table 4, eq. 2 */
  var E_target = 40.72 * F_target + 22.65 * P_target + 102.77
    , E = 40.72 * F + 22.65 * P + 102.77
    ;

  var ECM =  M * E / E_target;

  return ECM;

};

return {
    milk: milk
  , fat: fat
  , fat_a: fat_a
  , protein: protein
  , protein_a: protein_a
  , d_mx: d_mx
  , milk_305: milk_305
  , fat_avg_305: fat_avg_305
  , protein_avg_305: protein_avg_305
  , ECM: ECM
  , data: data
};

}());
<commit_msg>Change milk scale factor<commit_after>/*
  Calculate milk yield and solids adjusted for parity requiring estimates for parameters of Wood's lactation curve.

  REFERENCES

  Wood, P.D.P. 1980.
  Breed variations in the shape of the lactation curve of cattle and their implications for efficiency,
  Animal Science, Volume 31, Issue 02, 1980, pp 133-141

  Tyrrell, H.F. et al. 1965.
  Prediction of the Energy Value of Cow's Milk,
  Journal of Dairy Science, Volume 48, Issue 9, 1215 - 1223

  DLG-Information 1/2006.
  Schätzung der Futteraufnahme bei der Milchkuh,
  DLG-Arbeitskreis Futter und Fütterung

  LICENSE

  Copyright 2014 Jan Vaillant   <jan.vaillant@zalf.de>
  Copyright 2014 Lisa Baldinger <lisa.baldinger@boku.ac.at>

  Distributed under the MIT License. See accompanying file LICENSE or copy at http://opensource.org/licenses/MIT

  Any publication for which this file or a derived work is used must include an a reference to the article:

  Baldinger, L., Vaillant, J., Zollitsch, W., Rinne, M. (2014) SOLID-DSS - Eine online-Anwendung zur verbesserten
  Abstimmung von Grundfutterangebot und -bedarf auf biologisch wirtschaftenden Low Input Milchviehbetrieben.
  In: Wiesinger, K., Cais, K., Obermaier, S. (eds), Angewandte Forschung und Beratung für den ökologischen Landbau in
  Bayern: Öko-Landbau-Tag 2014 am 9. April 2014 in Triesdorf; Tagungsband. LfL, Freising-Weihenstephan, pp. 19-22.
*/

var dairy = dairy || {};

dairy.milk = (function () {

var pow = Math.pow
  , exp = Math.exp
  ;

function is_null_or_undefined (x) {
  return x === null || x === undefined;
}

/*
  DLG (1/2006), Tabelle 7

  Typical lactation milk yield data per yield level [day, milk yield]. May be used to estimate Wood's lactation curve
  parameters (e.g. with lmfit).
*/
var data = {
  '6500': [
    [20 , 29],
    [40 , 30],
    [60 , 28],
    [100, 25],
    [150, 22],
    [200, 19],
    [250, 15],
    [300, 13],
    [350, 12]
  ],
  '7500': [
    [20 , 32],
    [40 , 33],
    [60 , 32],
    [100, 29],
    [150, 26],
    [200, 22],
    [250, 18],
    [300, 15],
    [350, 13]
  ],
  '8500': [
    [20 , 36],
    [40 , 37],
    [60 , 36],
    [100, 32],
    [150, 28],
    [200, 25],
    [250, 21],
    [300, 17],
    [350, 15]
  ],
  '9500': [
    [20 , 39],
    [40 , 40],
    [60 , 39],
    [100, 36],
    [150, 32],
    [200, 28],
    [250, 24],
    [300, 19],
    [350, 16]
  ],
  '10500': [
    [20 , 42],
    [40 , 44],
    [60 , 43],
    [100, 39],
    [150, 35],
    [200, 31],
    [250, 27],
    [300, 21],
    [350, 18]
  ],
  '11500': [
    [20 , 45],
    [40 , 48],
    [60 , 47],
    [100, 43],
    [150, 38],
    [200, 35],
    [250, 30],
    [300, 24],
    [350, 21]
  ]
};

/*
  Wood (1980)

  Prediction of milk yield adjusted for parity. Parameter adjustment (b & c) from p.137, table 4.

  Milk production potential (a parameter per parity) is scaled proportionally subject to the cow's size (BW / MBW).

  milk  [kg]      Milk yield in week n
  a     [-]       Scale factor
  b     [-]       Shape constant
  c     [-]       Shape constant
  n     [week]    Week of lactation
  p     [#]       Parity, defaults to parity > 2
  BW    [kg]      Actual body weight
  MBW   [kg]      Mature body weight
*/

var milk = function (a, b, c, n, p, BW, MBW) {

  var milk = 0;

  // if (is_null_or_undefined(scale))
  //   scale = 0.75;

  if (p === 1)
    milk = BW / MBW * a * pow(n, b - 0.0374) * exp((c + 0.0092) * n);
  else if (p === 2)
    milk = BW / MBW * a * pow(n, b - 0.0253) * exp((c + 0.0000) * n);
  else /* defaults to parity > 2 */
    milk = a * pow(n, b + 0.0460) * exp((c - 0.0052) * n);

  return milk;

};

/*
  Calculate Wood a parameter for fat.

  We don't want to mess around with integrating incomplete gamma functions. Therefore we approximate with a numeric
  integration and calculate the a parameter from

    fat_average = a * (integrate pow(n, b) * exp(c * n) for n from 0 to 305/7) / 305/7
    a = fat_average / ((integrate pow(n, b) * exp(c * n) for n from 0 to 305/7) / 305/7)

  If we calculate a here we only need % fat as an input parameter which is usually available.

  fat_a   [%]     Wood fat scale parameter
  fat_avg [%]     fat average
  p       [#]     Parity, defaults to parity > 2
  n_mx    [week]  Week of maximum milk yield
*/

var fat_a = function (fat_avg, p, n_mx) {

  return fat_avg / fat_avg_305(1, p, n_mx);

};

/*
  Calculate Wood a parameter for protein.

  We don't want to mess around with integrating incomplete gamma functions. Therefore we approximate with a numeric
  integration and calculate the a parameter from

    protein_average = a * (integrate pow(n, b) * exp(c * n) for n from 0 to 305/7) / 305/7
    a = protein_average / ((integrate pow(n, b) * exp(c * n) for n from 0 to 305/7) / 305/7)

  If we calculate a here we only need % protein as an input parameter which is usually available.

  protein_a   [%]     Wood protein scale parameter
  protein_avg [%]     protein average
  p           [#]     Parity, defaults to parity > 2
  n_mx        [week]  Week of maximum milk yield
*/

var protein_a = function (protein_avg, p, n_mx) {

  return protein_avg / protein_avg_305(1, p, n_mx);

};

/*
  Wood (1980)

  Prediction of milk fat adjusted for parity. Parameter adjustment (b & c) from p.137, table 4. It is assumed that the
  fat minimum occurs six weeks after milk yield peaks.

  fat   [%]     Percent milk fat in week n
  a     [-]     Scale factor
  n     [week]  Week of lactation
  p     [#]     Parity, defaults to parity > 2
  n_mx  [week]  Week of maximum milk yield
*/

var fat = function (a, n, p, n_mx) {

  var fat = 0
    , b = -0.1230 /* shape constant */
    , c = 0.0104  /* shape constant */
    ;

  if (p === 1)
    b += 0.0168;
  else if (p === 2)
    b += 0.0320;
  else /* defaults to parity > 2 */
    b += -0.0078;

  /* adjust for week of milk peak */
  c = -(b / (4 + n_mx));
  b = -((6 + n_mx) * c);

  fat = a * pow(n, b) * exp(c * n);

  return fat;

};

/*
  Wood (1980)

  Prediction of milk protein adjusted for parity. Parameter adjustment (b & c) from p.137, table 4. It is assumed that
  the protein minimum occurs six weeks after milk yield peaks.

  protein [%]     Percent milk protein in week n
  a       [-]     Scale factor
  n       [week]  Week of lactation
  p       [#]     Parity, defaults to parity > 2
  n_mx    [week]  Week of maximum milk yield
*/

var protein = function (a, n, p, n_mx) {

  var protein = 0
    , b = -0.1274 /* shape constant */
    , c = 0.0107  /* shape constant */
    ;

  if (p === 1)
    b += 0.0200;
  else if (p === 2)
    b += 0.0025;
  else /* defaults to parity > 2 */
    b += -0.0136;

  /* adjust for week of milk peak */
  c = -(b / (4 + n_mx));
  b = -((6 + n_mx) * c);

  protein = a * pow(n, b) * exp(c * n);

  return protein;

};

/*
  Wood (1980)

  Day of maximum milk yield: x/dx (a * x^(b-1) * exp(c * x) * (b + c*x)) = 0 -> -b/c

  d_mx  [day]   day max milk
  b     [-]     Shape constant
  c     [-]     Shape constant
  p     [#]     Parity, defaults to parity > 2

*/

var d_mx = function (b, c, p) {

  /* in weeks */
  var n_mx = 0;

  if (p === 1)
    n_mx = -((b - 0.0374) / (c + 0.0092));
  else if (p === 2)
    n_mx = -((b - 0.0253) / (c + 0.0000));
  else /* defaults to parity > 2 */
    n_mx = -((b + 0.0460) / (c - 0.0052));

  return n_mx * 7;

};

/*
  305 days milk yield.

  milk_305  [kg]      Total milk yield in 305 days
  a         [-]       Scale factor
  b         [-]       Shape constant
  c         [-]       Shape constant
  p         [#]       Parity, defaults to parity > 2
  scale     [kg kg-1] Scale initial milk yield of heifers as pc of cows of parity > 2 (default 0.75)
*/

var milk_305 = function (a, b, c, p, scale) {

  var milk_305 = 0;

  for (var day = 1; day < 306; day++)
    milk_305 += milk(a, b, c, day / 7, p, scale);

  return milk_305;

};

/*
  Average 305 days milk fat percent.

  fat_avg_305 [%]     Average fat yield
  a           [-]     Scale factor
  p           [#]     Parity, defaults to parity > 2
  n_mx        [week]  Week of maximum milk yield
*/

var fat_avg_305 = function (a, p, n_mx) {

  var fat_avg_305 = 0;

  for (var day = 1; day < 306; day++)
     fat_avg_305 += fat(a, day / 7, p, n_mx);

  return fat_avg_305 / 305;

};

/*
  Average 305 days milk protein percent.

  protein_avg_305 [%]     Average protein yield
  a               [-]     Scale factor
  p               [#]     Parity, defaults to parity > 2
  n_mx            [week]  Week of maximum milk yield
*/

var protein_avg_305 = function (a, p, n_mx) {

  var protein_avg_305 = 0;

  for (var day = 1; day < 306; day++)
     protein_avg_305 += protein(a, day / 7, p, n_mx);

  return protein_avg_305 / 305;

};

/*
  Tyrrell (1965)

  Energy corrected milk. Corrected to F_target and P_target.

  ECM       [kg]  Energy corrected milk
  F_target  [%]   ECM fat target
  P_target  [%]   ECM protein target
  F         [%]   Fat in milk
  P         [%]   Protein in milk
  M         [kg]  Milk
*/

var ECM = function (F_target, P_target, F, P, M) {

  /* E [kcal lb-1] energy of one lb milk, Table 4, eq. 2 */
  var E_target = 40.72 * F_target + 22.65 * P_target + 102.77
    , E = 40.72 * F + 22.65 * P + 102.77
    ;

  var ECM =  M * E / E_target;

  return ECM;

};

return {
    milk: milk
  , fat: fat
  , fat_a: fat_a
  , protein: protein
  , protein_a: protein_a
  , d_mx: d_mx
  , milk_305: milk_305
  , fat_avg_305: fat_avg_305
  , protein_avg_305: protein_avg_305
  , ECM: ECM
  , data: data
};

}());
<|endoftext|>


<commit_before>'use strict';

mldsApp.controller('EditAffiliateController', [
		'$scope',
		'$log',
		'$location',
		'$modal',
		'$routeParams',
		'Session',
		'AffiliateService',
		'ApplicationUtilsService',
		function($scope, $log, $location, $modal, $routeParams, Session, AffiliateService, ApplicationUtilsService) {

			var affiliateId = $routeParams.affiliateId && parseInt($routeParams.affiliateId, 10);

			$scope.alerts = [];
			$scope.affiliate = {};
			$scope.affiliateDetails = {};
			$scope.approved = false;
			$scope.readOnly = false;
			$scope.type;

			function loadAffiliate() {
				var queryPromise = AffiliateService.affiliate(affiliateId);

				queryPromise.success(function(affiliate) {
					$scope.affiliate = affiliate;
					$scope.approved = ApplicationUtilsService.isApplicationApproved(affiliate.application);
					$scope.isEditable = Session.isAdmin() || (Session.member.key == affiliate.application.member.key);
					$scope.readOnly = !ApplicationUtilsService.isApplicationApproved(affiliate.application) || !$scope.isEditable;
					$scope.type = affiliate.type;

					if (affiliate.affiliateDetails) {
						$scope.affiliateDetails = affiliate.affiliateDetails;
					}
				});

			}

			loadAffiliate();

			$scope.form = {};
	    	$scope.form.attempted = false;

	    	$scope.save = function () {
	    		if ($scope.form.$invalid) {
	    			$scope.form.attempted = true;
	    			return;
	    		}

	    		$scope.submitting = true;
	    		$scope.alerts.splice(0, $scope.alerts.length);

	    		AffiliateService.updateAffiliateDetails($scope.affiliate.affiliateId, $scope.affiliateDetails)
	    			.success(function(result) {
	    				$location.path('/affiliates/'+ affiliateId);
	    			})
				["catch"](function(message) {
					$scope.alerts.push({type: 'danger', msg: 'Network failure, please try again later.'});
					$scope.submitting = false;
				});
	        };

	        $scope.cancel = function() {
	        	$location.path('/affiliates/'+ affiliateId);
	        };

		} ]);
<commit_msg>Edits: Removing redirect to show success message<commit_after>'use strict';

mldsApp.controller('EditAffiliateController', [
		'$scope',
		'$log',
		'$location',
		'$modal',
		'$routeParams',
		'Session',
		'AffiliateService',
		'ApplicationUtilsService',
		function($scope, $log, $location, $modal, $routeParams, Session, AffiliateService, ApplicationUtilsService) {

			var affiliateId = $routeParams.affiliateId && parseInt($routeParams.affiliateId, 10);

			$scope.alerts = [];
			$scope.affiliate = {};
			$scope.affiliateDetails = {};
			$scope.approved = false;
			$scope.readOnly = false;
			$scope.type;

			function loadAffiliate() {
				var queryPromise = AffiliateService.affiliate(affiliateId);

				queryPromise.success(function(affiliate) {
					$scope.affiliate = affiliate;
					$scope.approved = ApplicationUtilsService.isApplicationApproved(affiliate.application);
					$scope.isEditable = Session.isAdmin() || (Session.member.key == affiliate.application.member.key);
					$scope.readOnly = !ApplicationUtilsService.isApplicationApproved(affiliate.application) || !$scope.isEditable;
					$scope.type = affiliate.type;

					if (affiliate.affiliateDetails) {
						$scope.affiliateDetails = affiliate.affiliateDetails;
					}
				});

			}

			loadAffiliate();

			$scope.form = {};
	    	$scope.form.attempted = false;

	    	$scope.save = function () {
	    		if ($scope.form.$invalid) {
	    			$scope.form.attempted = true;
	    			return;
	    		}

	    		$scope.submitting = true;
	    		$scope.alerts.splice(0, $scope.alerts.length);

	    		AffiliateService.updateAffiliateDetails($scope.affiliate.affiliateId, $scope.affiliateDetails)
	    			.success(function(result) {
	    				$scope.affiliateDetails = result.data;
	    				$scope.submitting = false;
	    				$scope.alerts.push({type: 'success', msg: 'Contact information has been successfully saved.'});
	    			})
				["catch"](function(message) {
					$scope.alerts.push({type: 'danger', msg: 'Network failure, please try again later.'});
					$scope.submitting = false;
				});
	        };

	        $scope.cancel = function() {
	        	$location.path('/affiliates/'+ affiliateId);
	        };

		} ]);
<|endoftext|>