<commit_before>define([
    'numeral',
    'moment',
    'knockout',
    'uuid',
    'kb_common/utils',
    'kb_common/html',
    'knockout-mapping',
    'knockout-arraytransforms',
    './lib/knockout-es6-collections',
    'knockout-validation',
    'knockout-switch-case'
], function (
    numeral,
    moment,
    ko,
    Uuid,
    Utils,
    html
) {
    // Knockout Defaults
    ko.options.deferUpdates = true;

    // from: https://github.com/knockout/knockout/issues/914
    ko.subscribable.fn.subscribeChanged = function (callback, context) {
        var savedValue = this.peek();
        return this.subscribe(function (latestValue) {
            var oldValue = savedValue;
            savedValue = latestValue;
            callback.call(context, latestValue, oldValue);
        });
    };

    function isEmptyJSON(value) {
        if (value === undefined) {
            return true;
        }
        switch (value) {
        case '""':
            return true;
        case '[]':
            return true;
        case '{}':
            return true;
        case 'null':
            return true;
        }
    }

    ko.extenders.dirty = function (target, startDirty) {
        var lastValue = target();
        var cleanValue = ko.observable(ko.mapping.toJSON(target));

        var dirtyOverride = ko.observable(ko.utils.unwrapObservable(startDirty));

        target.isDirty = ko.computed(function () {
            var currentValue = ko.mapping.toJSON(target);
            var currentIsEmpty = isEmptyJSON(currentValue);
            var lastCleanValue = cleanValue();
            var cleanIsEmpty = isEmptyJSON(lastCleanValue);
            if (currentIsEmpty & cleanIsEmpty) {
                return false;
            }
            return dirtyOverride() || currentValue !== lastCleanValue;
        });

        target.markClean = function () {
            cleanValue(ko.mapping.toJSON(target));
            lastValue = target();
            dirtyOverride(false);
        };
        target.markDirty = function () {
            dirtyOverride(true);
        };
        target.reset = function () {
            target(lastValue);
        };

        return target;
    };

    ko.extenders.logChange = function (target, label) {
        target.subscribe(function (newValue) {
            console.log(label, newValue);
        });
        return target;
    };

    ko.extenders.enabled = function (target, config) {
        var isEnabled = ko.observable();
        target.isEnabled = isEnabled;

        function enableTest(newValue) {
            try {
                var enabled = isEnabled();
                var newEnabled = config.fun(newValue);
                if (enabled === undefined) {
                    // first time running.
                    isEnabled(newEnabled);
                } else {
                    if (enabled) {
                        if (!newEnabled) {
                            // reset target to be empty.
                            // target('');
                            isEnabled(false);
                        }
                    } else if (newEnabled) {
                        isEnabled(true);
                    }
                }
            } catch (ex) {
                console.error('Error running enable test: ' + ex.message);
            }
        }
        config.observable.subscribe(enableTest);
        // enableTest(config.observable());
    };

    ko.extenders.constraint = function (target, config) {
        target.constraint = {};
        target.constraint.description = config.description;
        target.constraint.messages = config.messages || {};

        if (config.required) {
            if (ko.isComputed(config.required)) {
                target.constraint.isRequired = config.required;
            } else if (ko.isObservable(config.required)) {
                target.constraint.isRequired = config.required;
            } else {
                target.constraint.isRequired = ko.observable(config.required);
            }
        } else {
            target.constraint.isRequired = ko.observable(false);
        }
        target.constraint.autoTrim = ko.observable(config.autoTrim || true);
        target.constraint.isValid = ko.observable(config.valid || true);
        target.constraint.message = ko.observable();
        target.constraint.state = ko.observable('new');

        function isEmpty(value) {
            if (typeof value === 'undefined' ||
                value === null) {
                return true;
            }
            if (typeof value === 'string') {
                if (target.constraint.autoTrim()) {
                    if (value.trim().length === 0) {
                        return true;
                    }
                }
            }
            if (value instanceof Array) {
                if (value.length === 0) {
                    return true;
                }
            }
            return false;
        }

        function validate() {
            try {
                var newValue = target();
                // first evaluate the required condition
                if (isEmpty(newValue)) {
                    if (target.constraint.isRequired()) {
                        target.constraint.message(target.constraint.messages.requiredButEmpty || 'Required but empty');
                        target.constraint.isValid(false);
                        target.constraint.state('required-missing');
                        return;
                    } else {
                        target.constraint.message('');
                        target.constraint.isValid(true);
                        target.constraint.state('empty-optional');
                        return;
                    }
                }

                // Then if it passes, run the validator
                if (!config.validate) {
                    target.constraint.message('');
                    target.constraint.isValid(true);
                    target.constraint.state('valid');
                    return;
                }
                var result = config.validate(newValue);
                if (typeof result === 'string') {
                    result = {
                        message: result
                    };
                }
                if (result) {
                    target.constraint.message(result.message || '');
                    target.constraint.isValid(false);
                    target.constraint.state('invalid');
                } else {
                    target.constraint.message('');
                    target.constraint.isValid(true);
                    target.constraint.state('valid');
                }
            } catch (ex) {
                target.constraint.message('Error running validation: ' + ex.message);
                console.error('Error running validation: ' + ex.message);
                target.constraint.isValid(false);
            }
        }

        validate(target());

        target.subscribe(validate);

        target.constraint.isRequired.subscribe(validate);

        return target;
    };

    function svgTemplateLoader(name, templateConfig, callback) {
        if (!templateConfig.svg) {
            callback(null);
            return;
        }
        var svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
        svg.innerHTML = templateConfig.svg;
        callback(svg.childNodes);
    }
    ko.components.loaders.unshift({
        loadTemplate: svgTemplateLoader
    });

    // BINDINGS

    ko.bindingHandlers.numberText = {
        update: function (element, valueAccessor, allBindings) {
            var value = valueAccessor();
            var valueUnwrapped = ko.unwrap(value);
            var format = allBindings.get('numberFormat') || '';
            var formatted = numeral(valueUnwrapped).format(format);
            element.innerText = formatted;
        }
    };

    function niceDuration(value, options) {
        options = options || {};
        var minimized = [];
        var units = [{
            unit: 'millisecond',
            short: 'ms',
            single: 'm',
            size: 1000
        }, {
            unit: 'second',
            short: 'sec',
            single: 's',
            size: 60
        }, {
            unit: 'minute',
            short: 'min',
            single: 'm',
            size: 60
        }, {
            unit: 'hour',
            short: 'hr',
            single: 'h',
            size: 24
        }, {
            unit: 'day',
            short: 'day',
            single: 'd',
            size: 30
        }];
        var temp = Math.abs(value);
        var parts = units
            .map(function (unit) {
                // Get the remainder of the current value
                // sans unit size of it composing the next
                // measure.
                var unitValue = temp % unit.size;
                // Recompute the measure in terms of the next unit size.
                temp = (temp - unitValue) / unit.size;
                return {
                    name: unit.single,
                    unit: unit.unit,
                    value: unitValue
                };
            }).reverse();

        parts.pop();

        // We skip over large units which have not value until we
        // hit the first unit with value. This effectively trims off
        // zeros from the end.
        // We also can limit the resolution with options.resolution
        var keep = false;
        for (var i = 0; i < parts.length; i += 1) {
            if (!keep) {
                if (parts[i].value > 0) {
                    keep = true;
                    minimized.push(parts[i]);
                }
            } else {
                minimized.push(parts[i]);
                if (options.resolution &&
                    options.resolution === parts[i].unit) {
                    break;
                }
            }
        }

        if (minimized.length === 0) {
            // This means that there is are no time measurements > 1 second.
            return '<1s';
        } else {
            // Skip seconds if we are into the hours...
            // if (minimized.length > 2) {
            //     minimized.pop();
            // }
            return minimized.map(function (item) {
                return String(item.value) + item.name;
            })
                .join(' ');
        }
    }

    ko.bindingHandlers.typedText = {
        update: function (element, valueAccessor) {
            var value = valueAccessor();
            var valueUnwrapped = ko.unwrap(value.value);
            var format = value.format;
            var type = value.type;
            var missing = value.missing || '';
            var defaultValue = value.default;
            // var format = allBindings.get('type') || '';
            // var format = allBindings.get('numberFormat') || '';
            var formatted;

            switch (type) {
            case 'number':
                numeral.nullFormat('');
                if (valueUnwrapped === undefined || valueUnwrapped === null) {
                    formatted = missing;
                } else {
                    formatted = numeral(valueUnwrapped).format(format);
                }
                break;
            case 'date':
                if (valueUnwrapped === undefined || valueUnwrapped === null) {
                    formatted = missing;
                } else {
                    switch (format) {
                    case 'elapsed':
                    case 'nice-elapsed':
                        formatted = Utils.niceElapsedTime(moment(valueUnwrapped).toDate());
                        break;
                    case 'duration':
                        // formatted = Utils.niceElapsedTime(moment(valueUnwrapped).toDate());
                        formatted = niceDuration(valueUnwrapped);
                        break;
                    default: formatted = moment(valueUnwrapped).format(format);
                    }
                }
                break;
            case 'bool':
            case 'boolean':
                if (valueUnwrapped === undefined || valueUnwrapped === null) {
                    if (defaultValue === undefined) {
                        formatted = missing;
                        break;
                    }
                    valueUnwrapped = defaultValue;
                }

                if (valueUnwrapped) {
                    formatted = 'true';
                }
                formatted = 'false';

                break;
            case 'text':
            case 'string':
            default:
                formatted = valueUnwrapped;
            }

            element.innerText = formatted;
        }
    };

    ko.subscribable.fn.subscribeChanged = function (callback, context) {
        var savedValue = this.peek();
        return this.subscribe(function (latestValue) {
            var oldValue = savedValue;
            savedValue = latestValue;
            callback.call(context, latestValue, oldValue);
        });
    };

    ko.subscribable.fn.syncWith = function (targetObservable, callbackTarget, event) {
        var sourceObservable = this;
        sourceObservable(targetObservable());
        sourceObservable.subscribe(function (newValue) {
            targetObservable(newValue);
        }, callbackTarget, event);
        targetObservable.subscribe(function (newValue) {
            sourceObservable(newValue);
        }, callbackTarget, event);
        return sourceObservable;
    };

    ko.subscribable.fn.syncFrom = function (targetObservable, callbackTarget, event) {
        var sourceObservable = this;
        sourceObservable(targetObservable());
        targetObservable.subscribe(function (v) {
            sourceObservable(v);
        }, callbackTarget, event);
        return sourceObservable;
    };

    ko.subscribable.fn.syncTo = function (targetObservable, callbackTarget, event) {
        var sourceObservable = this;
        targetObservable(sourceObservable());
        sourceObservable.subscribe(function (v) {
            targetObservable(v);
        }, callbackTarget, event);
        return sourceObservable;
    };

    // pure functions stuck onto ko

    var t = html.tag,
        div = t('div');

    function komponent(componentDef) {
        return '<!-- ko component: {name: "' + componentDef.name +
            '", params: {' +
            Object.keys(componentDef.params).map(function (key) {
                return key + ':' + componentDef.params[key];
            }).join(',') + '}}--><!-- /ko -->';
    }

    function createRootComponent(runtime, name) {
        var vm = {
            runtime: runtime,
            running: ko.observable(false)
        };
        var temp = document.createElement('div');
        temp.innerHTML = div({
            style: {
                flex: '1 1 0px',
                display: 'flex',
                flexDirection: 'column'
            }
        }, [
            '<!-- ko if: running -->',
            komponent({
                name: name,
                params: {
                    runtime: 'runtime'
                }
            }),
            '<!-- /ko -->'
        ]);
        var node = temp.firstChild;
        ko.applyBindings(vm, node);

        function start() {
            vm.running(true);
        }

        function stop() {
            vm.running(false);
        }

        return {
            vm: vm,
            node: node,
            start: start,
            stop: stop
        };
    }

    function registerComponent(component) {
        var name = new Uuid(4).format();
        ko.components.register(name, component());

        return {
            name: function () {
                return name;
            },
            quotedName: function () {
                return '"' + name + '"';
            }
        };
    }

    ko.kb = {};

    ko.kb.komponent = komponent;
    ko.kb.createRootComponent = createRootComponent;
    ko.kb.registerComponent = registerComponent;

    return ko;
});
<commit_msg>add support for global installation of component stylesheets [SCT-554]<commit_after>define([
    'numeral',
    'moment',
    'knockout',
    'uuid',
    'kb_common/utils',
    'kb_common/html',
    'knockout-mapping',
    'knockout-arraytransforms',
    './lib/knockout-es6-collections',
    'knockout-validation',
    'knockout-switch-case'
], function (
    numeral,
    moment,
    ko,
    Uuid,
    Utils,
    html
) {
    // Knockout Defaults
    ko.options.deferUpdates = true;

    // from: https://github.com/knockout/knockout/issues/914
    ko.subscribable.fn.subscribeChanged = function (callback, context) {
        var savedValue = this.peek();
        return this.subscribe(function (latestValue) {
            var oldValue = savedValue;
            savedValue = latestValue;
            callback.call(context, latestValue, oldValue);
        });
    };

    function isEmptyJSON(value) {
        if (value === undefined) {
            return true;
        }
        switch (value) {
        case '""':
            return true;
        case '[]':
            return true;
        case '{}':
            return true;
        case 'null':
            return true;
        }
    }

    ko.extenders.dirty = function (target, startDirty) {
        var lastValue = target();
        var cleanValue = ko.observable(ko.mapping.toJSON(target));

        var dirtyOverride = ko.observable(ko.utils.unwrapObservable(startDirty));

        target.isDirty = ko.computed(function () {
            var currentValue = ko.mapping.toJSON(target);
            var currentIsEmpty = isEmptyJSON(currentValue);
            var lastCleanValue = cleanValue();
            var cleanIsEmpty = isEmptyJSON(lastCleanValue);
            if (currentIsEmpty & cleanIsEmpty) {
                return false;
            }
            return dirtyOverride() || currentValue !== lastCleanValue;
        });

        target.markClean = function () {
            cleanValue(ko.mapping.toJSON(target));
            lastValue = target();
            dirtyOverride(false);
        };
        target.markDirty = function () {
            dirtyOverride(true);
        };
        target.reset = function () {
            target(lastValue);
        };

        return target;
    };

    ko.extenders.logChange = function (target, label) {
        target.subscribe(function (newValue) {
            console.log(label, newValue);
        });
        return target;
    };

    ko.extenders.enabled = function (target, config) {
        var isEnabled = ko.observable();
        target.isEnabled = isEnabled;

        function enableTest(newValue) {
            try {
                var enabled = isEnabled();
                var newEnabled = config.fun(newValue);
                if (enabled === undefined) {
                    // first time running.
                    isEnabled(newEnabled);
                } else {
                    if (enabled) {
                        if (!newEnabled) {
                            // reset target to be empty.
                            // target('');
                            isEnabled(false);
                        }
                    } else if (newEnabled) {
                        isEnabled(true);
                    }
                }
            } catch (ex) {
                console.error('Error running enable test: ' + ex.message);
            }
        }
        config.observable.subscribe(enableTest);
        // enableTest(config.observable());
    };

    ko.extenders.constraint = function (target, config) {
        target.constraint = {};
        target.constraint.description = config.description;
        target.constraint.messages = config.messages || {};

        if (config.required) {
            if (ko.isComputed(config.required)) {
                target.constraint.isRequired = config.required;
            } else if (ko.isObservable(config.required)) {
                target.constraint.isRequired = config.required;
            } else {
                target.constraint.isRequired = ko.observable(config.required);
            }
        } else {
            target.constraint.isRequired = ko.observable(false);
        }
        target.constraint.autoTrim = ko.observable(config.autoTrim || true);
        target.constraint.isValid = ko.observable(config.valid || true);
        target.constraint.message = ko.observable();
        target.constraint.state = ko.observable('new');

        function isEmpty(value) {
            if (typeof value === 'undefined' ||
                value === null) {
                return true;
            }
            if (typeof value === 'string') {
                if (target.constraint.autoTrim()) {
                    if (value.trim().length === 0) {
                        return true;
                    }
                }
            }
            if (value instanceof Array) {
                if (value.length === 0) {
                    return true;
                }
            }
            return false;
        }

        function validate() {
            try {
                var newValue = target();
                // first evaluate the required condition
                if (isEmpty(newValue)) {
                    if (target.constraint.isRequired()) {
                        target.constraint.message(target.constraint.messages.requiredButEmpty || 'Required but empty');
                        target.constraint.isValid(false);
                        target.constraint.state('required-missing');
                        return;
                    } else {
                        target.constraint.message('');
                        target.constraint.isValid(true);
                        target.constraint.state('empty-optional');
                        return;
                    }
                }

                // Then if it passes, run the validator
                if (!config.validate) {
                    target.constraint.message('');
                    target.constraint.isValid(true);
                    target.constraint.state('valid');
                    return;
                }
                var result = config.validate(newValue);
                if (typeof result === 'string') {
                    result = {
                        message: result
                    };
                }
                if (result) {
                    target.constraint.message(result.message || '');
                    target.constraint.isValid(false);
                    target.constraint.state('invalid');
                } else {
                    target.constraint.message('');
                    target.constraint.isValid(true);
                    target.constraint.state('valid');
                }
            } catch (ex) {
                target.constraint.message('Error running validation: ' + ex.message);
                console.error('Error running validation: ' + ex.message);
                target.constraint.isValid(false);
            }
        }

        validate(target());

        target.subscribe(validate);

        target.constraint.isRequired.subscribe(validate);

        return target;
    };

    function svgTemplateLoader(name, templateConfig, callback) {
        if (!templateConfig.svg) {
            callback(null);
            return;
        }
        var svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
        svg.innerHTML = templateConfig.svg;
        callback(svg.childNodes);
    }
    ko.components.loaders.unshift({
        loadTemplate: svgTemplateLoader
    });

    // BINDINGS

    ko.bindingHandlers.numberText = {
        update: function (element, valueAccessor, allBindings) {
            var value = valueAccessor();
            var valueUnwrapped = ko.unwrap(value);
            var format = allBindings.get('numberFormat') || '';
            var formatted = numeral(valueUnwrapped).format(format);
            element.innerText = formatted;
        }
    };

    function niceDuration(value, options) {
        options = options || {};
        var minimized = [];
        var units = [{
            unit: 'millisecond',
            short: 'ms',
            single: 'm',
            size: 1000
        }, {
            unit: 'second',
            short: 'sec',
            single: 's',
            size: 60
        }, {
            unit: 'minute',
            short: 'min',
            single: 'm',
            size: 60
        }, {
            unit: 'hour',
            short: 'hr',
            single: 'h',
            size: 24
        }, {
            unit: 'day',
            short: 'day',
            single: 'd',
            size: 30
        }];
        var temp = Math.abs(value);
        var parts = units
            .map(function (unit) {
                // Get the remainder of the current value
                // sans unit size of it composing the next
                // measure.
                var unitValue = temp % unit.size;
                // Recompute the measure in terms of the next unit size.
                temp = (temp - unitValue) / unit.size;
                return {
                    name: unit.single,
                    unit: unit.unit,
                    value: unitValue
                };
            }).reverse();

        parts.pop();

        // We skip over large units which have not value until we
        // hit the first unit with value. This effectively trims off
        // zeros from the end.
        // We also can limit the resolution with options.resolution
        var keep = false;
        for (var i = 0; i < parts.length; i += 1) {
            if (!keep) {
                if (parts[i].value > 0) {
                    keep = true;
                    minimized.push(parts[i]);
                }
            } else {
                minimized.push(parts[i]);
                if (options.resolution &&
                    options.resolution === parts[i].unit) {
                    break;
                }
            }
        }

        if (minimized.length === 0) {
            // This means that there is are no time measurements > 1 second.
            return '<1s';
        } else {
            // Skip seconds if we are into the hours...
            // if (minimized.length > 2) {
            //     minimized.pop();
            // }
            return minimized.map(function (item) {
                return String(item.value) + item.name;
            })
                .join(' ');
        }
    }

    ko.bindingHandlers.typedText = {
        update: function (element, valueAccessor) {
            var value = valueAccessor();
            var valueUnwrapped = ko.unwrap(value.value);
            var format = value.format;
            var type = value.type;
            var missing = value.missing || '';
            var defaultValue = value.default;
            // var format = allBindings.get('type') || '';
            // var format = allBindings.get('numberFormat') || '';
            var formatted;

            switch (type) {
            case 'number':
                numeral.nullFormat('');
                if (valueUnwrapped === undefined || valueUnwrapped === null) {
                    formatted = missing;
                } else {
                    formatted = numeral(valueUnwrapped).format(format);
                }
                break;
            case 'date':
                if (valueUnwrapped === undefined || valueUnwrapped === null) {
                    formatted = missing;
                } else {
                    switch (format) {
                    case 'elapsed':
                    case 'nice-elapsed':
                        formatted = Utils.niceElapsedTime(moment(valueUnwrapped).toDate());
                        break;
                    case 'duration':
                        // formatted = Utils.niceElapsedTime(moment(valueUnwrapped).toDate());
                        formatted = niceDuration(valueUnwrapped);
                        break;
                    default: formatted = moment(valueUnwrapped).format(format);
                    }
                }
                break;
            case 'bool':
            case 'boolean':
                if (valueUnwrapped === undefined || valueUnwrapped === null) {
                    if (defaultValue === undefined) {
                        formatted = missing;
                        break;
                    }
                    valueUnwrapped = defaultValue;
                }

                if (valueUnwrapped) {
                    formatted = 'true';
                }
                formatted = 'false';

                break;
            case 'text':
            case 'string':
            default:
                formatted = valueUnwrapped;
            }

            element.innerText = formatted;
        }
    };

    ko.subscribable.fn.subscribeChanged = function (callback, context) {
        var savedValue = this.peek();
        return this.subscribe(function (latestValue) {
            var oldValue = savedValue;
            savedValue = latestValue;
            callback.call(context, latestValue, oldValue);
        });
    };

    ko.subscribable.fn.syncWith = function (targetObservable, callbackTarget, event) {
        var sourceObservable = this;
        sourceObservable(targetObservable());
        sourceObservable.subscribe(function (newValue) {
            targetObservable(newValue);
        }, callbackTarget, event);
        targetObservable.subscribe(function (newValue) {
            sourceObservable(newValue);
        }, callbackTarget, event);
        return sourceObservable;
    };

    ko.subscribable.fn.syncFrom = function (targetObservable, callbackTarget, event) {
        var sourceObservable = this;
        sourceObservable(targetObservable());
        targetObservable.subscribe(function (v) {
            sourceObservable(v);
        }, callbackTarget, event);
        return sourceObservable;
    };

    ko.subscribable.fn.syncTo = function (targetObservable, callbackTarget, event) {
        var sourceObservable = this;
        targetObservable(sourceObservable());
        sourceObservable.subscribe(function (v) {
            targetObservable(v);
        }, callbackTarget, event);
        return sourceObservable;
    };

    // pure functions stuck onto ko

    var t = html.tag,
        div = t('div');

    function komponent(componentDef) {
        return '<!-- ko component: {name: "' + componentDef.name +
            '", params: {' +
            Object.keys(componentDef.params).map(function (key) {
                return key + ':' + componentDef.params[key];
            }).join(',') + '}}--><!-- /ko -->';
    }

    function createRootComponent(runtime, name) {
        var vm = {
            runtime: runtime,
            running: ko.observable(false)
        };
        var temp = document.createElement('div');
        temp.innerHTML = div({
            style: {
                flex: '1 1 0px',
                display: 'flex',
                flexDirection: 'column'
            }
        }, [
            '<!-- ko if: running -->',
            komponent({
                name: name,
                params: {
                    runtime: 'runtime'
                }
            }),
            '<!-- /ko -->'
        ]);
        var node = temp.firstChild;
        ko.applyBindings(vm, node);

        function start() {
            vm.running(true);
        }

        function stop() {
            vm.running(false);
        }

        return {
            vm: vm,
            node: node,
            start: start,
            stop: stop
        };
    }

    var installedStylesheets = {};
    function installStylesheet(id, stylesheet) {
        if (installedStylesheets[id]) {
            return;
        }
        var temp = document.createElement('div');
        temp.innerHTML = stylesheet;
        var style = temp.querySelector('style');
        style.id = 'componentStyle_' + id;
        if (!style) {
            // This means an invalid stylesheet was passed here.
            console.warn('Invalid component stylesheet, no style tag: ', stylesheet);
            return;
        }
        document.head.appendChild(style);
        installedStylesheets[id] = stylesheet;
    }

    function registerComponent(componentFactory) {
        var name = new Uuid(4).format();
        var component = componentFactory();
        ko.components.register(name, component);

        if (component.stylesheet) {
            installStylesheet(name, component.stylesheet);
        }

        return {
            name: function () {
                return name;
            },
            quotedName: function () {
                return '"' + name + '"';
            }
        };
    }

    ko.kb = {};

    ko.kb.komponent = komponent;
    ko.kb.createRootComponent = createRootComponent;
    ko.kb.registerComponent = registerComponent;

    return ko;
});
<|endoftext|>


<commit_before>/*!	Curve calc function for canvas 2.3.1
 *	Epistemex (c) 2013-2014
 *	License: MIT
 */

/**
 * Calculates an array containing points representing a cardinal spline through given point array.
 * Points must be arranged as: [x1, y1, x2, y2, ..., xn, yn].
 *
 * The points for the cardinal spline are returned as a new array.
 *
 * @param {Array} points - point array
 * @param {Number} [tension=0.5] - tension. Typically between [0.0, 1.0] but can be exceeded
 * @param {Number} [numOfSeg=20] - number of segments between two points (line resolution)
 * @returns {Float32Array} New array with the calculated points that was added to the path
 */
export function getCurvePoints(points, tension, numOfSeg) {
  "use strict";

  // options or defaults
  tension = typeof tension === "number" ? tension : 0.5;
  numOfSeg = numOfSeg ? numOfSeg : 25;

  let pts, // for cloning point array
    i = 1,
    l = points.length,
    rPos = 0,
    rLen = (l - 2) * numOfSeg + 2,
    res = new Float32Array(rLen),
    cache = new Float32Array((numOfSeg + 2) * 4),
    cachePtr = 4;

  pts = points.slice(0);

  pts.unshift(points[1]); // copy 1. point and insert at beginning
  pts.unshift(points[0]);
  pts.push(points[l - 2], points[l - 1]); // duplicate end-points

  // cache inner-loop calculations as they are based on t alone
  cache[0] = 1; // 1,0,0,0

  for (; i < numOfSeg; i++) {
    let st = i / numOfSeg,
      st2 = st * st,
      st3 = st2 * st,
      st23 = st3 * 2,
      st32 = st2 * 3;

    cache[cachePtr++] = st23 - st32 + 1; // c1
    cache[cachePtr++] = st32 - st23; // c2
    cache[cachePtr++] = st3 - 2 * st2 + st; // c3
    cache[cachePtr++] = st3 - st2; // c4
  }

  cache[++cachePtr] = 1; // 0,1,0,0

  // calc. points
  parse(pts, cache, l);

  function parse(pts, cache, l) {
    for (var i = 2, t; i < l; i += 2) {
      let pt1 = pts[i],
        pt2 = pts[i + 1],
        pt3 = pts[i + 2],
        pt4 = pts[i + 3],
        t1x = (pt3 - pts[i - 2]) * tension,
        t1y = (pt4 - pts[i - 1]) * tension,
        t2x = (pts[i + 4] - pt1) * tension,
        t2y = (pts[i + 5] - pt2) * tension;

      for (t = 0; t < numOfSeg; t++) {
        let c = t << 2, //t * 4;
          c1 = cache[c],
          c2 = cache[c + 1],
          c3 = cache[c + 2],
          c4 = cache[c + 3];

        res[rPos++] = c1 * pt1 + c2 * pt3 + c3 * t1x + c4 * t2x;
        res[rPos++] = c1 * pt2 + c2 * pt4 + c3 * t1y + c4 * t2y;
      }
    }
  }

  // add last point
  l = points.length - 2;
  res[rPos++] = points[l];
  res[rPos] = points[l + 1];

  return res;
}
<commit_msg>Use default args for spline<commit_after>/*!	Curve calc function for canvas 2.3.1
 *	Epistemex (c) 2013-2014
 *	License: MIT
 */

/**
 * Calculates an array containing points representing a cardinal spline through given point array.
 * Points must be arranged as: [x1, y1, x2, y2, ..., xn, yn].
 *
 * The points for the cardinal spline are returned as a new array.
 *
 * @param {Array} points - point array
 * @param {Number} [tension=0.5] - tension. Typically between [0.0, 1.0] but can be exceeded
 * @param {Number} [numOfSeg=20] - number of segments between two points (line resolution)
 * @returns {Float32Array} New array with the calculated points that was added to the path
 */
export function getCurvePoints(points, tension = 0.5, numOfSeg = 25) {
  let pts, // for cloning point array
    i = 1,
    l = points.length,
    rPos = 0,
    rLen = (l - 2) * numOfSeg + 2,
    res = new Float32Array(rLen),
    cache = new Float32Array((numOfSeg + 2) * 4),
    cachePtr = 4;

  pts = points.slice(0);

  pts.unshift(points[1]); // copy 1. point and insert at beginning
  pts.unshift(points[0]);
  pts.push(points[l - 2], points[l - 1]); // duplicate end-points

  // cache inner-loop calculations as they are based on t alone
  cache[0] = 1; // 1,0,0,0

  for (; i < numOfSeg; i++) {
    let st = i / numOfSeg,
      st2 = st * st,
      st3 = st2 * st,
      st23 = st3 * 2,
      st32 = st2 * 3;

    cache[cachePtr++] = st23 - st32 + 1; // c1
    cache[cachePtr++] = st32 - st23; // c2
    cache[cachePtr++] = st3 - 2 * st2 + st; // c3
    cache[cachePtr++] = st3 - st2; // c4
  }

  cache[++cachePtr] = 1; // 0,1,0,0

  // calc. points
  parse(pts, cache, l);

  function parse(pts, cache, l) {
    for (var i = 2, t; i < l; i += 2) {
      let pt1 = pts[i],
        pt2 = pts[i + 1],
        pt3 = pts[i + 2],
        pt4 = pts[i + 3],
        t1x = (pt3 - pts[i - 2]) * tension,
        t1y = (pt4 - pts[i - 1]) * tension,
        t2x = (pts[i + 4] - pt1) * tension,
        t2y = (pts[i + 5] - pt2) * tension;

      for (t = 0; t < numOfSeg; t++) {
        let c = t << 2, //t * 4;
          c1 = cache[c],
          c2 = cache[c + 1],
          c3 = cache[c + 2],
          c4 = cache[c + 3];

        res[rPos++] = c1 * pt1 + c2 * pt3 + c3 * t1x + c4 * t2x;
        res[rPos++] = c1 * pt2 + c2 * pt4 + c3 * t1y + c4 * t2y;
      }
    }
  }

  // add last point
  l = points.length - 2;
  res[rPos++] = points[l];
  res[rPos] = points[l + 1];

  return res;
}
<|endoftext|>


<commit_before>/*
Name:           geoip2ws - geoip2ws.js
Description:    Maxmind GeoIP2 Web Services for node.js (unofficial)
Author:         Franklin van de Meent (https://frankl.in)
Source & docs:  https://github.com/fvdm/nodejs-geoip2ws
Feedback:       https://github.com/fvdm/nodejs-geoip2ws/issues
License:        Unlicense (public domain, see LICENSE file)
*/

const { isIP } = require ('net');
const { doRequest } = require ('httpreq');

// setup
let api = {
  userId: null,
  licenseKey: null,
  service: 'city',
  endpoint: 'https://geoip.maxmind.com/geoip/v2.1/',
  requestTimeout: 5000,
};

function isService (service) {
  return !!/^(country|city|insights)$/.test (service);
}

/**
 * Call back an error
 *
 * @return  {Error}            The new error
 *
 * @param   {string}  message  `Error.message`
 * @param   {mixed}   err      `Error.error`
 * @param   {mixed}   code     `data.code` or `res.statusCode`
 */

function doError (message, err, code = null) {
  const error = new Error (message);

  error.code = code;
  error.error = err;
  return error;
}

/**
 * Promisify doRequest without deps
 *
 * @param   {object}   props  httpreq.doRequest options
 * @return  {Promise}
 */

function get (options) {
  return new Promise ((resolve, reject) => {
    doRequest (options, (err, res) => {
      if (err) {
        reject (err);
        return;
      }

      resolve (res);
    });
  });
}

/**
 * Process response body
 *
 * @param    {object}    res    httpreq response
 *
 * @return   {promise}
 * @promise  {object}   resolve  Result data
 * @promose  {error}    reject   API error
 */

function doResponse (res) {
  return new Promise ((resolve, reject) => {
    const data = JSON.parse (res.body);

    if (data.error) {
      reject (doError ('API error', data.error, data.code));
      return;
    }

    if (Array.isArray (data.subdivisions) && data.subdivisions.length) {
      data.most_specific_subdivision = data.subdivisions [data.subdivisions.length - 1];
    } else {
      data.subdivisions = [];
    }

    resolve (data);
  });
}

/**
 * Perform lookup
 *
 * @callback  [callback]
 * @return    {function|promise}                 doLookup()
 *
 * @promise   {object}    resolve                Response data
 * @promise   {error}     reject                 Agent or API error
 *
 * @param     {string}    [service=api.service]  Temporary service override
 * @param     {string}    [ip]                   IP-address, hostname or `me` to look up
 * @param     {function}  [callback]             `(err, data)`
 */

function doLookup (service, ip = null, callback = null) {
  let error;

  const httpProps = {
    method: 'GET',
    auth: `${api.userId}:${api.licenseKey}`,
    timeout: api.requestTimeout,
    headers: {
      'User-Agent': 'geoip2ws.js (https://github.com/fvdm/nodejs-geoip2ws)',
    },
  };

  // fix arguments
  if (service instanceof Object) {
    // object with details
    callback = ip;
    ip = service.ip;
    service = service.service || api.service;
  } else if (isIP (service) || (!isService (service) && !isIP (ip))) {
    // service is optional
    callback = ip;
    ip = service;
    service = api.service;
  }

  // check input
  if (!/^(country|city|insights)$/.test (service)) {
    error = new Error ('invalid service');
  }

  if (ip !== 'me' && !isIP (ip)) {
    error = new Error ('invalid ip');
  }

  // build request
  httpProps.url = `${api.endpoint}${service}/${ip}`;
  httpProps.headers.Accept = `application/vnd.maxmind.com-${service}+json; charset=UTF-8; version=2.1`;

  // do callback
  if (typeof callback === 'function') {
    if (error) {
      callback (error);
      return doLookup;
    }

    get (httpProps)
      .then (doResponse)
      .then (data => callback (null, data))
      .catch (err => {
        if (err.message === 'API error') {
          callback (err);
        } else {
          callback (doError ('request failed', err, err.code));
        }
      })
    ;

    return doLookup;
  }

  // do promise
  return new Promise ((resolve, reject) => {
    if (error) {
      reject (error);
      return;
    }

    get (httpProps)
      .then (doResponse)
      .then (resolve)
      .catch (reject)
    ;
  });
}

/**
 * Module interface
 *
 * @return  {function}                  doLookup()
 *
 * @param   {string}    userId          Account user ID
 * @param   {string}    licenseKey      Account license key
 * @param   {string}    [service=city]  Account service name
 * @param   {number}    [timeout=5000]  Request time out in milliseconds
 */

function setup (userId, licenseKey, service, timeout) {
  if (userId instanceof Object) {
    api = Object.assign (api, userId);
    return doLookup;
  }

  if (typeof service === 'number') {
    timeout = service;
    service = api.service;
  }

  api.userId = userId;
  api.licenseKey = licenseKey;
  api.service = service || api.service;
  api.requestTimeout = timeout || api.requestTimeout;

  return doLookup;
}

module.exports = setup;
<commit_msg>Fixed: most_specific_subdivision is sometimes missing<commit_after>/*
Name:           geoip2ws - geoip2ws.js
Description:    Maxmind GeoIP2 Web Services for node.js (unofficial)
Author:         Franklin van de Meent (https://frankl.in)
Source & docs:  https://github.com/fvdm/nodejs-geoip2ws
Feedback:       https://github.com/fvdm/nodejs-geoip2ws/issues
License:        Unlicense (public domain, see LICENSE file)
*/

const { isIP } = require ('net');
const { doRequest } = require ('httpreq');

// setup
let api = {
  userId: null,
  licenseKey: null,
  service: 'city',
  endpoint: 'https://geoip.maxmind.com/geoip/v2.1/',
  requestTimeout: 5000,
};

function isService (service) {
  return !!/^(country|city|insights)$/.test (service);
}

/**
 * Call back an error
 *
 * @return  {Error}            The new error
 *
 * @param   {string}  message  `Error.message`
 * @param   {mixed}   err      `Error.error`
 * @param   {mixed}   code     `data.code` or `res.statusCode`
 */

function doError (message, err, code = null) {
  const error = new Error (message);

  error.code = code;
  error.error = err;
  return error;
}

/**
 * Promisify doRequest without deps
 *
 * @param   {object}   props  httpreq.doRequest options
 * @return  {Promise}
 */

function get (options) {
  return new Promise ((resolve, reject) => {
    doRequest (options, (err, res) => {
      if (err) {
        reject (err);
        return;
      }

      resolve (res);
    });
  });
}

/**
 * Process response body
 *
 * @param    {object}    res    httpreq response
 *
 * @return   {promise}
 * @promise  {object}   resolve  Result data
 * @promose  {error}    reject   API error
 */

function doResponse (res) {
  return new Promise ((resolve, reject) => {
    const data = JSON.parse (res.body);

    if (data.error) {
      reject (doError ('API error', data.error, data.code));
      return;
    }

    if (Array.isArray (data.subdivisions) && data.subdivisions.length) {
      data.most_specific_subdivision = data.subdivisions [data.subdivisions.length - 1];
    } else {
      data.subdivisions = [];
    }

    if (!data.most_specific_subdivision) {
      data.most_specific_subdivision = {};
    }

    resolve (data);
  });
}

/**
 * Perform lookup
 *
 * @callback  [callback]
 * @return    {function|promise}                 doLookup()
 *
 * @promise   {object}    resolve                Response data
 * @promise   {error}     reject                 Agent or API error
 *
 * @param     {string}    [service=api.service]  Temporary service override
 * @param     {string}    [ip]                   IP-address, hostname or `me` to look up
 * @param     {function}  [callback]             `(err, data)`
 */

function doLookup (service, ip = null, callback = null) {
  let error;

  const httpProps = {
    method: 'GET',
    auth: `${api.userId}:${api.licenseKey}`,
    timeout: api.requestTimeout,
    headers: {
      'User-Agent': 'geoip2ws.js (https://github.com/fvdm/nodejs-geoip2ws)',
    },
  };

  // fix arguments
  if (service instanceof Object) {
    // object with details
    callback = ip;
    ip = service.ip;
    service = service.service || api.service;
  } else if (isIP (service) || (!isService (service) && !isIP (ip))) {
    // service is optional
    callback = ip;
    ip = service;
    service = api.service;
  }

  // check input
  if (!/^(country|city|insights)$/.test (service)) {
    error = new Error ('invalid service');
  }

  if (ip !== 'me' && !isIP (ip)) {
    error = new Error ('invalid ip');
  }

  // build request
  httpProps.url = `${api.endpoint}${service}/${ip}`;
  httpProps.headers.Accept = `application/vnd.maxmind.com-${service}+json; charset=UTF-8; version=2.1`;

  // do callback
  if (typeof callback === 'function') {
    if (error) {
      callback (error);
      return doLookup;
    }

    get (httpProps)
      .then (doResponse)
      .then (data => callback (null, data))
      .catch (err => {
        if (err.message === 'API error') {
          callback (err);
        } else {
          callback (doError ('request failed', err, err.code));
        }
      })
    ;

    return doLookup;
  }

  // do promise
  return new Promise ((resolve, reject) => {
    if (error) {
      reject (error);
      return;
    }

    get (httpProps)
      .then (doResponse)
      .then (resolve)
      .catch (reject)
    ;
  });
}

/**
 * Module interface
 *
 * @return  {function}                  doLookup()
 *
 * @param   {string}    userId          Account user ID
 * @param   {string}    licenseKey      Account license key
 * @param   {string}    [service=city]  Account service name
 * @param   {number}    [timeout=5000]  Request time out in milliseconds
 */

