Commit 6bdb4f34 by Rashid Khan

Upgraded to angular 1.1.5, refactored filter refreshing, changed panels to destroy scope on hide

parent a99c81d2
...@@ -47,7 +47,7 @@ function (angular) { ...@@ -47,7 +47,7 @@ function (angular) {
'<span class="row-button extra" ng-show="panelMeta.loading == true">' + '<span class="row-button extra" ng-show="panelMeta.loading == true">' +
'<span>'+ '<span>'+
'<i class="icon-spinner smaller icon-spin icon-large"></i>' + '<i class="icon-spinner icon-spin icon-large"></i>' +
'</span>'+ '</span>'+
'</span>' + '</span>' +
......
...@@ -61,7 +61,7 @@ ...@@ -61,7 +61,7 @@
<div ng-hide="filterSrv.list[id].editing && isEditable(filterSrv.list[id])"> <div ng-hide="filterSrv.list[id].editing && isEditable(filterSrv.list[id])">
<ul class="unstyled"> <ul class="unstyled">
<li ng-repeat="(key,value) in filterSrv.list[id]" ng-show="show_key(key)"><strong>{{key}}</strong> : {{value}}</li> <li ng-repeat="(key,value) in filterSrv.list[id] track by $index" ng-show="show_key(key)"><strong>{{key}}</strong> : {{value}}</li>
</ul> </ul>
</div> </div>
<div ng-show="filterSrv.list[id].editing && isEditable(filterSrv.list[id])"> <div ng-show="filterSrv.list[id].editing && isEditable(filterSrv.list[id])">
......
...@@ -33,16 +33,16 @@ function (angular, app, _) { ...@@ -33,16 +33,16 @@ function (angular, app, _) {
$scope.remove = function(id) { $scope.remove = function(id) {
filterSrv.remove(id); filterSrv.remove(id);
dashboard.refresh();
}; };
// This function should be moved to the service
$scope.toggle = function(id) { $scope.toggle = function(id) {
filterSrv.list[id].active = !filterSrv.list[id].active; filterSrv.list[id].active = !filterSrv.list[id].active;
dashboard.refresh(); dashboard.refresh();
}; };
$scope.refresh = function() { $scope.refresh = function() {
$rootScope.$broadcast('refresh'); dashboard.refresh();
}; };
$scope.render = function() { $scope.render = function() {
......
...@@ -312,9 +312,6 @@ function (angular, app, $, _, kbn, moment, timeSeries) { ...@@ -312,9 +312,6 @@ function (angular, app, $, _, kbn, moment, timeSeries) {
to:moment.utc(_to), to:moment.utc(_to),
field:$scope.panel.time_field field:$scope.panel.time_field
}); });
dashboard.refresh();
}; };
// I really don't like this function, too much dom manip. Break out into directive? // I really don't like this function, too much dom manip. Break out into directive?
...@@ -449,7 +446,7 @@ function (angular, app, $, _, kbn, moment, timeSeries) { ...@@ -449,7 +446,7 @@ function (angular, app, $, _, kbn, moment, timeSeries) {
scope.plot = $.plot(elem, scope.data, options); scope.plot = $.plot(elem, scope.data, options);
} catch(e) { } catch(e) {
console.log(e); // Nothing to do here
} }
} }
...@@ -502,7 +499,6 @@ function (angular, app, $, _, kbn, moment, timeSeries) { ...@@ -502,7 +499,6 @@ function (angular, app, $, _, kbn, moment, timeSeries) {
to : moment.utc(ranges.xaxis.to), to : moment.utc(ranges.xaxis.to),
field : scope.panel.time_field field : scope.panel.time_field
}); });
dashboard.refresh();
}); });
} }
}; };
......
...@@ -121,7 +121,6 @@ function (angular, app, _, $) { ...@@ -121,7 +121,6 @@ function (angular, app, _, $) {
$scope.build_search = function(field,value) { $scope.build_search = function(field,value) {
filterSrv.set({type:'querystring',mandate:'must',query:field+":"+value}); filterSrv.set({type:'querystring',mandate:'must',query:field+":"+value});
dashboard.refresh();
}; };
}); });
......
...@@ -175,7 +175,7 @@ define([ ...@@ -175,7 +175,7 @@ define([
}); });
module.directive('pie', function(querySrv, filterSrv, dashboard) { module.directive('pie', function(querySrv, filterSrv) {
return { return {
restrict: 'A', restrict: 'A',
link: function(scope, elem) { link: function(scope, elem) {
...@@ -270,7 +270,6 @@ define([ ...@@ -270,7 +270,6 @@ define([
} }
if(scope.panel.mode === 'terms') { if(scope.panel.mode === 'terms') {
filterSrv.set({type:'terms',field:scope.panel.query.field,value:object.series.label}); filterSrv.set({type:'terms',field:scope.panel.query.field,value:object.series.label});
dashboard.refresh();
} }
}); });
......
...@@ -41,4 +41,4 @@ ...@@ -41,4 +41,4 @@
<div class="progress" ng-show="micropanel.grouped"> <div class="progress" ng-show="micropanel.grouped">
<div ng-repeat='field in micropanel.values' bs-tooltip="field[0]+' ('+percent(field[1],data.length)+')'" class="bar {{micropanelColor($index)}}" ng-style="{width: percent(field[1],data.length)};"></div> <div ng-repeat='field in micropanel.values' bs-tooltip="field[0]+' ('+percent(field[1],data.length)+')'" class="bar {{micropanelColor($index)}}" ng-style="{width: percent(field[1],data.length)};"></div>
</div> </div>
<span ng-repeat='(field,count) in micropanel.related'><a ng-click="toggle_field(field)">{{field}}</a> ({{Math.round((count / micropanel.count) * 100)}}%), </span> <span ng-repeat='(field,count) in micropanel.related track by $index'><a ng-click="toggle_field(field)">{{field}}</a> ({{Math.round((count / micropanel.count) * 100)}}%), </span>
\ No newline at end of file \ No newline at end of file
...@@ -160,14 +160,12 @@ function (angular, app, _, kbn, moment) { ...@@ -160,14 +160,12 @@ function (angular, app, _, kbn, moment) {
} else { } else {
query = angular.toJson(value); query = angular.toJson(value);
} }
filterSrv.set({type:'field',field:field,query:query,mandate:(negate ? 'mustNot':'must')});
$scope.panel.offset = 0; $scope.panel.offset = 0;
dashboard.refresh(); filterSrv.set({type:'field',field:field,query:query,mandate:(negate ? 'mustNot':'must')});
}; };
$scope.fieldExists = function(field,mandate) { $scope.fieldExists = function(field,mandate) {
filterSrv.set({type:'exists',field:field,mandate:mandate}); filterSrv.set({type:'exists',field:field,mandate:mandate});
dashboard.refresh();
}; };
$scope.get_data = function(segment,query_id) { $scope.get_data = function(segment,query_id) {
......
...@@ -143,7 +143,6 @@ function (angular, app, _, $, kbn) { ...@@ -143,7 +143,6 @@ function (angular, app, _, $, kbn) {
} else { } else {
return; return;
} }
dashboard.refresh();
}; };
$scope.set_refresh = function (state) { $scope.set_refresh = function (state) {
......
...@@ -87,9 +87,6 @@ function (angular, app, _, moment, kbn) { ...@@ -87,9 +87,6 @@ function (angular, app, _, moment, kbn) {
set_time_filter($scope.time); set_time_filter($scope.time);
} }
dashboard.refresh();
// Start refresh timer if enabled // Start refresh timer if enabled
if ($scope.panel.refresh.enable) { if ($scope.panel.refresh.enable) {
$scope.set_interval($scope.panel.refresh.interval); $scope.set_interval($scope.panel.refresh.interval);
...@@ -219,8 +216,7 @@ function (angular, app, _, moment, kbn) { ...@@ -219,8 +216,7 @@ function (angular, app, _, moment, kbn) {
// Update internal time object // Update internal time object
// Remove all other time filters // Remove all other time filters
filterSrv.removeByType('time'); filterSrv.removeByType('time',true);
$scope.time = $scope.time_calc(); $scope.time = $scope.time_calc();
$scope.time.field = $scope.panel.timefield; $scope.time.field = $scope.panel.timefield;
...@@ -228,15 +224,14 @@ function (angular, app, _, moment, kbn) { ...@@ -228,15 +224,14 @@ function (angular, app, _, moment, kbn) {
update_panel(); update_panel();
set_time_filter($scope.time); set_time_filter($scope.time);
dashboard.refresh();
}; };
$scope.$watch('panel.mode', $scope.time_apply);
//$scope.$watch('panel.mode', $scope.time_apply);
function set_time_filter(time) { function set_time_filter(time) {
time.type = 'time'; time.type = 'time';
// Clear all time filters, set a new one // Clear all time filters, set a new one
filterSrv.removeByType('time'); filterSrv.removeByType('time',true);
$scope.panel.filter_id = filterSrv.set(compile_time(time)); $scope.panel.filter_id = filterSrv.set(compile_time(time));
return $scope.panel.filter_id; return $scope.panel.filter_id;
} }
......
...@@ -35,7 +35,7 @@ ...@@ -35,7 +35,7 @@
</div> </div>
</div> </div>
<div class="row-fluid" style="padding-top:0px" ng-hide="row.collapse"> <div class="row-fluid" style="padding-top:0px" ng-if="!row.collapse">
<!-- Panels --> <!-- Panels -->
<div ng-repeat="(name, panel) in row.panels|filter:isPanel" ng-hide="panel.span == 0 || panel.hide" class="span{{panel.span}} panel nospace" style="min-height:{{row.height}}; position:relative" data-drop="true" ng-model="row.panels" data-jqyoui-options jqyoui-droppable="{index:$index,mutate:false,onDrop:'panelMoveDrop',onOver:'panelMoveOver(true)',onOut:'panelMoveOut'}"> <div ng-repeat="(name, panel) in row.panels|filter:isPanel" ng-hide="panel.span == 0 || panel.hide" class="span{{panel.span}} panel nospace" style="min-height:{{row.height}}; position:relative" data-drop="true" ng-model="row.panels" data-jqyoui-options jqyoui-droppable="{index:$index,mutate:false,onDrop:'panelMoveDrop',onOver:'panelMoveOver(true)',onOut:'panelMoveOut'}">
...@@ -52,7 +52,7 @@ ...@@ -52,7 +52,7 @@
</div> </div>
</div> </div>
<div ng-hide="(12-rowSpan(row)) < 1 || !dashboard.current.panel_hints" class="panel span{{(12-rowSpan(row))}}" ng-class="{'dragInProgress':dashboard.panelDragging}" style="height:{{row.height}};" data-drop="true" ng-model="row.panels" data-jqyoui-options jqyoui-droppable="{index:row.panels.length,onDrop:'panelMoveDrop({{(12-rowSpan(row))}})',onOver:'panelMoveOver(false)',onOut:'panelMoveOut'}"> <div ng-hide="(12-rowSpan(row)) < 1 || !dashboard.current.panel_hints" class="panel span{{(12-rowSpan(row))}}" ng-class="{'dragInProgress':dashboard.panelDragging}" style="height:{{row.height}};" data-drop="true" ng-model="row.panels" data-jqyoui-options jqyoui-droppable="{index:row.panels.length,mutate:false,onDrop:'panelMoveDrop',onOver:'panelMoveOver',onOut:'panelMoveOut'}">
<span bs-modal="'app/partials/roweditor.html'" ng-show="row.editable && !dashboard.panelDragging"> <span bs-modal="'app/partials/roweditor.html'" ng-show="row.editable && !dashboard.panelDragging">
<i ng-hide="rowSpan(row) == 0" class="pointer icon-plus-sign" ng-click="editor.index = 2" bs-tooltip="'Add a panel to this row'" data-placement="right"></i> <i ng-hide="rowSpan(row) == 0" class="pointer icon-plus-sign" ng-click="editor.index = 2" bs-tooltip="'Add a panel to this row'" data-placement="right"></i>
......
...@@ -7,7 +7,7 @@ define([ ...@@ -7,7 +7,7 @@ define([
var module = angular.module('kibana.services'); var module = angular.module('kibana.services');
module.service('filterSrv', function(dashboard, ejsResource) { module.service('filterSrv', function(dashboard, ejsResource, $rootScope, $timeout) {
// Create an object to hold our service state on the dashboard // Create an object to hold our service state on the dashboard
dashboard.current.services.filter = dashboard.current.services.filter || {}; dashboard.current.services.filter = dashboard.current.services.filter || {};
...@@ -44,19 +44,20 @@ define([ ...@@ -44,19 +44,20 @@ define([
// This is used both for adding filters and modifying them. // This is used both for adding filters and modifying them.
// If an id is passed, the filter at that id is updated // If an id is passed, the filter at that id is updated
this.set = function(filter,id) { this.set = function(filter,id,noRefresh) {
var _r;
_.defaults(filter,{mandate:'must'}); _.defaults(filter,{mandate:'must'});
filter.active = true; filter.active = true;
if(!_.isUndefined(id)) { if(!_.isUndefined(id)) {
if(!_.isUndefined(self.list[id])) { if(!_.isUndefined(self.list[id])) {
_.extend(self.list[id],filter); _.extend(self.list[id],filter);
return id; _r = id;
} else { } else {
return false; _r = false;
} }
} else { } else {
if(_.isUndefined(filter.type)) { if(_.isUndefined(filter.type)) {
return false; _r = false;
} else { } else {
var _id = nextId(); var _id = nextId();
var _filter = { var _filter = {
...@@ -66,9 +67,54 @@ define([ ...@@ -66,9 +67,54 @@ define([
_.defaults(filter,_filter); _.defaults(filter,_filter);
self.list[_id] = filter; self.list[_id] = filter;
self.ids.push(_id); self.ids.push(_id);
return _id; _r = _id;
}
} }
if(!$rootScope.$$phase) {
$rootScope.$apply();
} }
if(noRefresh !== true) {
$timeout(function(){
dashboard.refresh();
},0);
}
return _r;
};
this.remove = function(id,noRefresh) {
var _r;
if(!_.isUndefined(self.list[id])) {
delete self.list[id];
// This must happen on the full path also since _.without returns a copy
self.ids = dashboard.current.services.filter.ids = _.without(self.ids,id);
_f.idQueue.unshift(id);
_f.idQueue.sort(function(v,k){return v-k;});
_r = true;
} else {
_r = false;
}
if(!$rootScope.$$phase) {
$rootScope.$apply();
}
if(noRefresh !== true) {
$timeout(function(){
dashboard.refresh();
},0);
}
return _r;
};
this.removeByType = function(type,noRefresh) {
var ids = self.idsByType(type);
_.each(ids,function(id) {
self.remove(id,true);
});
if(noRefresh !== true) {
$timeout(function(){
dashboard.refresh();
},0);
}
return ids;
}; };
this.getBoolFilter = function(ids) { this.getBoolFilter = function(ids) {
...@@ -106,6 +152,7 @@ define([ ...@@ -106,6 +152,7 @@ define([
case 'time': case 'time':
return ejs.RangeFilter(filter.field) return ejs.RangeFilter(filter.field)
.from(filter.from.valueOf()) .from(filter.from.valueOf())
//.from("now-1d")
.to(filter.to.valueOf()); .to(filter.to.valueOf());
case 'range': case 'range':
return ejs.RangeFilter(filter.field) return ejs.RangeFilter(filter.field)
...@@ -130,14 +177,6 @@ define([ ...@@ -130,14 +177,6 @@ define([
return _.pick(self.list,self.idsByType(type,inactive)); return _.pick(self.list,self.idsByType(type,inactive));
}; };
this.removeByType = function(type) {
var ids = self.idsByType(type);
_.each(ids,function(id) {
self.remove(id);
});
return ids;
};
this.idsByType = function(type,inactive) { this.idsByType = function(type,inactive) {
var _require = inactive ? {type:type} : {type:type,active:true}; var _require = inactive ? {type:type} : {type:type,active:true};
return _.pluck(_.where(self.list,_require),'id'); return _.pluck(_.where(self.list,_require),'id');
...@@ -171,20 +210,6 @@ define([ ...@@ -171,20 +210,6 @@ define([
} }
}; };
this.remove = function(id) {
if(!_.isUndefined(self.list[id])) {
delete self.list[id];
// This must happen on the full path also since _.without returns a copy
self.ids = dashboard.current.services.filter.ids = _.without(self.ids,id);
_f.idQueue.unshift(id);
_f.idQueue.sort(function(v,k){return v-k;});
return true;
} else {
return false;
}
};
var nextId = function() { var nextId = function() {
if(_f.idQueue.length > 0) { if(_f.idQueue.length > 0) {
return _f.idQueue.shift(); return _f.idQueue.shift();
......
/** /**
* @license AngularJS v1.0.8 * @license AngularJS v1.1.5
* (c) 2010-2012 Google, Inc. http://angularjs.org * (c) 2010-2012 Google, Inc. http://angularjs.org
* License: MIT * License: MIT
*/ */
...@@ -10,25 +10,6 @@ ...@@ -10,25 +10,6 @@
* @ngdoc overview * @ngdoc overview
* @name ngSanitize * @name ngSanitize
* @description * @description
*
* The `ngSanitize` module provides functionality to sanitize HTML.
*
* # Installation
* As a separate module, it must be loaded after Angular core is loaded; otherwise, an 'Uncaught Error:
* No module: ngSanitize' runtime error will occur.
*
* <pre>
* <script src="angular.js"></script>
* <script src="angular-sanitize.js"></script>
* </pre>
*
* # Usage
* To make sure the module is available to your application, declare it as a dependency of you application
* module.
*
* <pre>
* angular.module('app', ['ngSanitize']);
* </pre>
*/ */
/* /*
...@@ -148,7 +129,7 @@ var START_TAG_REGEXP = /^<\s*([\w:-]+)((?:\s+[\w:-]+(?:\s*=\s*(?:(?:"[^"]*")|(?: ...@@ -148,7 +129,7 @@ var START_TAG_REGEXP = /^<\s*([\w:-]+)((?:\s+[\w:-]+(?:\s*=\s*(?:(?:"[^"]*")|(?:
BEGING_END_TAGE_REGEXP = /^<\s*\//, BEGING_END_TAGE_REGEXP = /^<\s*\//,
COMMENT_REGEXP = /<!--(.*?)-->/g, COMMENT_REGEXP = /<!--(.*?)-->/g,
CDATA_REGEXP = /<!\[CDATA\[(.*?)]]>/g, CDATA_REGEXP = /<!\[CDATA\[(.*?)]]>/g,
URI_REGEXP = /^((ftp|https?):\/\/|mailto:|#)/i, URI_REGEXP = /^((ftp|https?):\/\/|mailto:|tel:|#)/,
NON_ALPHANUMERIC_REGEXP = /([^\#-~| |!])/g; // Match everything outside of normal chars and " (quote character) NON_ALPHANUMERIC_REGEXP = /([^\#-~| |!])/g; // Match everything outside of normal chars and " (quote character)
...@@ -302,10 +283,10 @@ function htmlParser( html, handler ) { ...@@ -302,10 +283,10 @@ function htmlParser( html, handler ) {
var attrs = {}; var attrs = {};
rest.replace(ATTR_REGEXP, function(match, name, doubleQuotedValue, singleQuotedValue, unquotedValue) { rest.replace(ATTR_REGEXP, function(match, name, doubleQuotedValue, singleQoutedValue, unqoutedValue) {
var value = doubleQuotedValue var value = doubleQuotedValue
|| singleQuotedValue || singleQoutedValue
|| unquotedValue || unqoutedValue
|| ''; || '';
attrs[name] = decodeEntities(value); attrs[name] = decodeEntities(value);
...@@ -452,6 +433,7 @@ angular.module('ngSanitize').directive('ngBindHtml', ['$sanitize', function($san ...@@ -452,6 +433,7 @@ angular.module('ngSanitize').directive('ngBindHtml', ['$sanitize', function($san
* plain email address links. * plain email address links.
* *
* @param {string} text Input text. * @param {string} text Input text.
* @param {string} target Window (_blank|_self|_parent|_top) or named frame to open links in.
* @returns {string} Html-linkified text. * @returns {string} Html-linkified text.
* *
* @usage * @usage
...@@ -468,6 +450,7 @@ angular.module('ngSanitize').directive('ngBindHtml', ['$sanitize', function($san ...@@ -468,6 +450,7 @@ angular.module('ngSanitize').directive('ngBindHtml', ['$sanitize', function($san
'mailto:us@somewhere.org,\n'+ 'mailto:us@somewhere.org,\n'+
'another@somewhere.org,\n'+ 'another@somewhere.org,\n'+
'and one more: ftp://127.0.0.1/.'; 'and one more: ftp://127.0.0.1/.';
$scope.snippetWithTarget = 'http://angularjs.org/';
} }
</script> </script>
<div ng-controller="Ctrl"> <div ng-controller="Ctrl">
...@@ -487,6 +470,15 @@ angular.module('ngSanitize').directive('ngBindHtml', ['$sanitize', function($san ...@@ -487,6 +470,15 @@ angular.module('ngSanitize').directive('ngBindHtml', ['$sanitize', function($san
<div ng-bind-html="snippet | linky"></div> <div ng-bind-html="snippet | linky"></div>
</td> </td>
</tr> </tr>
<tr id="linky-target">
<td>linky target</td>
<td>
<pre>&lt;div ng-bind-html="snippetWithTarget | linky:'_blank'"&gt;<br>&lt;/div&gt;</pre>
</td>
<td>
<div ng-bind-html="snippetWithTarget | linky:'_blank'"></div>
</td>
</tr>
<tr id="escaped-html"> <tr id="escaped-html">
<td>no filter</td> <td>no filter</td>
<td><pre>&lt;div ng-bind="snippet"&gt;<br>&lt;/div&gt;</pre></td> <td><pre>&lt;div ng-bind="snippet"&gt;<br>&lt;/div&gt;</pre></td>
...@@ -519,6 +511,11 @@ angular.module('ngSanitize').directive('ngBindHtml', ['$sanitize', function($san ...@@ -519,6 +511,11 @@ angular.module('ngSanitize').directive('ngBindHtml', ['$sanitize', function($san
toBe('new <a href="http://link">http://link</a>.'); toBe('new <a href="http://link">http://link</a>.');
expect(using('#escaped-html').binding('snippet')).toBe('new http://link.'); expect(using('#escaped-html').binding('snippet')).toBe('new http://link.');
}); });
it('should work with the target property', function() {
expect(using('#linky-target').binding("snippetWithTarget | linky:'_blank'")).
toBe('<a target="_blank" href="http://angularjs.org/">http://angularjs.org/</a>');
});
</doc:scenario> </doc:scenario>
</doc:example> </doc:example>
*/ */
...@@ -526,7 +523,7 @@ angular.module('ngSanitize').filter('linky', function() { ...@@ -526,7 +523,7 @@ angular.module('ngSanitize').filter('linky', function() {
var LINKY_URL_REGEXP = /((ftp|https?):\/\/|(mailto:)?[A-Za-z0-9._%+-]+@)\S*[^\s\.\;\,\(\)\{\}\<\>]/, var LINKY_URL_REGEXP = /((ftp|https?):\/\/|(mailto:)?[A-Za-z0-9._%+-]+@)\S*[^\s\.\;\,\(\)\{\}\<\>]/,
MAILTO_REGEXP = /^mailto:/; MAILTO_REGEXP = /^mailto:/;
return function(text) { return function(text, target) {
if (!text) return text; if (!text) return text;
var match; var match;
var raw = text; var raw = text;
...@@ -535,6 +532,10 @@ angular.module('ngSanitize').filter('linky', function() { ...@@ -535,6 +532,10 @@ angular.module('ngSanitize').filter('linky', function() {
var writer = htmlSanitizeWriter(html); var writer = htmlSanitizeWriter(html);
var url; var url;
var i; var i;
var properties = {};
if (angular.isDefined(target)) {
properties.target = target;
}
while ((match = raw.match(LINKY_URL_REGEXP))) { while ((match = raw.match(LINKY_URL_REGEXP))) {
// We can not end in these as they are sometimes found at the end of the sentence // We can not end in these as they are sometimes found at the end of the sentence
url = match[0]; url = match[0];
...@@ -542,7 +543,8 @@ angular.module('ngSanitize').filter('linky', function() { ...@@ -542,7 +543,8 @@ angular.module('ngSanitize').filter('linky', function() {
if (match[2] == match[3]) url = 'mailto:' + url; if (match[2] == match[3]) url = 'mailto:' + url;
i = match.index; i = match.index;
writer.chars(raw.substr(0, i)); writer.chars(raw.substr(0, i));
writer.start('a', {href:url}); properties.href = url;
writer.start('a', properties);
writer.chars(match[0].replace(MAILTO_REGEXP, '')); writer.chars(match[0].replace(MAILTO_REGEXP, ''));
writer.end('a'); writer.end('a');
raw = raw.substring(i + match[0].length); raw = raw.substring(i + match[0].length);
......
This source diff could not be displayed because it is too large. You can view the blob instead.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment