<commit_before>/*jslint node: true */

/*
 * #%L
 * %%
 * Copyright (C) 2016 BMW Car IT GmbH
 * %%
 * 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.
 * #L%
 */

var joynr = require("joynr");
var ComplexStruct = require("../generated-javascript/joynr/tests/performance/Types/ComplexStruct.js");
var testbase = require("test-base");
var PerformanceUtilities = require("./performanceutilities.js");
var Promise = require("bluebird").Promise;

var error = testbase.logging.error;
var log = testbase.logging.log;
var options = PerformanceUtilities.getCommandLineOptionsOrDefaults(process.env);
var timeout = 600000;

var consumerBase = {
    echoProxy: undefined,
    initialize: function() {
        if (consumerBase.echoProxy === undefined) {
            console.log("Using domain " + options.domain);
            console.error("Performing " + options.numRuns + " runs");
            var viaClusterController = options.viacc == 'true';
            console.log("Short-circuit-test: " + viaClusterController);
            var provisioning = testbase.provisioning_common;
            if (viaClusterController) {
                provisioning.ccAddress.host = options.cchost;
                provisioning.ccAddress.port = options.ccport;
                joynr.selectRuntime("websocket.libjoynr");
            } else {
                provisioning.bounceProxyBaseUrl = options.bounceProxyBaseUrl;
                provisioning.bounceProxyUrl = provisioning.bounceProxyBaseUrl + "/bounceproxy/";
                joynr.selectRuntime("inprocess");
            }
            return joynr.load(provisioning).then(function(loadedJoynr) {
                joynr = loadedJoynr;
                log("joynr started");
                if (!viaClusterController) {
                    consumerBase.registerProvider();
                }
                return loadedJoynr;
            }).then(function(loadedJoynr) {
                var messagingQos = new loadedJoynr.messaging.MessagingQos({
                    ttl : timeout
                });
                var EchoProxy = require("../generated-javascript/joynr/tests/performance/EchoProxy.js");
                return loadedJoynr.proxyBuilder.build(EchoProxy, {
                    domain : options.domain,
                    messagingQos : messagingQos
                });
            }).then(function(echoProxy) {
                consumerBase.echoProxy = echoProxy;
                return null;
            }).catch(function(error) {
                throw error;
            });
        } else {
            return Promise.resolve();
        }
    },
    registerProvider : function() {
        var providerQos = new joynr.types.ProviderQos({
            customParameters : [],
            priority : Date.now(),
            scope : joynr.types.ProviderScope.LOCAL,
            supportsOnChangeSubscriptions : true
        });

        var EchoProvider = require("../generated-javascript/joynr/tests/performance/EchoProvider.js");
        var EchoProviderImpl = require("./EchoProviderImpl.js");
        var echoProvider = joynr.providerBuilder.build(
                EchoProvider,
                EchoProviderImpl.implementation);

        joynr.capabilities.registerCapability("", options.domain, echoProvider, providerQos).then(function() {
            log("provider registered successfully");
        }).catch(function(error) {
            log("error registering provider: " + error.toString());
        });
    },
    executeBenchmark : function(benchmarkName, benchmark, numRuns) {
        var numRuns = numRuns ? numRuns : options.numRuns;
        console.log("call " + benchmarkName +" " + numRuns + " times");
        startTime = Date.now();
        var promises = [];

        for (var i = 1; i <= numRuns; i++) {
            promises.push(benchmark(i));
        }

        return Promise.all(promises).then(function() {
            console.log("all the numRuns were executed");
            var elapsedTimeMs = Date.now() - startTime;

            error(benchmarkName + " took " + elapsedTimeMs + " ms. " + numRuns / (elapsedTimeMs / 1000) + " msgs/s");
            return null;
        });
    },
    echoString : function() {
        var testProcedure = function(i) {
            var args = {
                data : PerformanceUtilities.createString(options.stringLength-2, "x") + "-" + i
            };
            return consumerBase.echoProxy.echoString(args).then(function(returnValues) {
                if (args.data !== returnValues.responseData) {
                    throw new Error("Echo " + JSON.stringify(returnValues.responseData) + " does not match input data: " + JSON.stringify(args.data));
                }
                return returnValues;
            });
        }
        return consumerBase.executeBenchmark("echoString", testProcedure);
    },
    echoComplexStruct : function() {
        var testProcedure = function(i) {
            var args = {
                data : new ComplexStruct({
                    num32 : PerformanceUtilities.createRandomNumber(100000),
                    num64 : PerformanceUtilities.createRandomNumber(1000000),
                    data : PerformanceUtilities.createByteArray(options.byteArrayLength, 1),
                    str : PerformanceUtilities.createString(options.stringLength-2, "x") + "-" + i
                })
            };
            return consumerBase.echoProxy.echoComplexStruct(args).then(function(returnValues) {
                if (args.data.num32 !== returnValues.responseData.num32 ||
                    args.data.num64 !== returnValues.responseData.num64 ||
                    args.data.data.length !== returnValues.responseData.data.length ||
                    args.data.str.length !== returnValues.responseData.str.length) {
                    throw new Error("Echo " + JSON.stringify(returnValues.responseData) + " does not match input data: " + JSON.stringify(args.data));
                }
                return returnValues;
            });
        }
        return consumerBase.executeBenchmark("echoComplexStruct", testProcedure);
    },
    echoByteArray : function(byteArraySizeFactor) {
        var byteArraySizeFactor = byteArraySizeFactor ? byteArraySizeFactor : 1;
        var byteArraySize = byteArraySizeFactor * options.byteArrayLength;
        var testProcedure = function(i) {
            var args = {
                data : PerformanceUtilities.createByteArray(byteArraySize, 1)
            };
            var firstElement = i % 128;
            args.data[0] = firstElement;
            return consumerBase.echoProxy.echoByteArray(args).then(function(returnValues) {
                if (args.data.length !== returnValues.responseData.length ||
                    firstElement !== returnValues.responseData[0]) {
                    throw new Error("Echo " + JSON.stringify(returnValues.responseData) + " does not match input data: " + JSON.stringify(args.data));
                }

                return returnValues;
            });
        }
        // the larger this byteArraySizeFactor is, the longer this test takes
        // in order to mitigate that, we scale the numer of runs by byteArraySizeFactor
        var numRuns = options.numRuns;
        if (byteArraySizeFactor > 1) {
            numRuns = numRuns / (Math.sqrt(byteArraySizeFactor));
        }
        return consumerBase.executeBenchmark("echoByteArray " + byteArraySize, testProcedure, numRuns);
    }
};
module.exports = consumerBase;
<commit_msg>[JS] Fix log in performance-test/consumer.base.js<commit_after>/*jslint node: true */

/*
 * #%L
 * %%
 * Copyright (C) 2016 BMW Car IT GmbH
 * %%
 * 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.
 * #L%
 */

var joynr = require("joynr");
var ComplexStruct = require("../generated-javascript/joynr/tests/performance/Types/ComplexStruct.js");
var testbase = require("test-base");
var PerformanceUtilities = require("./performanceutilities.js");
var Promise = require("bluebird").Promise;

var error = testbase.logging.error;
var log = testbase.logging.log;
var options = PerformanceUtilities.getCommandLineOptionsOrDefaults(process.env);
var timeout = 600000;