function setup (userId, licenseKey, service, timeout) {
  if (userId instanceof Object) {
    api = Object.assign (api, userId);
    return doLookup;
  }

  if (typeof service === 'number') {
    timeout = service;
    service = api.service;
  }

  api.userId = userId;
  api.licenseKey = licenseKey;
  api.service = service || api.service;
  api.requestTimeout = timeout || api.requestTimeout;

  return doLookup;
}

module.exports = setup;
<|endoftext|>


<commit_before>

var DAryHeap = function ( arity, compare ) {

	// arity of this heap

	this.arity = arity;

	// the comparison function

	this.compare = function ( a, b ) {
		return compare( a.value, b.value );
	};

	// array used to store values

	this.array = [];

	// size of the heap

	this.length = 0;

};

DAryHeap.Reference = function ( index, value ) {

	this.index = index;
	this.value = value;

};

DAryHeap.prototype.swap = function ( a, i, j ) {

	var tmp;

	tmp = a[i];
	a[i] = a[j];
	a[j] = tmp;

	a[i].index = i;
	a[j].index = j;

};

DAryHeap.prototype.head = function () {

	if ( this.length === 0 ) {
		return undefined;
	}

	return this.array[0].value;

};

DAryHeap.prototype.pop = function () {

	var a, i, j, reference;

	if ( this.length === 0 ) {
		return undefined;
	}

	a = this.array;
	i = 0;
	j = a.length;

	reference = daryheap.pop( this.arity, this.compare, this.swap, a, i, j );

	a.pop();

	--this.length;

	return reference.value;

};

DAryHeap.prototype.push = function ( value ) {

	var a, i, j;

	a = this.array;
	i = 0;
	j = a.length;

	a.push( new DAryHeap.Reference( j, value ) );

	daryheap.push( this.arity, this.compare, this.swap, a, i, j );

	++this.length;

};

DAryHeap.prototype.merge = function ( other ) {

	var a, i, j, k;

	a = this.array;
	i = 0;
	j = a.length;

	a = this.array = a.concat( other.array );

	k = a.length;

	daryheap.merge( this.arity, this.compare, this.swap, a, i, j, k );

	this.length += other.length;

};

exports.DAryHeap = DAryHeap;
<commit_msg>drafts for reference-related methods<commit_after>

var DAryHeap = function ( arity, compare ) {

	// arity of this heap

	this.arity = arity;

	// the comparison function

	this.compare = function ( a, b ) {
		return compare( a.value, b.value );
	};

	// array used to store values

	this.array = [];

	// size of the heap

	this.length = 0;

};

DAryHeap.Reference = function ( index, value ) {

	this.index = index;
	this.value = value;

};

DAryHeap.prototype.swap = function ( a, i, j ) {

	var tmp;

	tmp = a[i];
	a[i] = a[j];
	a[j] = tmp;

	a[i].index = i;
	a[j].index = j;

};

DAryHeap.prototype.head = function () {

	if ( this.length === 0 ) {
		return undefined;
	}

	return this.array[0].value;

};

DAryHeap.prototype.headreference = function () {

	if ( this.length === 0 ) {
		return null;
	}

	return this.array[0];

};

DAryHeap.prototype.pop = function () {

	var a, i, j, reference;

	if ( this.length === 0 ) {
		return undefined;
	}

	a = this.array;
	i = 0;
	j = a.length;

	reference = daryheap.pop( this.arity, this.compare, this.swap, a, i, j );

	a.pop();

	--this.length;

	return reference.value;

};

DAryHeap.prototype.popreference = function () {

	var a, i, j, reference;

	if ( this.length === 0 ) {
		return null;
	}

	a = this.array;
	i = 0;
	j = a.length;

	reference = daryheap.pop( this.arity, this.compare, this.swap, a, i, j );

	a.pop();

	--this.length;

	return reference;

};

DAryHeap.prototype.push = function ( value ) {

	var a, i, j;

	a = this.array;
	i = 0;
	j = a.length;

	a.push( new DAryHeap.Reference( j, value ) );

	daryheap.push( this.arity, this.compare, this.swap, a, i, j );

	++this.length;

};

DAryHeap.prototype.pushreference = function ( reference ) {

	var a, i, j;

	a = this.array;
	i = 0;
	j = a.length;

	reference.index = j;
	a.push( reference );

	daryheap.push( this.arity, this.compare, this.swap, a, i, j );

	++this.length;

};

DAryHeap.prototype.merge = function ( other ) {

	var a, i, j, k, t;

	a = this.array;
	i = 0;
	j = a.length;

	// concat arrays of both heaps

	a = this.array = a.concat( other.array );

	k = a.length;

	// update index of concatenated elements

	for ( t = j ; t < k ; ++t ) {
		a[t].index = t;
	}

	daryheap.merge( this.arity, this.compare, this.swap, a, i, j, k );

	this.length += other.length;

};

DAryHeap.prototype.update = function ( reference, value ) {

	var d;

	d = this.compare( value, reference.value );

	if ( d < 0 ) {
		this.decreasekey( reference, value );
	}

	else if ( d > 0 ) {
		this.increasekey( reference, value );
	}

	else {

		// d === 0 does not imply reference.value === value

		reference.value = value;

	}

};

DAryHeap.prototype.decreasekey = function ( reference, value ) {

	var a, i, j, k;

	a = this.array;
	i = 0;
	j = a.length;
	k = reference.index;

	reference.value = value;

	daryheap.percolateup( this.arity, this.compare, this.swap, a, i, j, k );

};

DAryHeap.prototype.increasekey = function ( reference, value ) {

	var a, i, j, k;

	a = this.array;
	i = 0;
	j = a.length;
	k = reference.index;

	reference.value = value;

	daryheap.percolatedown( this.arity, this.compare, this.swap, a, i, j, k );

};

DAryHeap.prototype.delete = function ( reference ) {

	var a, i, j, k;

	if ( this.length === 0 ) {
		return;
	}

	a = this.array;
	i = 0;
	j = a.length;
	k = reference.index;

	daryheap.delete( this.arity, this.compare, this.swap, a, i, j, k );

	--this.length;

};

exports.DAryHeap = DAryHeap;
<|endoftext|>


<commit_before>define([
    'jquery',
    'backbone',
    'collections/DateSummaryCollection',
    'collections/CommunityAreaCollection',
    'views/PageView',
    'views/CommunityAreaListView',
    'views/CommunityAreaDetailView',
    'views/DocListView',
    'views/DocDetailView',
    'text!templates/summary.jst'
],
function($, Backbone, DateSummaryCollection, CommunityAreaCollection, PageView,
         CommunityAreaListView, CommunityAreaDetailView, DocListView, DocDetailView,
         SummaryTemplate) {

    var CommunityAreaMonthlySummary = Backbone.View.extend({

        initialize: function() {
            var month, end;

            if ( parseInt(this.options.month, 10) > 9 ) {
                month = this.options.month;
                end = (parseInt(this.options.month, 10) + 1) % 12;
            } else {
                month = '0' + this.options.month;
                end = (parseInt(this.options.month, 10) + 1) % 12;
            }

            if ( parseInt(end, 10) < 9 )
                end = '0' + end;

            this.template = _.template(SummaryTemplate);

            this.summary = new DateSummaryCollection();
            this.summary.fetch({
                data: {
                    'community_area': this.options.community,
                    'related': 1,
                    'crime_date__gte': '2013-' + month  + '-01 00:00',
                    'crime_date__lt': '2013-' + end + '-01 00:00',
                    'limit': 0
                },
                success: _.bind(function() {
                    this.prior_year = new DateSummaryCollection();
                    this.prior_year.fetch({
                        data: {
                            'community_area': this.options.community,
                            'related': 1,
                            'crime_date__gte': '2012-' + month  + '-01 00:00',
                            'crime_date__lt': '2012-' + end + '-01 00:00',
                            'limit': 0
                        },
                        success: _.bind(function() {
                            this.community_areas = new CommunityAreaCollection();
                            this.community_areas.fetch({
                                success: _.bind(this.render, this)
                            });
                        }, this)
                    });
                }, this)
            });

            return this;
        },

        render: function() {
            var month = this.sums(this.summary),
                prior = this.sums(this.prior_year),
                community = this.community_areas.filter(_.bind(function(x) {
                    if ( x.get('area_number') == this.options.community )
                        return x;
                }, this))[0];

            this.$el.append(this.template({
                community: community.attributes,
                month: month,
                prior: prior,
                totals: this.total_crimes(month),
                totals_prior: this.total_crimes(prior)
            }));

            $('#content').append(this.$el);
        },

        sums: function(collection) {
            var crime_spec = {
                crimes: ["arson", "assault", "battery", "burglary",
                    "criminal_damage", "homicide", "motor_vehicle_theft",
                    "narcotics", "prostitution", "robbery", "sexual_assault",
                    "theft"],
                violent_crimes: ["robbery", "battery", "assault", "homicide",
                    "sexual_assault"],
                property_crimes: ["theft", "burglary", "motor_vehicle_theft",
                    "arson"],
                quality_of_life_crimes: ["criminal_damage", "narcotics",
                "prostitution"]
            }

            var sums = {};

            collection.each(function(val, idx) {
                _.each(crime_spec, function(subtypes, type){
                    _.each(subtypes, function(subtype) {
                        if ( !sums[subtype] ) {
                            sums[type] = parseInt(val.get(subtype), 10);
                        } else {
                            sums[type] += parseInt(val.get(subtype), 10);
                        }
                    });
                })
            });

            return sums;
        },

        total_crimes: function(sums_obj) {
            var total = 0;

            _.each(_.values(sums_obj), function(v) {
                total += v;
            });

            return total;
        }

    });

    return CommunityAreaMonthlySummary;

});
<commit_msg>Better<commit_after>define([
    'jquery',
    'backbone',
    'collections/DateSummaryCollection',
    'collections/CommunityAreaCollection',
    'views/PageView',
    'views/CommunityAreaListView',
    'views/CommunityAreaDetailView',
    'views/DocListView',
    'views/DocDetailView',
    'text!templates/summary.jst'
],
function($, Backbone, DateSummaryCollection, CommunityAreaCollection, PageView,
         CommunityAreaListView, CommunityAreaDetailView, DocListView, DocDetailView,
         SummaryTemplate) {

    var CommunityAreaMonthlySummary = Backbone.View.extend({

        initialize: function() {
            var month, end;

            if ( parseInt(this.options.month, 10) > 9 ) {
                month = this.options.month;
                end = (parseInt(this.options.month, 10) + 1) % 12;
            } else {
                month = '0' + this.options.month;
                end = (parseInt(this.options.month, 10) + 1) % 12;
            }

            if ( parseInt(end, 10) < 9 )
                end = '0' + end;

            this.template = _.template(SummaryTemplate);

            this.summary = new DateSummaryCollection();
            this.summary.fetch({
                data: {
                    'community_area': this.options.community,
                    'related': 1,
                    'crime_date__gte': '2013-' + month  + '-01 00:00',
                    'crime_date__lt': '2013-' + end + '-01 00:00',
                    'limit': 0
                },
                success: _.bind(function() {
                    this.prior_year = new DateSummaryCollection();
                    this.prior_year.fetch({
                        data: {
                            'community_area': this.options.community,
                            'related': 1,
                            'crime_date__gte': '2012-' + month  + '-01 00:00',
                            'crime_date__lt': '2012-' + end + '-01 00:00',
                            'limit': 0
                        },
                        success: _.bind(function() {
                            this.community_areas = new CommunityAreaCollection();
                            this.community_areas.fetch({
                                success: _.bind(this.render, this)
                            });
                        }, this)
                    });
                }, this)
            });

            return this;
        },

        render: function() {
            var month = this.sums(this.summary),
                prior = this.sums(this.prior_year),
                community = this.community_areas.find(_.bind(function(x) {
                    if ( x.get('area_number') == this.options.community )
                        return x;
                }, this));

            this.$el.append(this.template({
                community: community.attributes,
                month: month,
                prior: prior,
                totals: this.total_crimes(month),
                totals_prior: this.total_crimes(prior)
            }));

            $('#content').append(this.$el);
        },

        sums: function(collection) {
            var crime_spec = {
                crimes: ["arson", "assault", "battery", "burglary",
                    "criminal_damage", "homicide", "motor_vehicle_theft",
                    "narcotics", "prostitution", "robbery", "sexual_assault",
                    "theft"],
                violent_crimes: ["robbery", "battery", "assault", "homicide",
                    "sexual_assault"],
                property_crimes: ["theft", "burglary", "motor_vehicle_theft",
                    "arson"],
                quality_of_life_crimes: ["criminal_damage", "narcotics",
                "prostitution"]
            }

            var sums = {};

            collection.each(function(val, idx) {
                _.each(crime_spec, function(subtypes, type){
                    _.each(subtypes, function(subtype) {
                        if ( !sums[subtype] ) {
                            sums[type] = parseInt(val.get(subtype), 10);
                        } else {
                            sums[type] += parseInt(val.get(subtype), 10);
                        }
                    });
                })
            });

            return sums;
        },

        total_crimes: function(sums_obj) {
            var total = 0;

            _.each(_.values(sums_obj), function(v) {
                total += v;
            });

            return total;
        }

    });

    return CommunityAreaMonthlySummary;

});
<|endoftext|>


