// Generated by CoffeeScript 1.9.3 var Metadata, module; module = angular.module('diffy', [], function($interpolateProvider, $locationProvider) { $interpolateProvider.startSymbol('[['); $interpolateProvider.endSymbol(']]'); return $locationProvider.html5Mode(false); }); module.service('router', function($timeout, $rootScope) { var router, routes; router = new Grapnel(); routes = []; return function(route, callback) { return router.add(route, function(req) { return $timeout(function() { callback(req); return $rootScope.$apply(); }); }); }; }); module.factory('endpointInclusions', function() { return {}; }); module.factory('info', function() { return {}; }); module.factory('percentageRating', function() { return function(percent) { if (percent < .1) { return "perfect"; } else if (percent < 5) { return "almost"; } else if (percent < 50) { return "neutral"; } else { return "poor"; } }; }); module.filter('ago', function() { return function(input) { if (input <= 0) { return "forever"; } else { return moment(input).fromNow(); } }; }); module.filter('formatDate', function() { return function(input) { if (input <= 0) { return "-"; } return moment(input).format("lll"); }; }); module.directive('ngOnEsc', function() { return function(scope, element, attrs) { return $(document).bind('keydown keypress', function(event) { if (event.keyCode === 27) { scope.$eval(attrs.ngOnEsc); scope.$apply(); return event.preventDefault(); } }); }; }); module.directive('deepLink', function($rootScope) { return function(scope, element, attrs) { return element.bind('click', function(event) { var link; link = scope.$eval(attrs.deepLink); if ($.isArray(link)) { link = link.join("/"); } return window.location = '#/' + link; }); }; }); module.directive('requestTreeHighlight', function($rootScope, $timeout) { return function(scope, element, attrs) { return scope.$root.$on("highlight_differences", function(event, pathSelectors) { return $timeout(function() { var i, path, results; $rootScope.$apply(); results = []; for (i in pathSelectors) { path = pathSelectors[i]; results.push(jQuery(path + " strong").addClass('difference')); } return results; }, 0); }); }; }); module.service('api', function($rootScope) { return function(url, params, success) { params = $.extend(params, {}); return $.get(apiRoot + "/api/1/" + url, params, function() { success.apply(this, arguments); return $rootScope.$apply(); }).fail(function() { return console.log(arguments); }); }; }); Metadata = (function() { function Metadata() { this.metadata = {}; this.defaults = {}; this.prefix = function() { return "default"; }; } Metadata.prototype.get = function(key, prefix) { if (prefix == null) { prefix = this.prefix(); } if (this.metadata[prefix] === void 0) { this.metadata[prefix] = {}; } if (this.metadata[prefix][key] === void 0) { this.metadata[prefix][key] = $.extend(true, {}, this.defaults); } return this.metadata[prefix][key]; }; Metadata.prototype.each = function(func) { var key, ref, results, value; ref = this.metadata[this.prefix()]; results = []; for (key in ref) { value = ref[key]; results.push(func(key, value)); } return results; }; return Metadata; })(); module.factory('globalFields', function() { return {}; }); module.factory('globalMetadata', function() { return new Metadata(); }); module.controller('EndpointController', function($scope, $interval, api, router, info, globalMetadata, globalFields, endpointInclusions, percentageRating) { var metadata, sortEndpoints; $scope.globalExclusion = false; $scope.endpoints = {}; $scope.sortedEndpoints = []; $scope.metadata = new Metadata(); $scope.percentageRating = percentageRating; $scope.inclusionPercentage = function(ep) { if (ep in endpointInclusions) { return endpointInclusions[ep]; } else { return 1; } }; $scope.info = info; metadata = $scope.metadata; metadata.defaults.selected = false; router('/ep/:endpoint/:path?/:id?', function(req) { metadata.each(function(key, item) { if (item.selected) { return item.selected = false; } }); return metadata.get(req.params.endpoint).selected = true; }); $scope.size = Object.size; $scope.$root.$on('exclusions_updated', function(event) { return sortEndpoints(); }); $scope.showSettings = function() { return $scope.$root.$emit('show_settings'); }; $scope.loadEndpoints = function() { return api("endpoints", {}, function(endpoints) { var endpointObjs; if (!_.isEqual(endpoints, $scope.endpoints)) { endpointObjs = _.map(endpoints, function(stats, endpoint) { return { name: endpoint, failureRate: stats.differences / stats.total, inclusion: $scope.inclusionPercentage(endpoint), stats: stats }; }); $scope.endpoints = endpointObjs; return sortEndpoints(); } }); }; sortEndpoints = function() { return $scope.endpoints.sort(function(a, b) { var aInclusion, bInclusion; aInclusion = $scope.inclusionPercentage(a.name); bInclusion = $scope.inclusionPercentage(b.name); if (a.failureRate * aInclusion > b.failureRate * bInclusion) { return -1; } else if (a.failureRate * aInclusion < b.failureRate * bInclusion) { return 1; } else if (a.name < b.name) { return -1; } else { return 1; } }); }; $scope.excludeAll = function() { var endpoint, endpoints, i, ref; endpoints = []; ref = $scope.endpoints; for (i in ref) { endpoint = ref[i]; endpoints.push(endpoint.name); } return $scope.$root.$emit('toggle_global_exclusion', endpoints, $scope.globalExclusion); }; $scope.apiInterval = $interval($scope.loadEndpoints, 2000); return $scope.loadEndpoints(); }); module.controller('FieldsController', function($scope, api, router, globalMetadata, globalFields, endpointInclusions, percentageRating) { var metadata; $scope.loading = false; $scope.percentageRating = percentageRating; metadata = globalMetadata; metadata.defaults.included = true; metadata.prefix = function() { return $scope.endpointName; }; router('/ep/:endpoint/:path?/:id?', function(req) { if ($scope.endpointName !== req.params.endpoint) { clearInterval($scope.loadFieldsInterval); $scope.endpointName = req.params.endpoint; $scope.loadFields(false); return $scope.loadFieldsInterval = setInterval(function() { return $scope.loadFields(true); }, 5000); } }); $scope.expandFields = function(fields, endpointName) { var item, obj, path; obj = {}; for (path in fields) { item = fields[path]; $scope.addField(obj, path, item, endpointName); } return obj; }; $scope.iterateFields = function(field, func, endpointName) { var iterate; iterate = function(children) { var child, name, results; results = []; for (name in children) { child = children[name]; func(child, metadata.get(child.path, endpointName)); if (child.children) { results.push(iterate(child.children)); } else { results.push(void 0); } } return results; }; return iterate(field); }; $scope.addField = function(obj, path, item, endpointName) { var currentObj, currentPath, fieldName, i, k, len, meta, results; if (typeof path === "string") { path = path.split("."); } currentObj = obj; currentPath = ""; results = []; for (i = k = 0, len = path.length; k < len; i = ++k) { fieldName = path[i]; currentPath += fieldName; meta = metadata.get(currentPath, endpointName); if (i < path.length - 1) { currentObj[fieldName] = currentObj[fieldName] || { path: currentPath, children: {} }; if (!meta.differences) { meta.differences = 0; } currentObj[fieldName].path = currentPath; currentObj = currentObj[fieldName].children; } else { currentObj[fieldName] = $.extend(currentObj[fieldName], { path: currentPath, terminal: true }, item); meta.differences = item.differences; meta.weight = item.weight; meta.includedWeights = item.weight; meta.noise = item.noise; } results.push(currentPath += "."); } return results; }; $scope.expandAll = function() { return $scope.iterateFields($scope.fields, function(field, meta) { return meta.collapsed = false; }); }; $scope.clearExclusions = function(endpointName) { $scope.iterateFields(globalFields[endpointName], function(field, meta) { return meta.included = true; }, endpointName); return $scope.traverseFields(globalFields[endpointName], endpointName); }; $scope.inspectFields = function(children, path, endpointName) { var child, childMeta, childrenHaveChildren, meta, name; meta = metadata.get(path, endpointName); meta.weight = 0; meta.includedWeights = 0; meta.childrenIncluded = false; childrenHaveChildren = false; for (name in children) { child = children[name]; childMeta = metadata.get(child.path, endpointName); if (child.children) { childrenHaveChildren = true; if (!childMeta.terminal) { $scope.inspectFields(child.children, child.path, endpointName); } if (childMeta.childrenIncluded && childMeta.included) { meta.childrenIncluded = true; } } else { if (childMeta.included) { meta.childrenIncluded = true; } } meta.weight += childMeta.weight; if (childMeta.included) { meta.includedWeights += childMeta.includedWeights; } } if (meta.collapsed === void 0) { return meta.collapsed = !childrenHaveChildren; } }; $scope.traverseFields = function(field, endpointName) { var child, includedWeights, inclusionsPercentage, meta, name, weight; $scope.inspectFields(field, void 0, endpointName); weight = 0; includedWeights = 0; for (name in field) { child = field[name]; meta = metadata.get(name, endpointName); weight += meta.weight; if (meta.included) { includedWeights += meta.includedWeights; } } inclusionsPercentage = 0; if (includedWeights && weight) { inclusionsPercentage = includedWeights / weight; } if (endpointName === $scope.endpointName) { $scope.diffs = Math.ceil(inclusionsPercentage * $scope.endpoint.differences); $scope.percentage = ((inclusionsPercentage * $scope.endpoint.differences) / $scope.endpoint.total) * 100; } if (endpointInclusions[endpointName] !== inclusionsPercentage) { endpointInclusions[endpointName] = inclusionsPercentage; return $scope.$root.$emit('exclusions_updated'); } }; $scope.autoExclude = function(endpointName) { $scope.iterateFields(globalFields[endpointName], function(field, meta) { if (field.terminal) { return meta.included = field.differences > field.noise && field.relative_difference > relativeThreshold && field.absolute_difference > absoluteThreshold; } }, endpointName); return $scope.traverseFields(globalFields[endpointName], endpointName); }; $scope.collapseExcluded = function() { $scope.iterateFields($scope.fields, function(field, meta) { if (!meta.included || !meta.childrenIncluded) { return meta.collapsed = true; } }); return $scope.traverseFields($scope.fields, $scope.endpointName); }; $scope.size = Object.size; $scope.pathSelected = function(path) { metadata.each(function(key, item) { if (item.selected) { return item.selected = false; } }); metadata.get(path).selected = true; return $scope.$root.$emit('load_path', $scope.endpointName, path); }; $scope.hasFields = function() { return Object.size($scope.rawFields) > 0; }; $scope.fields = null; $scope.rawFields = null; $scope.loadFields = function(hideLoader) { $scope.loading = !hideLoader; if (!hideLoader) { $scope.fields = null; $scope.rawFields = null; } api('endpoints/' + $scope.endpointName + '/stats', { "include_weights": true, "exclude_noise": excludeNoise }, function(response) { $scope.endpoint = response.endpoint; $scope.loading = false; if (!_.isEqual(response.fields, $scope.rawFields)) { $scope.fields = $scope.expandFields(response.fields, $scope.endpointName); globalFields[$scope.endpointName] = $scope.fields; $scope.traverseFields($scope.fields, $scope.endpointName); } return $scope.rawFields = response.fields; }); return $scope.getGlobalMetadata = function(path) { return globalMetadata.get(path); }; }; return $scope.$root.$on('toggle_global_exclusion', function(event, endpoints, globalExclusion) { var endpoint, exclude, i, loadAndExclude, results; exclude = function(endpointName) { if (globalExclusion) { return $scope.autoExclude(endpointName); } else { return $scope.clearExclusions(endpointName); } }; loadAndExclude = function(endpointName) { return api('endpoints/' + endpointName + '/stats', { "include_weights": true, "exclude_noise": excludeNoise }, function(response) { globalFields[endpointName] = $scope.expandFields(response.fields, endpointName); $scope.traverseFields(globalFields[endpointName], endpointName); return exclude(endpointName); }); }; results = []; for (i in endpoints) { endpoint = endpoints[i]; if (globalFields[endpoint] === void 0) { results.push(loadAndExclude(endpoint)); } else { results.push(exclude(endpoint)); } } return results; }); }); module.controller('RequestsController', function($scope, api, router) { $scope.loading = false; router('/ep/:endpoint/:path/:id?', function(req) { if ($scope.endpointName !== req.params.endpoint || $scope.path !== req.params.path) { $scope.path = req.params.path; $scope.endpointName = req.params.endpoint; return $scope.loadPath(req.params.endpoint, req.params.path, false); } }); $scope.loadPath = function(endpoint, path, hideLoader) { $scope.loading = !hideLoader; $scope.requests = false; $scope.path = path; $scope.endpoint = endpoint; return api('endpoints/' + endpoint + '/fields/' + path + '/results', {}, function(response) { var difference, k, len, ref, ref1, request; $scope.loading = false; ref = response.requests; for (k = 0, len = ref.length; k < len; k++) { request = ref[k]; ref1 = request.differences; for (path in ref1) { difference = ref1[path]; difference.collapsed = (path.indexOf($scope.path)) !== 0; } } return $scope.requests = response.requests; }); }; $scope.requestSelected = function(id) { return $scope.$root.$emit('load_request', id); }; $scope.size = Object.size; $scope.countCollapsed = function(diffs) { var diff, i, path; i = 0; for (path in diffs) { diff = diffs[path]; if (diff.collapsed) { i++; } } return i; }; return $scope.uncollapse = function(diffs) { var diff, path, results; results = []; for (path in diffs) { diff = diffs[path]; results.push(diff.collapsed = false); } return results; }; }); module.controller('RequestController', function($scope, api, router) { $scope.size = Object.size; $scope.loading = false; $scope.isObject = $.isPlainObject; $scope.isArray = $.isArray; $scope.requestId = false; $scope.back = function() { return window.location = '#/ep/' + [$scope.endpointName, $scope.path].join('/'); }; router('/ep/:endpoint/:path', function(req) { return $scope.request = null; }); return router('/ep/:endpoint/:path/:id', function(req) { $scope.loading = true; $scope.requestId = req.params.id; $scope.endpointName = req.params.endpoint; $scope.path = req.params.path; return api('requests/' + $scope.requestId, {}, function(response) { var diff, diffs, i, j, p, pathSelector, pathSelectorArray, pathSelectors, ref; $scope.loading = false; $scope.request = response; diffs = DeepDiff($scope.request.left, $scope.request.right); pathSelectors = []; for (i in diffs) { diff = diffs[i]; pathSelectorArray = []; ref = diff.path; for (j in ref) { p = ref[j]; pathSelectorArray.push("[data-path='" + p + "']"); } pathSelector = pathSelectorArray.join(" "); pathSelectors.push(pathSelector); } return $scope.$root.$emit('highlight_differences', pathSelectors); }); }); }); module.controller('SettingsController', function($scope, $timeout, $interval, api, router, info) { var apiInterval, loadInfo; loadInfo = function() { return api('info', {}, function(response) { return $.extend(info, response); }); }; apiInterval = $interval(loadInfo, 10000); loadInfo(); $scope.info = info; $scope.visible = false; $scope.$root.$on('show_settings', function(event) { $scope.visible = true; return $scope.loading = true; }); return $scope.clear = function() { $scope.clearLoading = true; if (confirm("This will reset all counters and clear all the saved requests/responses, are you sure?")) { return api('clear', {}, function(response) { $scope.clearLoading = true; return $timeout(function() { return $scope.clearLoading = false; }, 2000); }); } }; }); Object.size = function(obj) { var key, size; size = 0; for (key in obj) { if (obj.hasOwnProperty(key)) { size++; } } return size; };