var consumerBase = {
    echoProxy: undefined,
    initialize: function() {
        if (consumerBase.echoProxy === undefined) {
            console.log("Using domain " + options.domain);
            console.error("Performing " + options.numRuns + " runs");
            var viaClusterController = options.viacc == 'true';
            console.log("Via cluster-contoller: " + viaClusterController);
            var provisioning = testbase.provisioning_common;
            if (viaClusterController) {
                provisioning.ccAddress.host = options.cchost;
                provisioning.ccAddress.port = options.ccport;
                joynr.selectRuntime("websocket.libjoynr");
            } else {
                provisioning.bounceProxyBaseUrl = options.bounceProxyBaseUrl;
                provisioning.bounceProxyUrl = provisioning.bounceProxyBaseUrl + "/bounceproxy/";
                joynr.selectRuntime("inprocess");
            }
            return joynr.load(provisioning).then(function(loadedJoynr) {
                joynr = loadedJoynr;
                log("joynr started");
                if (!viaClusterController) {
                    consumerBase.registerProvider();
                }
                return loadedJoynr;
            }).then(function(loadedJoynr) {
                var messagingQos = new loadedJoynr.messaging.MessagingQos({
                    ttl : timeout
                });
                var EchoProxy = require("../generated-javascript/joynr/tests/performance/EchoProxy.js");
                return loadedJoynr.proxyBuilder.build(EchoProxy, {
                    domain : options.domain,
                    messagingQos : messagingQos
                });
            }).then(function(echoProxy) {
                consumerBase.echoProxy = echoProxy;
                return null;
            }).catch(function(error) {
                throw error;
            });
        } else {
            return Promise.resolve();
        }
    },
    registerProvider : function() {
        var providerQos = new joynr.types.ProviderQos({
            customParameters : [],
            priority : Date.now(),
            scope : joynr.types.ProviderScope.LOCAL,
            supportsOnChangeSubscriptions : true
        });

        var EchoProvider = require("../generated-javascript/joynr/tests/performance/EchoProvider.js");
        var EchoProviderImpl = require("./EchoProviderImpl.js");
        var echoProvider = joynr.providerBuilder.build(
                EchoProvider,
                EchoProviderImpl.implementation);

        joynr.capabilities.registerCapability("", options.domain, echoProvider, providerQos).then(function() {
            log("provider registered successfully");
        }).catch(function(error) {
            log("error registering provider: " + error.toString());
        });
    },
    executeBenchmark : function(benchmarkName, benchmark, numRuns) {
        var numRuns = numRuns ? numRuns : options.numRuns;
        console.log("call " + benchmarkName +" " + numRuns + " times");
        startTime = Date.now();
        var promises = [];

        for (var i = 1; i <= numRuns; i++) {
            promises.push(benchmark(i));
        }

        return Promise.all(promises).then(function() {
            console.log("all the numRuns were executed");
            var elapsedTimeMs = Date.now() - startTime;

            error(benchmarkName + " took " + elapsedTimeMs + " ms. " + numRuns / (elapsedTimeMs / 1000) + " msgs/s");
            return null;
        });
    },
    echoString : function() {
        var testProcedure = function(i) {
            var args = {
                data : PerformanceUtilities.createString(options.stringLength-2, "x") + "-" + i
            };
            return consumerBase.echoProxy.echoString(args).then(function(returnValues) {
                if (args.data !== returnValues.responseData) {
                    throw new Error("Echo " + JSON.stringify(returnValues.responseData) + " does not match input data: " + JSON.stringify(args.data));
                }
                return returnValues;
            });
        }
        return consumerBase.executeBenchmark("echoString", testProcedure);
    },
    echoComplexStruct : function() {
        var testProcedure = function(i) {
            var args = {
                data : new ComplexStruct({
                    num32 : PerformanceUtilities.createRandomNumber(100000),
                    num64 : PerformanceUtilities.createRandomNumber(1000000),
                    data : PerformanceUtilities.createByteArray(options.byteArrayLength, 1),
                    str : PerformanceUtilities.createString(options.stringLength-2, "x") + "-" + i
                })
            };
            return consumerBase.echoProxy.echoComplexStruct(args).then(function(returnValues) {
                if (args.data.num32 !== returnValues.responseData.num32 ||
                    args.data.num64 !== returnValues.responseData.num64 ||
                    args.data.data.length !== returnValues.responseData.data.length ||
                    args.data.str.length !== returnValues.responseData.str.length) {
                    throw new Error("Echo " + JSON.stringify(returnValues.responseData) + " does not match input data: " + JSON.stringify(args.data));
                }
                return returnValues;
            });
        }
        return consumerBase.executeBenchmark("echoComplexStruct", testProcedure);
    },
    echoByteArray : function(byteArraySizeFactor) {
        var byteArraySizeFactor = byteArraySizeFactor ? byteArraySizeFactor : 1;
        var byteArraySize = byteArraySizeFactor * options.byteArrayLength;
        var testProcedure = function(i) {
            var args = {
                data : PerformanceUtilities.createByteArray(byteArraySize, 1)
            };
            var firstElement = i % 128;
            args.data[0] = firstElement;
            return consumerBase.echoProxy.echoByteArray(args).then(function(returnValues) {
                if (args.data.length !== returnValues.responseData.length ||
                    firstElement !== returnValues.responseData[0]) {
                    throw new Error("Echo " + JSON.stringify(returnValues.responseData) + " does not match input data: " + JSON.stringify(args.data));
                }

                return returnValues;
            });
        }
        // the larger this byteArraySizeFactor is, the longer this test takes
        // in order to mitigate that, we scale the numer of runs by byteArraySizeFactor
        var numRuns = options.numRuns;
        if (byteArraySizeFactor > 1) {
            numRuns = numRuns / (Math.sqrt(byteArraySizeFactor));
        }
        return consumerBase.executeBenchmark("echoByteArray " + byteArraySize, testProcedure, numRuns);
    }
};
module.exports = consumerBase;
<|endoftext|>