<commit_before>(function (enyo) {

	var observerTable = {};

	var ObserverChain = enyo.ObserverChain
		, ObserverSupport;

	enyo.concatenated.push("observers");

	/**
		@private
	*/
	function addObserver (path, fn, ctx, opts) {

		var observers = this.getObservers(),
			chains = this.getChains(),
			parts = path.split('.'),
			prio = opts && opts.priority,
			entries,
			noChain;

		noChain = (opts && opts.noChain) ||
				chains[path] ||
				parts.length < 2 ||
				(parts.length === 2 && path[0] == '$');

		if (observers[path] && !observers.hasOwnProperty(path)) {
			observers[path] = observers[path].slice();
		}

		entries = observers[path] || (observers[path] = []);
		entries[prio ? 'unshift' : 'push']({method: fn, ctx: ctx || this});

		if (!noChain) {
			this.getChains()[path] = new ObserverChain(path, this);
		}

		return this;
	}

	/**
		@private
	*/
	function removeObserver (obj, path, fn, ctx) {
		var observers = obj.getObservers(path)
			, chains = obj.getChains()
			, idx, chain;

		if (observers && observers.length) {
			idx = observers.findIndex(function (ln) {
				return ln.method === fn && (ctx? ln.ctx === ctx: true);
			});
			idx > -1 && observers.splice(idx, 1);
		}

		if ((chain = chains[path]) && !observers.length) {
			chain.destroy();
		}

		return obj;
	}

	/**
		@private
	*/
	function notifyObservers (obj, path, was, is, opts) {
		if (obj.isObserving()) {
			var observers = obj.getObservers(path);

			if (observers && observers.length) {
				for (var i=0, ln; (ln=observers[i]); ++i) {
					if (typeof ln.method == "string") obj[ln.method](was, is, path, opts);
					else ln.method.call(ln.ctx || obj, was, is, path, opts);
				}
			}
		} else enqueue(obj, path, was, is, opts);

		return obj;
	}

	/**
		@private
	*/
	function enqueue (obj, path, was, is, opts) {
		if (obj._notificationQueueEnabled) {
			var queue = obj._notificationQueue || (obj._notificationQueue = {})
				, ln = queue[path] || (queue[path] = {});

			ln.was = was;
			ln.is = is;
			ln.opts = opts;
		}
	}

	/**
		@private
	*/
	function flushQueue (obj) {
		var queue = obj._notificationQueue
			, path, ln;

		if (queue) {
			obj._notificationQueue = null;

			for (path in queue) {
				ln = queue[path];
				obj.notify(path, ln.was, ln.is, ln.opts);
			}
		}
	}

	/**
		@public
		@mixin
	*/
	ObserverSupport = enyo.ObserverSupport = {
		name: "ObserverSupport",

		/**
			@private
		*/
		_observing: true,

		/**
			@private
		*/
		_observeCount: 0,

		/**
			@private
		*/
		_notificationQueue: null,

		/**
			@private
		*/
		_notificationQueueEnabled: true,

		/**
			@public
			@method
		*/
		isObserving: function () {
			return this._observing;
		},

		/**
			@public
			@method
		*/
		getObservers: function (path) {
			var euid = this.euid || (this.euid = enyo.uid('o')),
				ret,
				loc;

			loc = observerTable[euid] || (observerTable[euid] = (
				this._observers? Object.create(this._observers): {}
			));

			if (!path) return loc;

			ret = loc[path];

			// if the special property exists...
			if (loc['*']) ret = ret ? ret.concat(loc['*']) : loc['*'].slice();
			return ret;
		},

		/**
			@private
			@method
		*/
		getChains: function () {
			return this._observerChains || (this._observerChains = {});
		},

		/**
			@public
			@method
		*/
		addObserver: function () {
			// @NOTE: In this case we use apply because of internal variable use of parameters
			return addObserver.apply(this, arguments);
		},

		/**
			@public
			@method
			@alias addObserver
		*/
		observe: function () {
			// @NOTE: In this case we use apply because of internal variable use of parameters
			return addObserver.apply(this, arguments);
		},

		/**
			@public
			@method
		*/
		removeObserver: function (path, fn, ctx) {
			return removeObserver(this, path, fn);
		},

		/**
			@public
			@method
			@alias removeObserver
		*/
		unobserve: function (path, fn, ctx) {
			return removeObserver(this, path, fn, ctx);
		},

		/**
			@public
			@method
		*/
		removeAllObservers: function (path) {
			var euid = this.euid
				, loc = euid && observerTable[euid];

			if (loc) {
				if (path) {
					loc[path] = null;
				} else {
					observerTable[euid] = null;
				}
			}

			return this;
		},

		/**
			@public
			@method
		*/
		notifyObservers: function (path, was, is, opts) {
			return notifyObservers(this, path, was, is, opts);
		},

		/**
			@public
			@method
			@alias notifyObservers
		*/
		notify: function (path, was, is, opts) {
			return notifyObservers(this, path, was, is, opts);
		},

		/**
			@public
			@method
		*/
		stopNotifications: function (noQueue) {
			this._observing = false;
			this._observeCount++;
			noQueue && this.disableNotificationQueue();
			return this;
		},

		/**
			@public
			@method
		*/
		startNotifications: function (queue) {
			this._observeCount && this._observeCount--;
			this._observeCount === 0 && (this._observing = true);
			queue && this.enableNotificationQueue();
			this.isObserving() && flushQueue(this);
			return this;
		},

		/**
			@public
			@method
		*/
		enableNotificationQueue: function () {
			this._notificationQueueEnabled = true;
			return this;
		},

		/**
			@public
			@method
		*/
		disableNotificationQueue: function () {
			this._notificationQueueEnabled = false;
			this._notificationQueue = null;
			return this;
		},

		/**
			@private
			@method
		*/
		constructor: enyo.inherit(function (sup) {
			return function () {
				var chains, chain;

				// if there are any observers that need to create dynamic chains
				// we look for and instance those now
				if (this._observerChains) {
					chains = this._observerChains;
					this._observerChains = {};
					for (var i=0; (chain=chains[i]); ++i) this.observe(chain.path, chain.method);
				}

				sup.apply(this, arguments);
			};
		}),

		/**
			@private
			@method
		*/
		destroy: enyo.inherit(function (sup) {
			return function () {
				var chains = this._observerChains,
					path,
					chain;

				sup.apply(this, arguments);

				if (chains) {
					for (path in chains) {
						chain = chains[path];
						chain.destroy();
					}

					this._observerChains = null;
				}
			};
		})

	};

	/**
		Hijack the original so we can add additional default behavior.
	*/
	var sup = enyo.concatHandler;

	// @NOTE: It seems like a lot of work but it really won't happen that much and the more
	// we push to kind-time the better for initialization time
	enyo.concatHandler = function (ctor, props, instance) {

		sup.call(this, ctor, props, instance);

		if (props === ObserverSupport) return;

		var proto = ctor.prototype || ctor
			, observers = proto._observers? Object.create(proto._observers): null
			, incoming = props.observers
			, chains = proto._observerChains && proto._observerChains.slice();

		if (!observers) {
			if (proto.kindName) observers = {};
			else return;
		}

		if (incoming && !(incoming instanceof Array)) {
			(function () {
				var tmp = [], deps, name;
				// here is the slow iteration over the properties...
				for (name in props.observers) {
					// points to the dependencies of the computed method
					deps = props.observers[name];
					// create a single entry now for the method/computed with all dependencies
					tmp.push({method: name, path: deps});
				}
				incoming = tmp;
			}());
			// we need to ensure we don't modify the fixed array of a mixin or reused object
			// because it could wind up inadvertantly adding the same entry multiple times
		} else if (incoming) incoming = incoming.slice();

		// this scan is required to figure out what auto-observers might be present
		for (var key in props) {
			if (key.slice(-7) == "Changed") {
				incoming || (incoming = []);
				incoming.push({method: key, path: key.slice(0, -7)});
			}
		}

		var addObserverEntry = function (path, method) {
			var obs;
			// we have to make sure that the path isn't a chain because if it is we add it
			// to the chains instead
			if (path.indexOf(".") > -1) (chains || (chains = [])).push({path: path, method: method});
			else {
				if (observers[path] && !observers.hasOwnProperty(path)) observers[path] = observers[path].slice();
				obs = observers[path] || (observers[path] = []);
				if (!obs.find(function (ln) { return ln.method == method; })) obs.push({method: method});
			}
		};

		if (incoming) {
			incoming.forEach(function (ln) {
				// first we determine if the path itself is an array of paths to observe
				if (ln.path && ln.path instanceof Array) ln.path.forEach(function (en) { addObserverEntry(en, ln.method); });
				else addObserverEntry(ln.path, ln.method);
			});
		}

		// we clear the key so it will not be added to the prototype
		// delete props.observers;
		// we update the properties to whatever their new values may be
		proto._observers = observers;
		proto._observerChains = chains;
	};

})(enyo);<commit_msg>fix mismatch for chains now always use object literal instead of array<commit_after>(function (enyo) {

	var observerTable = {};

	var ObserverChain = enyo.ObserverChain
		, ObserverSupport;

	enyo.concatenated.push("observers");

	/**
		@private
	*/
	function addObserver (path, fn, ctx, opts) {

		var observers = this.getObservers(),
			chains = this.getChains(),
			parts = path.split('.'),
			prio = opts && opts.priority,
			entries,
			noChain;

		noChain = (opts && opts.noChain) ||
				chains[path] ||
				parts.length < 2 ||
				(parts.length === 2 && path[0] == '$');

		if (observers[path] && !observers.hasOwnProperty(path)) {
			observers[path] = observers[path].slice();
		}

		entries = observers[path] || (observers[path] = []);
		entries[prio ? 'unshift' : 'push']({method: fn, ctx: ctx || this});

		if (!noChain) {
			this.getChains()[path] = new ObserverChain(path, this);
		}

		return this;
	}

	/**
		@private
	*/
	function removeObserver (obj, path, fn, ctx) {
		var observers = obj.getObservers(path)
			, chains = obj.getChains()
			, idx, chain;

		if (observers && observers.length) {
			idx = observers.findIndex(function (ln) {
				return ln.method === fn && (ctx? ln.ctx === ctx: true);
			});
			idx > -1 && observers.splice(idx, 1);
		}

		if ((chain = chains[path]) && !observers.length) {
			chain.destroy();
		}

		return obj;
	}

	/**
		@private
	*/
	function notifyObservers (obj, path, was, is, opts) {
		if (obj.isObserving()) {
			var observers = obj.getObservers(path);

			if (observers && observers.length) {
				for (var i=0, ln; (ln=observers[i]); ++i) {
					if (typeof ln.method == "string") obj[ln.method](was, is, path, opts);
					else ln.method.call(ln.ctx || obj, was, is, path, opts);
				}
			}
		} else enqueue(obj, path, was, is, opts);

		return obj;
	}

	/**
		@private
	*/
	function enqueue (obj, path, was, is, opts) {
		if (obj._notificationQueueEnabled) {
			var queue = obj._notificationQueue || (obj._notificationQueue = {})
				, ln = queue[path] || (queue[path] = {});

			ln.was = was;
			ln.is = is;
			ln.opts = opts;
		}
	}

	/**
		@private
	*/
	function flushQueue (obj) {
		var queue = obj._notificationQueue
			, path, ln;

		if (queue) {
			obj._notificationQueue = null;

			for (path in queue) {
				ln = queue[path];
				obj.notify(path, ln.was, ln.is, ln.opts);
			}
		}
	}

	/**
		@public
		@mixin
	*/
	ObserverSupport = enyo.ObserverSupport = {
		name: "ObserverSupport",

		/**
			@private
		*/
		_observing: true,

		/**
			@private
		*/
		_observeCount: 0,

		/**
			@private
		*/
		_notificationQueue: null,

		/**
			@private
		*/
		_notificationQueueEnabled: true,

		/**
			@public
			@method
		*/
		isObserving: function () {
			return this._observing;
		},

		/**
			@public
			@method
		*/
		getObservers: function (path) {
			var euid = this.euid || (this.euid = enyo.uid('o')),
				ret,
				loc;

			loc = observerTable[euid] || (observerTable[euid] = (
				this._observers? Object.create(this._observers): {}
			));

			if (!path) return loc;

			ret = loc[path];

			// if the special property exists...
			if (loc['*']) ret = ret ? ret.concat(loc['*']) : loc['*'].slice();
			return ret;
		},

		/**
			@private
			@method
		*/
		getChains: function () {
			return this._observerChains || (this._observerChains = {});
		},

		/**
			@public
			@method
		*/
		addObserver: function () {
			// @NOTE: In this case we use apply because of internal variable use of parameters
			return addObserver.apply(this, arguments);
		},

		/**
			@public
			@method
			@alias addObserver
		*/
		observe: function () {
			// @NOTE: In this case we use apply because of internal variable use of parameters
			return addObserver.apply(this, arguments);
		},

		/**
			@public
			@method
		*/
		removeObserver: function (path, fn, ctx) {
			return removeObserver(this, path, fn);
		},

		/**
			@public
			@method
			@alias removeObserver
		*/
		unobserve: function (path, fn, ctx) {
			return removeObserver(this, path, fn, ctx);
		},

		/**
			@public
			@method
		*/
		removeAllObservers: function (path) {
			var euid = this.euid
				, loc = euid && observerTable[euid];

			if (loc) {
				if (path) {
					loc[path] = null;
				} else {
					observerTable[euid] = null;
				}
			}

			return this;
		},

		/**
			@public
			@method
		*/
		notifyObservers: function (path, was, is, opts) {
			return notifyObservers(this, path, was, is, opts);
		},

		/**
			@public
			@method
			@alias notifyObservers
		*/
		notify: function (path, was, is, opts) {
			return notifyObservers(this, path, was, is, opts);
		},

		/**
			@public
			@method
		*/
		stopNotifications: function (noQueue) {
			this._observing = false;
			this._observeCount++;
			noQueue && this.disableNotificationQueue();
			return this;
		},

		/**
			@public
			@method
		*/
		startNotifications: function (queue) {
			this._observeCount && this._observeCount--;
			this._observeCount === 0 && (this._observing = true);
			queue && this.enableNotificationQueue();
			this.isObserving() && flushQueue(this);
			return this;
		},

		/**
			@public
			@method
		*/
		enableNotificationQueue: function () {
			this._notificationQueueEnabled = true;
			return this;
		},

		/**
			@public
			@method
		*/
		disableNotificationQueue: function () {
			this._notificationQueueEnabled = false;
			this._notificationQueue = null;
			return this;
		},

		/**
			@private
			@method
		*/
		constructor: enyo.inherit(function (sup) {
			return function () {
				var chains, chain, path, entries, i;

				// if there are any observers that need to create dynamic chains
				// we look for and instance those now
				if (this._observerChains) {
					chains = this._observerChains;
					this._observerChains = {};
					for (path in chains) {
						entries = chains[path];
						for (i = 0; (chain = entries[i]); ++i) this.observe(path, chain.method);
					}
				}

				sup.apply(this, arguments);
			};
		}),

		/**
			@private
			@method
		*/
		destroy: enyo.inherit(function (sup) {
			return function () {
				var chains = this._observerChains,
					path,
					chain;

				sup.apply(this, arguments);

				if (chains) {
					for (path in chains) {
						chain = chains[path];
						chain.destroy();
					}

					this._observerChains = null;
				}
			};
		})

	};

	/**
		Hijack the original so we can add additional default behavior.
	*/
	var sup = enyo.concatHandler;

	// @NOTE: It seems like a lot of work but it really won't happen that much and the more
	// we push to kind-time the better for initialization time
	enyo.concatHandler = function (ctor, props, instance) {

		sup.call(this, ctor, props, instance);

		if (props === ObserverSupport) return;

		var proto = ctor.prototype || ctor
			, observers = proto._observers? Object.create(proto._observers): null
			, incoming = props.observers
			, chains = proto._observerChains && Object.create(proto._observerChains);

		if (!observers) {
			if (proto.kindName) observers = {};
			else return;
		}

		if (incoming && !(incoming instanceof Array)) {
			(function () {
				var tmp = [], deps, name;
				// here is the slow iteration over the properties...
				for (name in props.observers) {
					// points to the dependencies of the computed method
					deps = props.observers[name];
					// create a single entry now for the method/computed with all dependencies
					tmp.push({method: name, path: deps});
				}
				incoming = tmp;
			}());
			// we need to ensure we don't modify the fixed array of a mixin or reused object
			// because it could wind up inadvertantly adding the same entry multiple times
		} else if (incoming) incoming = incoming.slice();

		// this scan is required to figure out what auto-observers might be present
		for (var key in props) {
			if (key.slice(-7) == "Changed") {
				incoming || (incoming = []);
				incoming.push({method: key, path: key.slice(0, -7)});
			}
		}

		var addObserverEntry = function (path, method) {
			var obs;
			// we have to make sure that the path isn't a chain because if it is we add it
			// to the chains instead
			if (path.indexOf(".") > -1) {
				if (!chains) chains = {};
				obs = chains[path] || (chains[path] = []);
				obs.push({method: method});
			} else {
				if (observers[path] && !observers.hasOwnProperty(path)) observers[path] = observers[path].slice();
				obs = observers[path] || (observers[path] = []);
				if (!obs.find(function (ln) { return ln.method == method; })) obs.push({method: method});
			}
		};

		if (incoming) {
			incoming.forEach(function (ln) {
				// first we determine if the path itself is an array of paths to observe
				if (ln.path && ln.path instanceof Array) ln.path.forEach(function (en) { addObserverEntry(en, ln.method); });
				else addObserverEntry(ln.path, ln.method);
			});
		}

		// we clear the key so it will not be added to the prototype
		// delete props.observers;
		// we update the properties to whatever their new values may be
		proto._observers = observers;
		proto._observerChains = chains;
	};

})(enyo);<|endoftext|>


<commit_before>/*
* Copyright 2004 ThoughtWorks, Inc
*
*  Licensed under the Apache License, Version 2.0 (the "License");
*  you may not use this file except in compliance with the License.
*  You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
*  Unless required by applicable law or agreed to in writing, software
*  distributed under the License is distributed on an "AS IS" BASIS,
*  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*  See the License for the specific language governing permissions and
*  limitations under the License.
*/

function TestLoop(commandFactory) {
    this.commandFactory = commandFactory;
}

TestLoop.prototype = {

    start : function() {
        selenium.reset();
        LOG.debug("currentTest.start()");
        this.continueTest();
    },

    continueTest : function() {
        /**
         * Select the next command and continue the test.
         */
        LOG.debug("currentTest.continueTest() - acquire the next command");
        if (! this.aborted) {
            this.currentCommand = this.nextCommand();
        }
        if (! this.requiresCallBack) {
            this.continueTestAtCurrentCommand();
        } // otherwise, just finish and let the callback invoke continueTestAtCurrentCommand()
    },

    continueTestAtCurrentCommand : function() {
        LOG.debug("currentTest.continueTestAtCurrentCommand()");
        if (this.currentCommand) {
            // TODO: rename commandStarted to commandSelected, OR roll it into nextCommand
            this.commandStarted(this.currentCommand);
            this._resumeAfterDelay();
        } else {
            this._testComplete();
        }
    },

    _resumeAfterDelay : function() {
        /**
         * Pause, then execute the current command.
         */

        // Get the command delay. If a pauseInterval is set, use it once
        // and reset it.  Otherwise, use the defined command-interval.
        var delay = this.pauseInterval || this.getCommandInterval();
        this.pauseInterval = undefined;

        if (this.currentCommand.isBreakpoint || delay < 0) {
            // Pause: enable the "next/continue" button
            this.pause();
        } else {
            window.setTimeout(fnBind(this.resume, this), delay);
        }
    },

    resume: function() {
        /**
         * Select the next command and continue the test.
         */
        LOG.debug("currentTest.resume() - actually execute");
        try {
            selenium.browserbot.runScheduledPollers();
            this._executeCurrentCommand();
            this.continueTestWhenConditionIsTrue();
        } catch (e) {
            if (!this._handleCommandError(e)) {
                this.testComplete();
            } else {
                this.continueTest();
            }
        }
    },

    _testComplete : function() {
        selenium.ensureNoUnhandledPopups();
        this.testComplete();
    },

    _executeCurrentCommand : function() {
        /**
         * Execute the current command.
         *
         * @return a function which will be used to determine when
         * execution can continue, or null if we can continue immediately
         */
        var command = this.currentCommand;
        LOG.info("Executing: |" + command.command + " | " + command.target + " | " + command.value + " |");

        var handler = this.commandFactory.getCommandHandler(command.command);
        if (handler == null) {
            throw new SeleniumError("Unknown command: '" + command.command + "'");
        }

        command.target = selenium.preprocessParameter(command.target);
        command.value = selenium.preprocessParameter(command.value);
        LOG.debug("Command found, going to execute " + command.command);
        this.result = handler.execute(selenium, command);

        this.waitForCondition = this.result.terminationCondition;

    },

    _handleCommandError : function(e) {
        if (!e.isSeleniumError) {
            LOG.exception(e);
            var msg = "Selenium failure. Please report to the Selenium Users forum at http://forums.openqa.org, with error details from the log window.";
            msg += "  The error message is: " + extractExceptionMessage(e);
            return this.commandError(msg);
        } else {
            LOG.error(e.message);
            return this.commandError(e.message);
        }
    },

    continueTestWhenConditionIsTrue: function () {
        /**
         * Busy wait for waitForCondition() to become true, and then carry
         * on with test.  Fail the current test if there's a timeout or an
         * exception.
         */
        //LOG.debug("currentTest.continueTestWhenConditionIsTrue()");
        selenium.browserbot.runScheduledPollers();
        try {
            if (this.waitForCondition == null) {
                LOG.debug("null condition; let's continueTest()");
                LOG.debug("Command complete");
                this.commandComplete(this.result);
                this.continueTest();
            } else if (this.waitForCondition()) {
                LOG.debug("condition satisfied; let's continueTest()");
                this.waitForCondition = null;
                LOG.debug("Command complete");
                this.commandComplete(this.result);
                this.continueTest();
            } else {
                //LOG.debug("waitForCondition was false; keep waiting!");
                window.setTimeout(fnBind(this.continueTestWhenConditionIsTrue, this), 10);
            }
        } catch (e) {
            this.result = {};
            this.result.failed = true;
            this.result.failureMessage = extractExceptionMessage(e);
            this.commandComplete(this.result);
            this.continueTest();
        }
    },

    pause : function() {},
    nextCommand : function() {},
    commandStarted : function() {},
    commandComplete : function() {},
    commandError : function() {},
    testComplete : function() {},

    getCommandInterval : function() {
        return 0;
    }

}
<commit_msg>* Changed default failure message according to SEL-586<commit_after>/*
* Copyright 2004 ThoughtWorks, Inc
*
*  Licensed under the Apache License, Version 2.0 (the "License");
*  you may not use this file except in compliance with the License.
*  You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
*  Unless required by applicable law or agreed to in writing, software
*  distributed under the License is distributed on an "AS IS" BASIS,
*  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*  See the License for the specific language governing permissions and
*  limitations under the License.
*/