<commit_before>define([
    'arches',
    'jquery',
    'knockout',
    'knockout-mapping',
    'leaflet',
    'views/components/workbench',
    'text!templates/views/components/iiif-popup.htm',
    'leaflet-iiif',
    'leaflet-fullscreen',
    'bindings/select2-query',
    'bindings/leaflet'
], function(arches, $, ko, koMapping, L, WorkbenchViewmodel, iiifPopup) {
    var IIIFViewerViewmodel = function(params) {
        var self = this;
        var abortFetchManifest;
        this.getManifestDataValue = function(object, property, returnFirstVal) {
            var val = object[property];
            if (Array.isArray(val) && returnFirstVal) val = object[property][0]["@value"];
            return val;
        };

        this.map = ko.observable();
        this.manifest = ko.observable(params.manifest);
        this.editManifest = ko.observable(!params.manifest);
        this.canvas = ko.observable(params.canvas);
        this.manifestLoading = ko.observable();
        this.filter = ko.observable('');
        this.manifestData = ko.observable();
        this.manifestError = ko.observable();
        this.manifestName = ko.observable();
        this.manifestDescription = ko.observable();
        this.manifestAttribution = ko.observable();
        this.manifestLogo = ko.observable();
        this.manifestMetadata = koMapping.fromJS([]);
        this.canvasLabel = ko.observable();
        this.zoomToCanvas = !(params.zoom && params.center);
        this.annotationNodes = ko.observableArray();
        window.fetch(arches.urls.iiifannotationnodes)
            .then(function(response) {
                return response.json();
            })
            .then(function(json) {
                self.annotationNodes(
                    json.map(function(node) {
                        var annotations = ko.observableArray();
                        var updateAnnotations = function() {
                            var canvas = self.canvas();
                            if (canvas) {
                                window.fetch(arches.urls.iiifannotations + '?canvas=' + canvas + '&nodeid=' + node.nodeid)
                                    .then(function(response) {
                                        return response.json();
                                    })
                                    .then(function(json) {
                                        json.features.forEach(function(feature) {
                                            feature.properties.graphName = node['graph_name'];
                                        });
                                        annotations(json.features);
                                    });
                            }
                        };
                        self.canvas.subscribe(updateAnnotations);
                        updateAnnotations();
                        return {
                            name: node['graph_name'] + ' - ' + node.name,
                            icon: node.icon,
                            active: ko.observable(false),
                            opacity: ko.observable(100),
                            annotations: annotations
                        };
                    })
                );
            });

        var annotationLayer = ko.computed(function() {
            var annotationFeatures = [];
            self.annotationNodes().forEach(function(node) {
                if (node.active()) {
                    var annotations = node.annotations();
                    if (params.tile && params.tile.tileid) {
                        annotations = annotations.filter(function(annotation) {
                            return annotation.properties.tileId !== params.tile.tileid;
                        });
                    }
                    annotations.forEach(function(annotation) {
                        annotation.properties.opacityModifier = node.opacity();
                    });
                    annotationFeatures = annotations.concat(annotationFeatures);
                }
            });
            return L.geoJson({
                type: 'FeatureCollection',
                features: annotationFeatures
            }, {
                pointToLayer: function(feature, latlng) {
                    var modifier = feature.properties.opacityModifier / 100;
                    var style = {
                        color: feature.properties.color,
                        fillColor: feature.properties.fillColor,
                        weight: feature.properties.weight,
                        radius: feature.properties.radius,
                        opacity: (feature.properties.opacity * modifier),
                        fillOpacity: (feature.properties.fillOpacity * modifier)
                    };
                    return L.circleMarker(latlng, style);
                },
                style: function(feature) {
                    var modifier = feature.properties.opacityModifier / 100;
                    var style = {
                        color: feature.properties.color,
                        fillColor: feature.properties.fillColor,
                        weight: feature.properties.weight,
                        radius: feature.properties.radius,
                        opacity: (feature.properties.opacity * modifier),
                        fillOpacity: (feature.properties.fillOpacity * modifier)
                    };
                    return style;
                },
                onEachFeature: function(feature, layer) {
                    if (params.onEachFeature) {
                        params.onEachFeature(feature, layer);
                    }
                    else {
                        var popup = L.popup({
                            closeButton: false,
                            maxWidth: 349
                        })
                            .setContent(iiifPopup)
                            .on('add', function() {
                                var popupData = {
                                    'closePopup': function() {
                                        popup.remove();
                                    },
                                    'name': ko.observable(''),
                                    'description': ko.observable(''),
                                    'graphName': feature.properties.graphName,
                                    'resourceinstanceid': feature.properties.resourceId,
                                    'reportURL': arches.urls.resource_report
                                };
                                window.fetch(arches.urls.resource_descriptors + popupData.resourceinstanceid)
                                    .then(function(response) {
                                        return response.json();
                                    })
                                    .then(function(descriptors) {
                                        popupData.name(descriptors.displayname);
                                        popupData.description(descriptors['map_popup']);
                                    });
                                var popupElement = popup.getElement()
                                    .querySelector('.mapboxgl-popup-content');
                                ko.applyBindingsToDescendants(popupData, popupElement);
                            });
                        layer.bindPopup(popup);
                    }
                }
            });
        });
        var annotationFeatureGroup = new L.FeatureGroup();

        annotationLayer.subscribe(function(newAnnotationLayer) {
            var map = self.map();
            if (map) {
                annotationFeatureGroup.clearLayers();
                annotationFeatureGroup.addLayer(newAnnotationLayer);
            }
        });

        this.canvases = ko.pureComputed(function() {
            var manifestData = self.manifestData();
            var sequences = manifestData ? manifestData.sequences : [];
            var canvases = [];
            sequences.forEach(function(sequence) {
                if (sequence.canvases) {
                    sequence.label = self.getManifestDataValue(sequence, 'label', true);
                    sequence.canvases.forEach(function(canvas) {
                        canvas.label = self.getManifestDataValue(canvas, 'label', true);
                        if (typeof canvas.thumbnail === 'object')
                            canvas.thumbnail = canvas.thumbnail["@id"];
                        else if (canvas.images && canvas.images[0] && canvas.images[0].resource)
                            canvas.thumbnail = canvas.images[0].resource["@id"];
                        canvases.push(canvas);
                    });
                }
            });
            return canvases;
        });

        var validateUrl = function(value) {
            return /^(?:(?:(?:https?|ftp):)?\/\/)(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,})))(?::\d{2,5})?(?:[/?#]\S*)?$/i.test(value);
        };

        var queryTerm;
        var limit = 10;
        this.manifestSelectConfig = {
            value: this.manifest,
            clickBubble: true,
            multiple: false,
            closeOnSelect: false,
            allowClear: true,
            ajax: {
                url: arches.urls.iiifmanifest,
                dataType: 'json',
                quietMillis: 250,
                data: function(term, page) {
                    var data = {
                        start: (page-1)*limit,
                        limit: limit
                    };
                    queryTerm = term;
                    if (term) data.query = term;
                    return data;
                },
                results: function(data, page) {
                    var results = data.results;
                    if (validateUrl(queryTerm)) results.unshift({
                        url: queryTerm,
                        label: queryTerm
                    });
                    return {
                        results: results,
                        more: data.count >= (page*limit)
                    };
                }
            },
            id: function(item) {
                return item.url;
            },
            formatResult: function(item) {
                return item.label;
            },
            formatSelection: function(item) {
                return item.label;
            },
            clear: function() {
                self.manifest('');
            },
            isEmpty: ko.computed(function() {
                return self.manifest() === '' || !self.manifest();
            }, this),
            initSelection: function() {
                return;
            }
        };

        this.getManifestData = function() {
            var manifestURL = self.manifest();
            if (manifestURL) {
                self.manifestLoading(true);
                self.manifestError(undefined);
                abortFetchManifest = new window.AbortController();
                window.fetch(manifestURL, {signal: abortFetchManifest.signal})
                    .then(function(response) {
                        return response.json();
                    })
                    .then(function(manifestData) {
                        self.manifestData(manifestData);
                        self.editManifest(false);
                    })
                    .catch(function(error) {
                        if (error.message !== "The user aborted a request.")
                            self.manifestError(error);
                    })
                    .finally(function() {
                        self.manifestLoading(false);
                        abortFetchManifest = undefined;
                    });
            }
        };
        this.getManifestData();

        WorkbenchViewmodel.apply(this, [params]);

        this.activeTab.subscribe(function() {
            var map = self.map();
            if (map) setTimeout(function() {
                map.invalidateSize();
            }, 1);
        });

        if (params.showGallery === undefined) params.showGallery = true;
        this.showGallery = ko.observable(params.showGallery);
        if (!params.manifest) params.expandGallery = true;
        this.expandGallery = ko.observable(params.expandGallery);
        this.expandGallery.subscribe(function(expandGallery) {
            if (expandGallery) self.showGallery(true);
        });
        this.showGallery.subscribe(function(showGallery) {
            if (!showGallery) self.expandGallery(false);
        });

        this.toggleGallery = function() {
            self.showGallery(!self.showGallery());
        };

        this.leafletConfig = {
            center: params.center || [0, 0],
            crs: L.CRS.Simple,
            zoom: params.zoom || 0,
            afterRender: this.map
        };

        var canvasLayer;
        this.brightness = ko.observable(100);
        this.contrast = ko.observable(100);
        this.saturation = ko.observable(100);
        this.greyscale = ko.observable(false);
        this.canvasFilter = ko.pureComputed(function() {
            var b = self.brightness() / 100;
            var c = self.contrast() / 100;
            var s = self.saturation() / 100;
            var g = self.greyscale() ? 1 : 0;
            return 'brightness(' + b + ') contrast(' + c + ') ' +
                'saturate(' + s + ') grayscale(' + g + ')';
        });
        var updateCanvasLayerFilter = function() {
            var filter = self.canvasFilter();
            var map = self.map();
            if (map) {
                map.getContainer().querySelector('.leaflet-tile-pane').style.filter = filter;
            }
        };
        this.canvasFilter.subscribe(updateCanvasLayerFilter);

        this.resetImageSettings = function() {
            self.brightness(100);
            self.contrast(100);
            self.saturation(100);
            self.greyscale(false);
        };

        var addCanvasLayer = function() {
            var map = self.map();
            var canvas = self.canvas();
            if (map && canvas) {
                if (canvasLayer) {
                    map.removeLayer(canvasLayer);
                    canvasLayer = undefined;
                }
                if (canvas) {
                    canvasLayer = L.tileLayer.iiif(canvas + '/info.json', {
                        fitBounds: self.zoomToCanvas
                    });
                    canvasLayer.addTo(map);
                    updateCanvasLayerFilter();
                }
            }
            self.zoomToCanvas = false;
        };
        this.map.subscribe(function(map) {
            L.control.fullscreen({
                fullscreenElement: $(map.getContainer()).closest('.workbench-card-wrapper')[0]
            }).addTo(map);
            addCanvasLayer();
            map.addLayer(annotationFeatureGroup);
        });
        this.canvas.subscribe(addCanvasLayer);

        this.selectCanvas = function(canvas) {
            var service = self.getCanvasService(canvas);
            self.zoomToCanvas = true;
            if (service) self.canvas(service);

            self.origCanvasLabel = self.getManifestDataValue(canvas, 'label', true);
            self.canvasLabel(self.getManifestDataValue(canvas, 'label', true));
        };

        this.canvasClick = function(canvas) {
            self.selectCanvas(canvas);
            self.expandGallery(false);
        };

        this.getCanvasService = function(canvas) {
            if (canvas.images.length > 0) return canvas.images[0].resource.service['@id'];
        };

        this.updateCanvas = !self.canvas();
        this.manifestData.subscribe(function(manifestData) {
            if (manifestData) {
                if (manifestData.sequences.length > 0) {
                    var sequence = manifestData.sequences[0];
                    var canvasIndex = 0;
                    if (sequence.canvases.length > 0) {
                        if (!self.updateCanvas) {
                            canvasIndex = sequence.canvases.findIndex(function(c){return c.images[0].resource.service['@id'] === self.canvas();});
                        }
                        var canvas = sequence.canvases[canvasIndex];
                        self.selectCanvas(canvas);
                    }
                }
                self.updateCanvas = true;
                self.origManifestName = self.getManifestDataValue(manifestData, 'label', true);
                self.manifestName(self.origManifestName);
                self.origManifestDescription = self.getManifestDataValue(manifestData, 'description', true);
                self.manifestDescription(self.origManifestDescription);
                self.origManifestAttribution = self.getManifestDataValue(manifestData, 'attribution', true);
                self.manifestAttribution(self.origManifestAttribution);
                self.origManifestLogo = self.getManifestDataValue(manifestData, 'logo', true);
                self.manifestLogo(self.origManifestLogo);
                self.origManifestMetadata = koMapping.toJSON(self.getManifestDataValue(manifestData, 'metadata'));
                self.manifestMetadata.removeAll();
                self.getManifestDataValue(manifestData, 'metadata').forEach(function(entry){
                    self.manifestMetadata.push(koMapping.fromJS(entry));
                });
            }
        });

        this.toggleManifestEditor = function() {
            self.editManifest(!self.editManifest());
            if (abortFetchManifest) abortFetchManifest.abort();
        };

        this.getAnnotationCount = function() {
            return 0;
        };
    };
    ko.components.register('iiif-viewer', {
        viewModel: IIIFViewerViewmodel,
        template: {
            require: 'text!templates/views/components/iiif-viewer.htm'
        }
    });
    return IIIFViewerViewmodel;
});
<commit_msg>Allow user to replace method to create annotation node overlays, re #7826<commit_after>define([
    'arches',
    'jquery',
    'knockout',
    'knockout-mapping',
    'leaflet',
    'views/components/workbench',
    'text!templates/views/components/iiif-popup.htm',
    'leaflet-iiif',
    'leaflet-fullscreen',
    'bindings/select2-query',
    'bindings/leaflet'
], function(arches, $, ko, koMapping, L, WorkbenchViewmodel, iiifPopup) {
    var IIIFViewerViewmodel = function(params) {
        var self = this;
        var abortFetchManifest;
        this.getManifestDataValue = function(object, property, returnFirstVal) {
            var val = object[property];
            if (Array.isArray(val) && returnFirstVal) val = object[property][0]["@value"];
            return val;
        };

        this.map = ko.observable();
        this.manifest = ko.observable(params.manifest);
        this.editManifest = ko.observable(!params.manifest);
        this.canvas = ko.observable(params.canvas);
        this.manifestLoading = ko.observable();
        this.filter = ko.observable('');
        this.manifestData = ko.observable();
        this.manifestError = ko.observable();
        this.manifestName = ko.observable();
        this.manifestDescription = ko.observable();
        this.manifestAttribution = ko.observable();
        this.manifestLogo = ko.observable();
        this.manifestMetadata = koMapping.fromJS([]);
        this.canvasLabel = ko.observable();
        this.zoomToCanvas = !(params.zoom && params.center);
        this.annotationNodes = ko.observableArray();

        this.buildAnnotationNodes = params.buildAnnotationNodes || function(json) {
            self.annotationNodes(
                json.map(function(node) {
                    var annotations = ko.observableArray();
                    var updateAnnotations = function() {
                        var canvas = self.canvas();
                        if (canvas) {
                            window.fetch(arches.urls.iiifannotations + '?canvas=' + canvas + '&nodeid=' + node.nodeid)
                                .then(function(response) {
                                    return response.json();
                                })
                                .then(function(json) {
                                    json.features.forEach(function(feature) {
                                        feature.properties.graphName = node['graph_name'];
                                    });
                                    annotations(json.features);
                                });
                        }
                    };
                    self.canvas.subscribe(updateAnnotations);
                    updateAnnotations();
                    return {
                        name: node['graph_name'] + ' - ' + node.name,
                        icon: node.icon,
                        active: ko.observable(false),
                        opacity: ko.observable(100),
                        annotations: annotations
                    };
                })
            );
        };

        window.fetch(arches.urls.iiifannotationnodes)
            .then(function(response) {
                return response.json();
            })
            .then(self.buildAnnotationNodes);

        var annotationLayer = ko.computed(function() {
            var annotationFeatures = [];
            self.annotationNodes().forEach(function(node) {
                if (node.active()) {
                    var annotations = node.annotations();
                    if (params.tile && params.tile.tileid) {
                        annotations = annotations.filter(function(annotation) {
                            return annotation.properties.tileId !== params.tile.tileid;
                        });
                    }
                    annotations.forEach(function(annotation) {
                        annotation.properties.opacityModifier = node.opacity();
                    });
                    annotationFeatures = annotations.concat(annotationFeatures);
                }
            });
            return L.geoJson({
                type: 'FeatureCollection',
                features: annotationFeatures
            }, {
                pointToLayer: function(feature, latlng) {
                    var modifier = feature.properties.opacityModifier / 100;
                    var style = {
                        color: feature.properties.color,
                        fillColor: feature.properties.fillColor,
                        weight: feature.properties.weight,
                        radius: feature.properties.radius,
                        opacity: (feature.properties.opacity * modifier),
                        fillOpacity: (feature.properties.fillOpacity * modifier)
                    };
                    return L.circleMarker(latlng, style);
                },
                style: function(feature) {
                    var modifier = feature.properties.opacityModifier / 100;
                    var style = {
                        color: feature.properties.color,
                        fillColor: feature.properties.fillColor,
                        weight: feature.properties.weight,
                        radius: feature.properties.radius,
                        opacity: (feature.properties.opacity * modifier),
                        fillOpacity: (feature.properties.fillOpacity * modifier)
                    };
                    return style;
                },
                onEachFeature: function(feature, layer) {
                    if (params.onEachFeature) {
                        params.onEachFeature(feature, layer);
                    }
                    else {
                        var popup = L.popup({
                            closeButton: false,
                            maxWidth: 349
                        })
                            .setContent(iiifPopup)
                            .on('add', function() {
                                var popupData = {
                                    'closePopup': function() {
                                        popup.remove();
                                    },
                                    'name': ko.observable(''),
                                    'description': ko.observable(''),
                                    'graphName': feature.properties.graphName,
                                    'resourceinstanceid': feature.properties.resourceId,
                                    'reportURL': arches.urls.resource_report
                                };
                                window.fetch(arches.urls.resource_descriptors + popupData.resourceinstanceid)
                                    .then(function(response) {
                                        return response.json();
                                    })
                                    .then(function(descriptors) {
                                        popupData.name(descriptors.displayname);
                                        popupData.description(descriptors['map_popup']);
                                    });
                                var popupElement = popup.getElement()
                                    .querySelector('.mapboxgl-popup-content');
                                ko.applyBindingsToDescendants(popupData, popupElement);
                            });
                        layer.bindPopup(popup);
                    }
                }
            });
        });
        var annotationFeatureGroup = new L.FeatureGroup();

        annotationLayer.subscribe(function(newAnnotationLayer) {
            var map = self.map();
            if (map) {
                annotationFeatureGroup.clearLayers();
                annotationFeatureGroup.addLayer(newAnnotationLayer);
            }
        });

        this.canvases = ko.pureComputed(function() {
            var manifestData = self.manifestData();
            var sequences = manifestData ? manifestData.sequences : [];
            var canvases = [];
            sequences.forEach(function(sequence) {
                if (sequence.canvases) {
                    sequence.label = self.getManifestDataValue(sequence, 'label', true);
                    sequence.canvases.forEach(function(canvas) {
                        canvas.label = self.getManifestDataValue(canvas, 'label', true);
                        if (typeof canvas.thumbnail === 'object')
                            canvas.thumbnail = canvas.thumbnail["@id"];
                        else if (canvas.images && canvas.images[0] && canvas.images[0].resource)
                            canvas.thumbnail = canvas.images[0].resource["@id"];
                        canvases.push(canvas);
                    });
                }
            });
            return canvases;
        });

        var validateUrl = function(value) {
            return /^(?:(?:(?:https?|ftp):)?\/\/)(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,})))(?::\d{2,5})?(?:[/?#]\S*)?$/i.test(value);
        };

        var queryTerm;
        var limit = 10;
        this.manifestSelectConfig = {
            value: this.manifest,
            clickBubble: true,
            multiple: false,
            closeOnSelect: false,
            allowClear: true,
            ajax: {
                url: arches.urls.iiifmanifest,
                dataType: 'json',
                quietMillis: 250,
                data: function(term, page) {
                    var data = {
                        start: (page-1)*limit,
                        limit: limit
                    };
                    queryTerm = term;
                    if (term) data.query = term;
                    return data;
                },
                results: function(data, page) {
                    var results = data.results;
                    if (validateUrl(queryTerm)) results.unshift({
                        url: queryTerm,
                        label: queryTerm
                    });
                    return {
                        results: results,
                        more: data.count >= (page*limit)
                    };
                }
            },
            id: function(item) {
                return item.url;
            },
            formatResult: function(item) {
                return item.label;
            },
            formatSelection: function(item) {
                return item.label;
            },
            clear: function() {
                self.manifest('');
            },
            isEmpty: ko.computed(function() {
                return self.manifest() === '' || !self.manifest();
            }, this),
            initSelection: function() {
                return;
            }
        };

        this.getManifestData = function() {
            var manifestURL = self.manifest();
            if (manifestURL) {
                self.manifestLoading(true);
                self.manifestError(undefined);
                abortFetchManifest = new window.AbortController();
                window.fetch(manifestURL, {signal: abortFetchManifest.signal})
                    .then(function(response) {
                        return response.json();
                    })
                    .then(function(manifestData) {
                        self.manifestData(manifestData);
                        self.editManifest(false);
                    })
                    .catch(function(error) {
                        if (error.message !== "The user aborted a request.")
                            self.manifestError(error);
                    })
                    .finally(function() {
                        self.manifestLoading(false);
                        abortFetchManifest = undefined;
                    });
            }
        };
        this.getManifestData();

        WorkbenchViewmodel.apply(this, [params]);

        this.activeTab.subscribe(function() {
            var map = self.map();
            if (map) setTimeout(function() {
                map.invalidateSize();
            }, 1);
        });

        if (params.showGallery === undefined) params.showGallery = true;
        this.showGallery = ko.observable(params.showGallery);
        if (!params.manifest) params.expandGallery = true;
        this.expandGallery = ko.observable(params.expandGallery);
        this.expandGallery.subscribe(function(expandGallery) {
            if (expandGallery) self.showGallery(true);
        });
        this.showGallery.subscribe(function(showGallery) {
            if (!showGallery) self.expandGallery(false);
        });

        this.toggleGallery = function() {
            self.showGallery(!self.showGallery());
        };

        this.leafletConfig = {
            center: params.center || [0, 0],
            crs: L.CRS.Simple,
            zoom: params.zoom || 0,
            afterRender: this.map
        };

        var canvasLayer;
        this.brightness = ko.observable(100);
        this.contrast = ko.observable(100);
        this.saturation = ko.observable(100);
        this.greyscale = ko.observable(false);
        this.canvasFilter = ko.pureComputed(function() {
            var b = self.brightness() / 100;
            var c = self.contrast() / 100;
            var s = self.saturation() / 100;
            var g = self.greyscale() ? 1 : 0;
            return 'brightness(' + b + ') contrast(' + c + ') ' +
                'saturate(' + s + ') grayscale(' + g + ')';
        });
        var updateCanvasLayerFilter = function() {
            var filter = self.canvasFilter();
            var map = self.map();
            if (map) {
                map.getContainer().querySelector('.leaflet-tile-pane').style.filter = filter;
            }
        };
        this.canvasFilter.subscribe(updateCanvasLayerFilter);

        this.resetImageSettings = function() {
            self.brightness(100);
            self.contrast(100);
            self.saturation(100);
            self.greyscale(false);
        };

        var addCanvasLayer = function() {
            var map = self.map();
            var canvas = self.canvas();
            if (map && canvas) {
                if (canvasLayer) {
                    map.removeLayer(canvasLayer);
                    canvasLayer = undefined;
                }
                if (canvas) {
                    canvasLayer = L.tileLayer.iiif(canvas + '/info.json', {
                        fitBounds: self.zoomToCanvas
                    });
                    canvasLayer.addTo(map);
                    updateCanvasLayerFilter();
                }
            }
            self.zoomToCanvas = false;
        };
        this.map.subscribe(function(map) {
            L.control.fullscreen({
                fullscreenElement: $(map.getContainer()).closest('.workbench-card-wrapper')[0]
            }).addTo(map);
            addCanvasLayer();
            map.addLayer(annotationFeatureGroup);
        });
        this.canvas.subscribe(addCanvasLayer);

        this.selectCanvas = function(canvas) {
            var service = self.getCanvasService(canvas);
            self.zoomToCanvas = true;
            if (service) self.canvas(service);

            self.origCanvasLabel = self.getManifestDataValue(canvas, 'label', true);
            self.canvasLabel(self.getManifestDataValue(canvas, 'label', true));
        };

        this.canvasClick = function(canvas) {
            self.selectCanvas(canvas);
            self.expandGallery(false);
        };

        this.getCanvasService = function(canvas) {
            if (canvas.images.length > 0) return canvas.images[0].resource.service['@id'];
        };

        this.updateCanvas = !self.canvas();
        this.manifestData.subscribe(function(manifestData) {
            if (manifestData) {
                if (manifestData.sequences.length > 0) {
                    var sequence = manifestData.sequences[0];
                    var canvasIndex = 0;
                    if (sequence.canvases.length > 0) {
                        if (!self.updateCanvas) {
                            canvasIndex = sequence.canvases.findIndex(function(c){return c.images[0].resource.service['@id'] === self.canvas();});
                        }
                        var canvas = sequence.canvases[canvasIndex];
                        self.selectCanvas(canvas);
                    }
                }
                self.updateCanvas = true;
                self.origManifestName = self.getManifestDataValue(manifestData, 'label', true);
                self.manifestName(self.origManifestName);
                self.origManifestDescription = self.getManifestDataValue(manifestData, 'description', true);
                self.manifestDescription(self.origManifestDescription);
                self.origManifestAttribution = self.getManifestDataValue(manifestData, 'attribution', true);
                self.manifestAttribution(self.origManifestAttribution);
                self.origManifestLogo = self.getManifestDataValue(manifestData, 'logo', true);
                self.manifestLogo(self.origManifestLogo);
                self.origManifestMetadata = koMapping.toJSON(self.getManifestDataValue(manifestData, 'metadata'));
                self.manifestMetadata.removeAll();
                self.getManifestDataValue(manifestData, 'metadata').forEach(function(entry){
                    self.manifestMetadata.push(koMapping.fromJS(entry));
                });
            }
        });

        this.toggleManifestEditor = function() {
            self.editManifest(!self.editManifest());
            if (abortFetchManifest) abortFetchManifest.abort();
        };

        this.getAnnotationCount = function() {
            return 0;
        };
    };
    ko.components.register('iiif-viewer', {
        viewModel: IIIFViewerViewmodel,
        template: {
            require: 'text!templates/views/components/iiif-viewer.htm'
        }
    });
    return IIIFViewerViewmodel;
});
<|endoftext|>


<commit_before>// From: http://jsfiddle.net/unLSJ/

function prettyPrint(obj) {
    var jsonLine = /^( *)("[\w]+": )?("[^"]*"|[\w.+-]*)?([,[{])?$/mg;
    return JSON.stringify(obj, null, 3)
        .replace(/&/g, '&').replace(/\\"/g, '"')
        .replace(/</g, '<').replace(/>/g, '>')
        .replace(jsonLine, replacer);
}

function replacer(match, pIndent, pKey, pVal, pEnd) {
    var key = '';
    var val = '';
    var str = '';
    var r = pIndent || '';
    if (pKey)
        r = r + key + pKey.replace(/[": ]/g, '') + ': ';
    if (pVal)
        r = r + (pVal[0] == '"' ? str : val) + pVal + '';
    return r + (pEnd || '');
}

var width = 100;

var granularities = [
    {'name': 'days', 'duration': 86400},
    {'name': '6hours', 'duration': 21600},
    {'name': 'hours', 'duration': 3600},
    {'name': '10minutes', 'duration': 600},
    {'name': 'minutes', 'duration': 60},
    {'name': '10seconds', 'duration': 10},
    {'name': 'seconds', 'duration': 1}
]

function initializePlot() {
    plot = new Highcharts.StockChart({
        'chart': {
            'renderTo': 'plot',
            'type': 'spline',
            'zoomType': 'x',
            'borderRadius': 10
        },
        'credits': {
            'enabled': false
        },
        'navigator': {
            'adaptToUpdatedData': false,
            'series': {
                'data': [] // We will set data manually
            }
        },
        'scrollbar': {
            'liveRedraw': false
        },
        'legend': {
            'enabled': true,
            'verticalAlign': 'top',
            'floating': true,
            'padding': 5
        },
        'rangeSelector': {
            'buttonTheme': {
                'width': 50
            },
            'buttons': [
                {
                    'type': 'day',
                    'count': 1,
                    'text': "day"
                },
                {
                    'type': 'week',
                    'count': 1,
                    'text': "week"
                },
                {
                    'type': 'month',
                    'count': 1,
                    'text': "month"
                },
                {
                    'type': 'year',
                    'count': 1,
                    'text': "year"
                },
                {
                    'type': 'all',
                    'text': "all"
                }
            ],
            'selected': 4 // All
        },
        'xAxis': {
            'events': {
                'afterSetExtremes': reloadGraphData
            },
            'minRange': width * 1000 // TODO: Should this depend on possible granularity for the stream(s)?
        },
        'yAxis': [],
        'plotOptions': {
            'series': {
                'marker': {
                    'enabled': true,
                    'radius': 3
                },
                'dataGrouping': {
                    'enabled': false
                }
            }
        },
        'series': []
    });
}

function tagsToObject(tags) {
    result = {};
    $.each(tags, function (i, tag) {
        if ($.isPlainObject(tag)) {
            $.extend(result, tag);
        }
        else {
            result[tag] = tag;
        }
    });
    return result;
}

function convertDatapoints(datapoints) {
    line = []
    range = []
    $.each(datapoints, function (i, datapoint) {
        var t = moment.utc($.isPlainObject(datapoint.t) ? datapoint.t.m : datapoint.t).valueOf();
        if ($.isPlainObject(datapoint.v)) {
            line.push([t, datapoint.v.m]);
            range.push([t, datapoint.v.l, datapoint.v.u]);
        }
        else {
            line.push([t, datapoint.v]);
            range.push([t, datapoint.v, datapoint.v]);
        }
    });
    return {
        'line': line,
        'range': range
    };
}

function computeRange(min, max) {
    var range = {
        'granularity': granularities[0]
    }

    if (!$.isNumeric(min) || !$.isNumeric(min)) {
        return range;
    }

    range.start = min / 1000;
    range.end = max / 1000;

    var interval = range.end - range.start;

    $.each(granularities, function (i, granularity) {
        if (interval / granularity.duration > width) {
            range.granularity = granularity;
            return false;
        }
    });

    range.start -= range.granularity.duration / 2;
    range.end += range.granularity.duration / 2;

    range.start = parseInt(Math.floor(range.start));
    range.end = parseInt(Math.floor(range.end));

    return range;
}

var loadingShown = 0;

function showLoading() {
    assert(loadingShown >= 0);

    loadingShown++;
    if (loadingShown === 1) {
        plot.showLoading("Loading data from server...");
    }
}

function hideLoading() {
    assert(loadingShown > 0);

    loadingShown--;
    if (loadingShown === 0) {
        plot.hideLoading();
    }
}

function reloadGraphData(event) {
    var range = computeRange(event.min, event.max);
    var streams = $.map(activeStreams, function (stream, id) {
        return stream;
    });
    showLoading();
    async.each(streams, function (stream, cb) {
        $.getJSON(stream.resource_uri, {
            'granularity': range.granularity.name,
            'limit': 10000,
            'start': range.start,
            'end': range.end
        }, function (data, textStatus, jqXHR) {
            assert.equal(data.id, stream.id);

            var datapoints = convertDatapoints(data.datapoints);
            plot.get('line-' + stream.id).setData(datapoints.line);
            plot.get('range-' + stream.id).setData(datapoints.range);

            cb();
        }).fail(function () {
            cb(arguments);
        });
    }, function (err) {
        hideLoading();
    });
}

function addPlotData(stream) {
    var range = computeRange(plot.xAxis[0].userMin, plot.xAxis[0].userMax);
    showLoading();
    $.getJSON(stream.resource_uri, {
        'granularity': range.granularity.name,
        'limit': 10000,
        'start': range.start,
        'end': range.end
    }, function (data, textStatus, jqXHR) {
        assert.equal(data.id, stream.id);

        var datapoints = convertDatapoints(data.datapoints);

        plot.addAxis({
            'id': 'axis-' + stream.id,
            'title': {
                'text': stream.tags.unit
            },
            'showEmpty': false
        });
        var series = plot.addSeries({
            'id': 'range-' + stream.id,
            'name': stream.tags.name,
            'yAxis': 'axis-' + stream.id,
            'type': 'arearange',
            'lineWidth': 0,
            'fillOpacity': 0.3,
            'tooltip': {
                'pointFormat': '{series.name} min/max: {point.low} - {point.high}
',
                'valueDecimals': 3
            },
            'data': datapoints.range
        });
        // Match yAxis title color with series color
        series.yAxis.axisTitle.css({'color': series.color});
        plot.addSeries({
            'id': 'line-' + stream.id,
            'name': stream.tags.name,
            'linkedTo': 'range-' + stream.id,
            'color': series.color,
            'yAxis': 'axis-' + stream.id,
            'tooltip': {
                'pointFormat': '{series.name} mean: {point.y}
',
                'valueDecimals': 3
            },
            'data': datapoints.line
        });
        var navigator = plot.get('highcharts-navigator-series');
        if (navigator.data.length === 0) {
            // TODO: Should we set some better data for navigator?
            navigator.setData(datapoints.line);
        }
    }).always(function () {
        hideLoading();
    });
}

var activeStreams = {};
var plot = null;

$(document).ready(function () {
    $('#streams').empty();

    $.getJSON('/api/v1/stream/', function (data, textStatus, jqXHR) {
        $.each(data.objects, function (i, stream) {
            if (data.id in activeStreams) return;

            stream.tags = tagsToObject(stream.tags);

            $('
').data(stream).html(prettyPrint(stream.tags)).appendTo('#streams').click(function (e) {
                activeStreams[stream.id] = stream;
                addPlotData(stream);
                $(this).remove();
            });
        });
    });

    $(document).ajaxError(function (event, jqXHR, ajaxSettings, thrownError) {
        console.error(event, jqXHR, ajaxSettings, thrownError);
    });

    initializePlot();
});<commit_msg>Remember what was the largest known region for each stream.<commit_after>// From: http://jsfiddle.net/unLSJ/

function prettyPrint(obj) {
    var jsonLine = /^( *)("[\w]+": )?("[^"]*"|[\w.+-]*)?([,[{])?$/mg;
    return JSON.stringify(obj, null, 3)
        .replace(/&/g, '&').replace(/\\"/g, '"')
        .replace(/</g, '<').replace(/>/g, '>')
        .replace(jsonLine, replacer);
}

function replacer(match, pIndent, pKey, pVal, pEnd) {
    var key = '';
    var val = '';
    var str = '';
    var r = pIndent || '';
    if (pKey)
        r = r + key + pKey.replace(/[": ]/g, '') + ': ';
    if (pVal)
        r = r + (pVal[0] == '"' ? str : val) + pVal + '';
    return r + (pEnd || '');
}

var width = 100;

var granularities = [
    {'name': 'days', 'duration': 86400},
    {'name': '6hours', 'duration': 21600},
    {'name': 'hours', 'duration': 3600},
    {'name': '10minutes', 'duration': 600},
    {'name': 'minutes', 'duration': 60},
    {'name': '10seconds', 'duration': 10},
    {'name': 'seconds', 'duration': 1}
]

function firstDefined(obj) {
    for (var i = 1; i < arguments.length; i++) {
        if (typeof obj[arguments[i]] !== 'undefined') {
            return obj[arguments[i]];
        }
    }
}

function updateKnownMaxRange(stream) {
    assert(stream.id in activeStreams);

    var activeStream = activeStreams[stream.id];

    if (!('range' in activeStream)) {
        activeStream.range = {};
    }

    var firstDatapoint = stream.datapoints[0];
    var lastDatapoint = stream.datapoints[stream.datapoints.length - 1];

    // We go through downsampled timestamps in such order to maximize the range
    var start = $.isPlainObject(firstDatapoint.t) ? firstDefined(firstDatapoint.t, 'a', 'e', 'm', 'z') : firstDatapoint.t;
    var end = $.isPlainObject(lastDatapoint.t) ? firstDefined(lastDatapoint.t, 'z', 'm', 'e', 'a') : lastDatapoint.t;

    if ((typeof start !== 'undefined') && ((typeof activeStream.range.start === 'undefined') || (moment.utc(start).valueOf() < activeStream.range.start))) {
        activeStream.range.start = moment.utc(start).valueOf();
    }
    if ((typeof end !== 'undefined') && ((typeof activeStream.range.end === 'undefined') || (moment.utc(end).valueOf() > activeStream.range.end))) {
        activeStream.range.end = moment.utc(end).valueOf();
    }
}

function initializePlot() {
    plot = new Highcharts.StockChart({
        'chart': {
            'renderTo': 'plot',
            'type': 'spline',
            'zoomType': 'x',
            'borderRadius': 10
        },
        'credits': {
            'enabled': false
        },
        'navigator': {
            'adaptToUpdatedData': false,
            'series': {
                'data': [] // We will set data manually
            }
        },
        'scrollbar': {
            'liveRedraw': false
        },
        'legend': {
            'enabled': true,
            'verticalAlign': 'top',
            'floating': true,
            'padding': 5
        },
        'rangeSelector': {
            'buttonTheme': {
                'width': 50
            },
            'buttons': [
                {
                    'type': 'day',
                    'count': 1,
                    'text': "day"
                },
                {
                    'type': 'week',
                    'count': 1,
                    'text': "week"
                },
                {
                    'type': 'month',
                    'count': 1,
                    'text': "month"
                },
                {
                    'type': 'year',
                    'count': 1,
                    'text': "year"
                },
                {
                    'type': 'all',
                    'text': "all"
                }
            ],
            'selected': 4 // All
        },
        'xAxis': {
            'events': {
                'afterSetExtremes': reloadGraphData
            },
            'minRange': width * 1000 // TODO: Should this depend on possible granularity for the stream(s)?
        },
        'yAxis': [],
        'plotOptions': {
            'series': {
                'marker': {
                    'enabled': true,
                    'radius': 3
                },
                'dataGrouping': {
                    'enabled': false
                }
            }
        },
        'series': []
    });
}

function tagsToObject(tags) {
    result = {};
    $.each(tags, function (i, tag) {
        if ($.isPlainObject(tag)) {
            $.extend(result, tag);
        }
        else {
            result[tag] = tag;
        }
    });
    return result;
}

function convertDatapoints(datapoints) {
    line = []
    range = []
    $.each(datapoints, function (i, datapoint) {
        var t = moment.utc($.isPlainObject(datapoint.t) ? datapoint.t.m : datapoint.t).valueOf();
        if ($.isPlainObject(datapoint.v)) {
            line.push([t, datapoint.v.m]);
            range.push([t, datapoint.v.l, datapoint.v.u]);
        }
        else {
            line.push([t, datapoint.v]);
            range.push([t, datapoint.v, datapoint.v]);
        }
    });
    return {
        'line': line,
        'range': range
    };
}

function computeRange(min, max) {
    var range = {
        'granularity': granularities[0]
    }

    if (!$.isNumeric(min) || !$.isNumeric(min)) {
        return range;
    }

    range.start = min / 1000;
    range.end = max / 1000;

    var interval = range.end - range.start;

    $.each(granularities, function (i, granularity) {
        if (interval / granularity.duration > width) {
            range.granularity = granularity;
            return false;
        }
    });

    range.start -= range.granularity.duration / 2;
    range.end += range.granularity.duration / 2;

    range.start = parseInt(Math.floor(range.start));
    range.end = parseInt(Math.floor(range.end));

    return range;
}

var loadingShown = 0;

function showLoading() {
    assert(loadingShown >= 0);

    loadingShown++;
    if (loadingShown === 1) {
        plot.showLoading("Loading data from server...");
    }
}

function hideLoading() {
    assert(loadingShown > 0);

    loadingShown--;
    if (loadingShown === 0) {
        plot.hideLoading();
    }
}

function reloadGraphData(event) {
    var range = computeRange(event.min, event.max);
    var streams = $.map(activeStreams, function (stream, id) {
        return stream;
    });
    showLoading();
    async.each(streams, function (stream, cb) {
        $.getJSON(stream.resource_uri, {
            'granularity': range.granularity.name,
            'limit': 10000,
            'start': range.start,
            'end': range.end
        }, function (data, textStatus, jqXHR) {
            assert.equal(data.id, stream.id);

            updateKnownMaxRange(data);

            var datapoints = convertDatapoints(data.datapoints);
            plot.get('line-' + stream.id).setData(datapoints.line);
            plot.get('range-' + stream.id).setData(datapoints.range);

            cb();
        }).fail(function () {
            cb(arguments);
        });
    }, function (err) {
        hideLoading();
    });
}

function addPlotData(stream) {
    var range = computeRange(plot.xAxis[0].userMin, plot.xAxis[0].userMax);
    showLoading();
    $.getJSON(stream.resource_uri, {
        'granularity': range.granularity.name,
        'limit': 10000,
        'start': range.start,
        'end': range.end
    }, function (data, textStatus, jqXHR) {
        assert.equal(data.id, stream.id);

        updateKnownMaxRange(data);

        var datapoints = convertDatapoints(data.datapoints);

        plot.addAxis({
            'id': 'axis-' + stream.id,
            'title': {
                'text': stream.tags.unit
            },
            'showEmpty': false
        });
        var series = plot.addSeries({
            'id': 'range-' + stream.id,
            'name': stream.tags.name,
            'yAxis': 'axis-' + stream.id,
            'type': 'arearange',
            'lineWidth': 0,
            'fillOpacity': 0.3,
            'tooltip': {
                'pointFormat': '{series.name} min/max: {point.low} - {point.high}
',
                'valueDecimals': 3
            },
            'data': datapoints.range
        });
        // Match yAxis title color with series color
        series.yAxis.axisTitle.css({'color': series.color});
        plot.addSeries({
            'id': 'line-' + stream.id,
            'name': stream.tags.name,
            'linkedTo': 'range-' + stream.id,
            'color': series.color,
            'yAxis': 'axis-' + stream.id,
            'tooltip': {
                'pointFormat': '{series.name} mean: {point.y}
',
                'valueDecimals': 3
            },
            'data': datapoints.line
        });
        var navigator = plot.get('highcharts-navigator-series');
        if (navigator.data.length === 0) {
            // TODO: Should we set some better data for navigator?
            navigator.setData(datapoints.line);
        }
    }).always(function () {
        hideLoading();
    });
}

var activeStreams = {};
var plot = null;

$(document).ready(function () {
    $('#streams').empty();

    $.getJSON('/api/v1/stream/', function (data, textStatus, jqXHR) {
        $.each(data.objects, function (i, stream) {
            if (data.id in activeStreams) return;

            stream.tags = tagsToObject(stream.tags);

            $('
').data(stream).html(prettyPrint(stream.tags)).appendTo('#streams').click(function (e) {
                activeStreams[stream.id] = stream;
                addPlotData(stream);
                $(this).remove();
            });
        });
    });

    $(document).ajaxError(function (event, jqXHR, ajaxSettings, thrownError) {
        console.error(event, jqXHR, ajaxSettings, thrownError);
    });

    initializePlot();
});<|endoftext|>


<commit_before>define(['knockout'], function (ko) {
    var name = 'number-datatype-config';
    ko.components.register(name, {
        viewModel: function(params) {
            var self = this;
            this.search = params.search;
            if (this.search) {
                var filter = params.filterValue();
                this.op = ko.observable(filter.op || '~');
                this.searchValue = ko.observable(filter.val || '');
                this.filterValue = ko.computed(function () {
                    return {
                        op: self.op(),
                        val: self.searchValue()
                    }
                }).extend({ throttle: 750 });
                params.filterValue(this.filterValue());
                this.filterValue.subscribe(function (val) {
                    params.filterValue(val);
                });
            }
        },
        template: { require: 'text!datatype-config-templates/number' }
    });
    return name;
});
<commit_msg>A little clean up, re #2027<commit_after>define(['knockout'], function (ko) {
    var name = 'number-datatype-config';
    ko.components.register(name, {
        viewModel: function(params) {
            var self = this;
            this.search = params.search;
            if (this.search) {
                var filter = params.filterValue();
                this.op = ko.observable(filter.op || '');
                this.searchValue = ko.observable(filter.val || '');
                this.filterValue = ko.computed(function () {
                    return {
                        op: self.op(),
                        val: self.searchValue()
                    }
                }).extend({ throttle: 750 });
                params.filterValue(this.filterValue());
                this.filterValue.subscribe(function (val) {
                    params.filterValue(val);
                });
            }
        },
        template: { require: 'text!datatype-config-templates/number' }
    });
    return name;
});
<|endoftext|>