function TestLoop(commandFactory) {
    this.commandFactory = commandFactory;
}

TestLoop.prototype = {

    start : function() {
        selenium.reset();
        LOG.debug("currentTest.start()");
        this.continueTest();
    },

    continueTest : function() {
        /**
         * Select the next command and continue the test.
         */
        LOG.debug("currentTest.continueTest() - acquire the next command");
        if (! this.aborted) {
            this.currentCommand = this.nextCommand();
        }
        if (! this.requiresCallBack) {
            this.continueTestAtCurrentCommand();
        } // otherwise, just finish and let the callback invoke continueTestAtCurrentCommand()
    },

    continueTestAtCurrentCommand : function() {
        LOG.debug("currentTest.continueTestAtCurrentCommand()");
        if (this.currentCommand) {
            // TODO: rename commandStarted to commandSelected, OR roll it into nextCommand
            this.commandStarted(this.currentCommand);
            this._resumeAfterDelay();
        } else {
            this._testComplete();
        }
    },

    _resumeAfterDelay : function() {
        /**
         * Pause, then execute the current command.
         */

        // Get the command delay. If a pauseInterval is set, use it once
        // and reset it.  Otherwise, use the defined command-interval.
        var delay = this.pauseInterval || this.getCommandInterval();
        this.pauseInterval = undefined;

        if (this.currentCommand.isBreakpoint || delay < 0) {
            // Pause: enable the "next/continue" button
            this.pause();
        } else {
            window.setTimeout(fnBind(this.resume, this), delay);
        }
    },

    resume: function() {
        /**
         * Select the next command and continue the test.
         */
        LOG.debug("currentTest.resume() - actually execute");
        try {
            selenium.browserbot.runScheduledPollers();
            this._executeCurrentCommand();
            this.continueTestWhenConditionIsTrue();
        } catch (e) {
            if (!this._handleCommandError(e)) {
                this.testComplete();
            } else {
                this.continueTest();
            }
        }
    },

    _testComplete : function() {
        selenium.ensureNoUnhandledPopups();
        this.testComplete();
    },

    _executeCurrentCommand : function() {
        /**
         * Execute the current command.
         *
         * @return a function which will be used to determine when
         * execution can continue, or null if we can continue immediately
         */
        var command = this.currentCommand;
        LOG.info("Executing: |" + command.command + " | " + command.target + " | " + command.value + " |");

        var handler = this.commandFactory.getCommandHandler(command.command);
        if (handler == null) {
            throw new SeleniumError("Unknown command: '" + command.command + "'");
        }

        command.target = selenium.preprocessParameter(command.target);
        command.value = selenium.preprocessParameter(command.value);
        LOG.debug("Command found, going to execute " + command.command);
        this.result = handler.execute(selenium, command);

        this.waitForCondition = this.result.terminationCondition;

    },

    _handleCommandError : function(e) {
        if (!e.isSeleniumError) {
            LOG.exception(e);
            var msg = "Command execution failure. Please search the forum at http://clearspace.openqa.org for error details from the log window.";
            msg += "  The error message is: " + extractExceptionMessage(e);
            return this.commandError(msg);
        } else {
            LOG.error(e.message);
            return this.commandError(e.message);
        }
    },

    continueTestWhenConditionIsTrue: function () {
        /**
         * Busy wait for waitForCondition() to become true, and then carry
         * on with test.  Fail the current test if there's a timeout or an
         * exception.
         */
        //LOG.debug("currentTest.continueTestWhenConditionIsTrue()");
        selenium.browserbot.runScheduledPollers();
        try {
            if (this.waitForCondition == null) {
                LOG.debug("null condition; let's continueTest()");
                LOG.debug("Command complete");
                this.commandComplete(this.result);
                this.continueTest();
            } else if (this.waitForCondition()) {
                LOG.debug("condition satisfied; let's continueTest()");
                this.waitForCondition = null;
                LOG.debug("Command complete");
                this.commandComplete(this.result);
                this.continueTest();
            } else {
                //LOG.debug("waitForCondition was false; keep waiting!");
                window.setTimeout(fnBind(this.continueTestWhenConditionIsTrue, this), 10);
            }
        } catch (e) {
            this.result = {};
            this.result.failed = true;
            this.result.failureMessage = extractExceptionMessage(e);
            this.commandComplete(this.result);
            this.continueTest();
        }
    },

    pause : function() {},
    nextCommand : function() {},
    commandStarted : function() {},
    commandComplete : function() {},
    commandError : function() {},
    testComplete : function() {},

    getCommandInterval : function() {
        return 0;
    }

}
<|endoftext|>