mirror of
https://github.com/ail-project/ail-framework.git
synced 2024-11-30 09:47:17 +00:00
chg: [UI preview items] b4 migration + new template + better display + decrease item loading time + add basic animation/transition
This commit is contained in:
parent
e9bac098c9
commit
65e6eae00d
4 changed files with 532 additions and 79 deletions
|
@ -245,6 +245,12 @@ class Paste(object):
|
|||
def get_p_date(self):
|
||||
return self.p_date
|
||||
|
||||
def get_item_source(self):
|
||||
return self.p_source
|
||||
|
||||
def get_item_size(self):
|
||||
return self.p_size
|
||||
|
||||
def _get_p_size(self):
|
||||
return self.p_size
|
||||
|
||||
|
@ -298,6 +304,10 @@ class Paste(object):
|
|||
else:
|
||||
return '[]'
|
||||
|
||||
def get_nb_duplicate(self):
|
||||
# # TODO: FIXME use relative path
|
||||
return self.store_metadata.scard('dup:'+self.p_path) + self.store_metadata.scard('dup:'+self.p_rel_path)
|
||||
|
||||
def _get_p_tags(self):
|
||||
self.p_tags = self.store_metadata.smembers('tag:'+path, tag)
|
||||
if self.self.p_tags is not None:
|
||||
|
|
|
@ -22,6 +22,20 @@
|
|||
<script language="javascript" src="{{ url_for('static', filename='js/jquery.daterangepicker.min.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='js/tags.js') }}"></script>
|
||||
|
||||
<style>
|
||||
.rotate{
|
||||
-moz-transition: all 0.1s linear;
|
||||
-webkit-transition: all 0.1s linear;
|
||||
transition: all 0.1s linear;
|
||||
}
|
||||
|
||||
.rotate.down{
|
||||
-moz-transform:rotate(180deg);
|
||||
-webkit-transform:rotate(180deg);
|
||||
transform:rotate(180deg);
|
||||
}
|
||||
</style>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
@ -119,7 +133,7 @@
|
|||
<td class="pb-0">{{ paste_linenum[loop.index0] }}</td>
|
||||
<td class="pb-0"><p>
|
||||
<span class="fas fa-info-circle" data-toggle="tooltip" data-placement="left" title="{{ content[loop.index0] }} "></span>
|
||||
<button type="button" class="btn btn-light" data-num="{{ loop.index0 + 1 }}" data-toggle="modal" data-target="#mymodal" data-url="{{ url_for('showsavedpastes.showsavedpaste') }}?paste={{ path }}&num={{ loop.index0+1 }}" data-path="{{ path }}">
|
||||
<button type="button" class="btn btn-light" data-num="{{ loop.index0 + 1 }}" data-toggle="modal" data-target="#mymodal" data-url="{{ url_for('showsavedpastes.showsaveditem_min') }}?paste={{ path }}&num={{ loop.index0+1 }}" data-path="{{ path }}">
|
||||
<span class="fas fa-search-plus"></span>
|
||||
</button></p>
|
||||
</td>
|
||||
|
@ -318,16 +332,18 @@ function toggle_sidebar(){
|
|||
var cleared_data = data.split("<body>")[1].split("</body>")[0];
|
||||
$("#mymodalbody").html(cleared_data);
|
||||
|
||||
var button = $('<button type="button" id="load-more-button" class="btn btn-info btn-xs center-block" data-url="' + $(modal).attr('data-path') +'" data-toggle="tooltip" data-placement="bottom" title="Load more content"><i class="fas fa-arrow-circle-down"></i></button>');
|
||||
button.tooltip();
|
||||
$("#mymodalbody").children(".panel-default").append(button);
|
||||
var button = $('<button type="button" id="load-more-button" class="btn btn-outline-primary rounded-circle px-1 py-0" data-url="' + $(modal).attr('data-path') +'" data-toggle="tooltip" data-placement="bottom" title="Load more content"><i class="fas fa-arrow-circle-down mt-1"></i></button>');
|
||||
button.tooltip(button);
|
||||
$("#container-show-more").append(button);
|
||||
|
||||
$("#button_show_path").attr('href', $(modal).attr('data-url'));
|
||||
$("#button_show_path").attr('href', '{{ url_for('showsavedpastes.showsavedpaste') }}?paste=' + $(modal).attr('data-path'));
|
||||
$("#button_show_path").show('fast');
|
||||
$("#loading-gif-modal").css("visibility", "hidden"); // Hide the loading GIF
|
||||
if ($("[data-initsize]").attr('data-initsize') < char_to_display) { // All the content is displayed
|
||||
nothing_to_display();
|
||||
}
|
||||
// collapse decoded
|
||||
$('#collapseDecoded').collapse('hide');
|
||||
// On click, donwload all paste's content
|
||||
$("#load-more-button").on("click", function (event) {
|
||||
if (complete_paste == null) { //Donwload only once
|
||||
|
|
|
@ -223,6 +223,128 @@ def showpaste(content_range, requested_path):
|
|||
crawler_metadata=crawler_metadata,
|
||||
l_64=l_64, vt_enabled=vt_enabled, misp=misp, hive=hive, misp_eventid=misp_eventid, misp_url=misp_url, hive_caseid=hive_caseid, hive_url=hive_url)
|
||||
|
||||
def get_item_basic_info(item):
|
||||
item_basic_info = {}
|
||||
item_basic_info['date'] = str(item.get_p_date())
|
||||
item_basic_info['date'] = '{}/{}/{}'.format(item_basic_info['date'][0:4], item_basic_info['date'][4:6], item_basic_info['date'][6:8])
|
||||
item_basic_info['source'] = item.get_item_source()
|
||||
item_basic_info['size'] = item.get_item_size()
|
||||
|
||||
## TODO: FIXME ##performance
|
||||
item_basic_info['encoding'] = item._get_p_encoding()
|
||||
## TODO: FIXME ##performance
|
||||
#item_basic_info['language'] = item._get_p_language()
|
||||
## TODO: FIXME ##performance
|
||||
info_line = item.get_lines_info()
|
||||
item_basic_info['nb_lines'] = info_line[0]
|
||||
item_basic_info['max_length_line'] = info_line[1]
|
||||
|
||||
return item_basic_info
|
||||
|
||||
def show_item_min(requested_path , content_range=0):
|
||||
relative_path = None
|
||||
if PASTES_FOLDER not in requested_path:
|
||||
relative_path = requested_path
|
||||
requested_path = os.path.join(PASTES_FOLDER, requested_path)
|
||||
else:
|
||||
relative_path = requested_path.replace(PASTES_FOLDER, '', 1)[1:]
|
||||
# remove old full path
|
||||
#requested_path = requested_path.replace(PASTES_FOLDER, '')
|
||||
# escape directory transversal
|
||||
if os.path.commonprefix((os.path.realpath(requested_path),PASTES_FOLDER)) != PASTES_FOLDER:
|
||||
return 'path transversal detected'
|
||||
|
||||
item_info ={}
|
||||
|
||||
paste = Paste.Paste(requested_path)
|
||||
item_basic_info = get_item_basic_info(paste)
|
||||
item_info['nb_duplictates'] = paste.get_nb_duplicate()
|
||||
|
||||
## TODO: use this for fix ?
|
||||
item_content = paste.get_p_content()
|
||||
char_to_display = len(item_content)
|
||||
if content_range != 0:
|
||||
item_content = item_content[0:content_range]
|
||||
|
||||
vt_enabled = Flask_config.vt_enabled
|
||||
|
||||
|
||||
p_hashtype_list = []
|
||||
|
||||
l_tags = r_serv_metadata.smembers('tag:'+requested_path)
|
||||
if relative_path is not None:
|
||||
l_tags.union( r_serv_metadata.smembers('tag:'+relative_path) )
|
||||
item_info['tags'] = l_tags
|
||||
item_info['name'] = relative_path.replace('/', ' / ')
|
||||
|
||||
|
||||
l_64 = []
|
||||
# load hash files
|
||||
if r_serv_metadata.scard('hash_paste:'+requested_path) > 0:
|
||||
set_b64 = r_serv_metadata.smembers('hash_paste:'+requested_path)
|
||||
for hash in set_b64:
|
||||
nb_in_file = int(r_serv_metadata.zscore('nb_seen_hash:'+hash, requested_path))
|
||||
estimated_type = r_serv_metadata.hget('metadata_hash:'+hash, 'estimated_type')
|
||||
file_type = estimated_type.split('/')[0]
|
||||
# set file icon
|
||||
if file_type == 'application':
|
||||
file_icon = 'fa-file '
|
||||
elif file_type == 'audio':
|
||||
file_icon = 'fa-file-video '
|
||||
elif file_type == 'image':
|
||||
file_icon = 'fa-file-image'
|
||||
elif file_type == 'text':
|
||||
file_icon = 'fa-file-alt'
|
||||
else:
|
||||
file_icon = 'fa-file'
|
||||
saved_path = r_serv_metadata.hget('metadata_hash:'+hash, 'saved_path')
|
||||
if r_serv_metadata.hexists('metadata_hash:'+hash, 'vt_link'):
|
||||
b64_vt = True
|
||||
b64_vt_link = r_serv_metadata.hget('metadata_hash:'+hash, 'vt_link')
|
||||
b64_vt_report = r_serv_metadata.hget('metadata_hash:'+hash, 'vt_report')
|
||||
else:
|
||||
b64_vt = False
|
||||
b64_vt_link = ''
|
||||
b64_vt_report = r_serv_metadata.hget('metadata_hash:'+hash, 'vt_report')
|
||||
# hash never refreshed
|
||||
if b64_vt_report is None:
|
||||
b64_vt_report = ''
|
||||
|
||||
l_64.append( (file_icon, estimated_type, hash, saved_path, nb_in_file, b64_vt, b64_vt_link, b64_vt_report) )
|
||||
|
||||
crawler_metadata = {}
|
||||
if 'infoleak:submission="crawler"' in l_tags:
|
||||
crawler_metadata['get_metadata'] = True
|
||||
crawler_metadata['domain'] = r_serv_metadata.hget('paste_metadata:'+requested_path, 'domain')
|
||||
crawler_metadata['paste_father'] = r_serv_metadata.hget('paste_metadata:'+requested_path, 'father')
|
||||
crawler_metadata['real_link'] = r_serv_metadata.hget('paste_metadata:'+requested_path,'real_link')
|
||||
crawler_metadata['screenshot'] = paste.get_p_rel_path()
|
||||
else:
|
||||
crawler_metadata['get_metadata'] = False
|
||||
|
||||
misp_event = r_serv_metadata.get('misp_events:' + requested_path)
|
||||
if misp_event is None:
|
||||
misp_eventid = False
|
||||
misp_url = ''
|
||||
else:
|
||||
misp_eventid = True
|
||||
misp_url = misp_event_url + misp_event
|
||||
|
||||
hive_case = r_serv_metadata.get('hive_cases:' + requested_path)
|
||||
if hive_case is None:
|
||||
hive_caseid = False
|
||||
hive_url = ''
|
||||
else:
|
||||
hive_caseid = True
|
||||
hive_url = hive_case_url.replace('id_here', hive_case)
|
||||
|
||||
return render_template("show_saved_item_min.html", bootstrap_label=bootstrap_label, content=item_content,
|
||||
item_basic_info=item_basic_info, item_info=item_info,
|
||||
initsize=len(item_content),
|
||||
hashtype_list = p_hashtype_list,
|
||||
crawler_metadata=crawler_metadata,
|
||||
l_64=l_64, vt_enabled=vt_enabled, misp_eventid=misp_eventid, misp_url=misp_url, hive_caseid=hive_caseid, hive_url=hive_url)
|
||||
|
||||
# ============ ROUTES ============
|
||||
|
||||
@showsavedpastes.route("/showsavedpaste/") #completely shows the paste in a new tab
|
||||
|
@ -230,6 +352,11 @@ def showsavedpaste():
|
|||
requested_path = request.args.get('paste', '')
|
||||
return showpaste(0, requested_path)
|
||||
|
||||
@showsavedpastes.route("/showsaveditem_min/") #completely shows the paste in a new tab
|
||||
def showsaveditem_min():
|
||||
requested_path = request.args.get('paste', '')
|
||||
return show_item_min(requested_path)
|
||||
|
||||
@showsavedpastes.route("/showsavedrawpaste/") #shows raw
|
||||
def showsavedrawpaste():
|
||||
requested_path = request.args.get('paste', '')
|
||||
|
@ -241,7 +368,7 @@ def showsavedrawpaste():
|
|||
def showpreviewpaste():
|
||||
num = request.args.get('num', '')
|
||||
requested_path = request.args.get('paste', '')
|
||||
return "|num|"+num+"|num|"+showpaste(max_preview_modal, requested_path)
|
||||
return "|num|"+num+"|num|"+show_item_min(requested_path, content_range=max_preview_modal)
|
||||
|
||||
|
||||
@showsavedpastes.route("/getmoredata/")
|
||||
|
|
300
var/www/modules/showpaste/templates/show_saved_item_min.html
Normal file
300
var/www/modules/showpaste/templates/show_saved_item_min.html
Normal file
|
@ -0,0 +1,300 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Paste information - AIL</title>
|
||||
<link rel="icon" href="{{ url_for('static', filename='image/ail-icon.png') }}">
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
<link href="{{ url_for('static', filename='css/bootstrap4.min.css') }}" rel="stylesheet">
|
||||
<link href="{{ url_for('static', filename='css/font-awesome.min.css') }}" rel="stylesheet">
|
||||
<link href="{{ url_for('static', filename='css/dataTables.bootstrap4.min.css') }}" rel="stylesheet">
|
||||
|
||||
<script language="javascript" src="{{ url_for('static', filename='js/jquery.js')}}"></script>
|
||||
<script src="{{ url_for('static', filename='js/bootstrap.min.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='js/jquery.dataTables.min.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='js/dataTables.bootstrap.min.js') }}"></script>
|
||||
|
||||
<style>
|
||||
.scrollable-menu {
|
||||
height: auto;
|
||||
max-height: 200px;
|
||||
overflow-x: hidden;
|
||||
width:100%;
|
||||
}
|
||||
|
||||
.red_table thead{
|
||||
background: #d91f2d;
|
||||
color: #fff;
|
||||
}
|
||||
</style>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="card mb-2">
|
||||
<div class="card-header bg-dark">
|
||||
<h3 class="text-white text-center" >{{ item_info['name'] }}</h3>
|
||||
</div>
|
||||
<div class="card-body pb-1">
|
||||
<table class="table table-condensed table-responsive">
|
||||
<thead class="">
|
||||
<tr>
|
||||
<th>Date</th>
|
||||
<th>Source</th>
|
||||
<th>Encoding</th>
|
||||
<th>Size (Kb)</th>
|
||||
<th>Number of lines</th>
|
||||
<th>Max line length</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>{{ item_basic_info['date'] }}</td>
|
||||
<td>{{ item_basic_info['source'] }}</td>
|
||||
<td>{{ item_basic_info['encoding'] }}</td>
|
||||
<td>{{ item_basic_info['size'] }}</td>
|
||||
<td>{{ item_basic_info['nb_lines'] }}</td>
|
||||
<td>{{ item_basic_info['max_length_line'] }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div>
|
||||
<h5>
|
||||
{% for tag in item_info['tags'] %}
|
||||
<span class="badge badge-{{ bootstrap_label[loop.index0 % 5] }}">{{ tag }}</span>
|
||||
{% endfor %}
|
||||
</h5>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if misp_eventid %}
|
||||
<div class="list-group" id="misp_event">
|
||||
<li class="list-group-item active">MISP Events already Created</li>
|
||||
<a target="_blank" href="{{ misp_url }}" class="list-group-item">{{ misp_url }}</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if hive_caseid %}
|
||||
<div class="list-group" id="misp_event">
|
||||
<li class="list-group-item active">The Hive Case already Created</li>
|
||||
<a target="_blank" href="{{ hive_url }}" class="list-group-item">{{ hive_url }}</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if item_info['nb_duplictates'] != 0 %}
|
||||
<div id="accordionDuplicate" class="mb-2">
|
||||
<div class="card">
|
||||
<div class="card-header py-1" id="headingDuplicate">
|
||||
<div class="my-1">
|
||||
<i class="far fa-clone"></i> duplicates
|
||||
<div class="badge badge-warning">{{item_info['nb_duplictates']}}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
|
||||
{% if l_64|length != 0 %}
|
||||
<div id="accordionDecoded" class="mb-3">
|
||||
<div class="card">
|
||||
<div class="card-header py-1" id="headingDecoded">
|
||||
<div class="row">
|
||||
<div class="col-11">
|
||||
<div class="mt-2">
|
||||
<i class="fas fa-lock-open"></i> Decoded Files
|
||||
<div class="badge badge-warning">{{l_64|length}}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-1">
|
||||
<button class="btn btn-link py-2 rotate" data-toggle="collapse" data-target="#collapseDecoded" aria-expanded="true" aria-controls="collapseDecoded">
|
||||
<i class="fas fa-chevron-circle-down"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="collapseDecoded" class="collapse show" aria-labelledby="headingDecoded" data-parent="#accordionDecoded">
|
||||
<div class="card-body">
|
||||
|
||||
<table id="tableb64" class="red_table table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>estimated type</th>
|
||||
<th>hash</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for b64 in l_64 %}
|
||||
<tr>
|
||||
<td><i class="fas {{ b64[0] }}"></i> {{ b64[1] }}</td>
|
||||
<td><a target="_blank" href="{{ url_for('hashDecoded.showHash') }}?hash={{ b64[2] }}">{{ b64[2] }}</a> ({{ b64[4] }})</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if crawler_metadata['get_metadata'] %}
|
||||
<div id="accordionCrawler" class="mb-3">
|
||||
<div class="card">
|
||||
<div class="card-header py-1" id="headingCrawled">
|
||||
<div class="row">
|
||||
<div class="col-11">
|
||||
<div class="mt-2">
|
||||
<i class="fas fa-spider"></i> Crawled Item
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-1">
|
||||
<button class="btn btn-link py-2 rotate" data-toggle="collapse" data-target="#collapseCrawled" aria-expanded="true" aria-controls="collapseCrawled">
|
||||
<i class="fas fa-chevron-circle-up"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="collapseCrawled" class="collapse show" aria-labelledby="headingCrawled" data-parent="#accordionCrawler">
|
||||
<div class="card-body">
|
||||
|
||||
<table class="table table-hover table-striped">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Domain</td>
|
||||
<td><a target="_blank" href="{{ url_for('hiddenServices.show_domain') }}?domain={{ crawler_metadata['domain'] }}" id='domain'>{{ crawler_metadata['domain'] }}</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Father</td>
|
||||
<td><a target="_blank" href="{{ url_for('showsavedpastes.showsavedpaste') }}?paste={{ crawler_metadata['paste_father'] }}" id='paste_father'>{{ crawler_metadata['paste_father'] }}</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Url</td>
|
||||
<td>{{ crawler_metadata['real_link'] }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div class="card my-2" style="background-color:#ecf0f1;">
|
||||
<div class="card-body py-2">
|
||||
<div class="row">
|
||||
<div class="col-md-8">
|
||||
<input class="custom-range mt-2" id="blocks" type="range" min="1" max="50" value="13">
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<button class="btn btn-primary" onclick="blocks.value=50;pixelate();">
|
||||
<i class="fas fa-search-plu"></i>
|
||||
<span class="label-icon">Full resolution</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<canvas id="canvas" style="width:100%;"></canvas>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="card bg-dark text-white">
|
||||
<div class="card-header">
|
||||
|
||||
<div class="row">
|
||||
<div class="col-10">
|
||||
<h3> Content: </h3>
|
||||
</div>
|
||||
<div class="col-2">
|
||||
<div class="mt-2">
|
||||
<small><a class="text-info" href="{{ url_for('showsavedpastes.showsavedrawpaste') }}?paste={{ request.args.get('paste') }}" id='raw_paste' > [Raw content] </a></small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<p class="my-0" data-initsize="{{ initsize }}"> <pre id="paste-holder" class="border">{{ content }}</pre></p>
|
||||
|
||||
<script>
|
||||
var ltags
|
||||
var ltagsgalaxies
|
||||
$(document).ready(function(){
|
||||
$('#tableDup').DataTable();
|
||||
$('#tableb64').DataTable({
|
||||
"aLengthMenu": [[5, 10, 15, -1], [5, 10, 15, "All"]],
|
||||
"iDisplayLength": 5,
|
||||
"order": [[ 1, "asc" ]]
|
||||
});
|
||||
$(".rotate").click(function(){
|
||||
$(this).toggleClass("down") ;
|
||||
})
|
||||
});
|
||||
</script>
|
||||
|
||||
<script>
|
||||
var ctx = canvas.getContext('2d'), img = new Image();
|
||||
|
||||
/// turn off image smoothing
|
||||
ctx.webkitImageSmoothingEnabled = false;
|
||||
ctx.imageSmoothingEnabled = false;
|
||||
|
||||
img.onload = pixelate;
|
||||
img.addEventListener("error", img_error);
|
||||
var draw_img = false;
|
||||
|
||||
img.src = "{{ url_for('showsavedpastes.screenshot', filename=crawler_metadata['screenshot']) }}";
|
||||
|
||||
function pixelate() {
|
||||
|
||||
/// use slider value
|
||||
if( blocks.value == 50 ){
|
||||
size = 1;
|
||||
} else {
|
||||
var size = (blocks.value) * 0.01;
|
||||
}
|
||||
|
||||
canvas.width = img.width;
|
||||
canvas.height = img.height;
|
||||
|
||||
/// cache scaled width and height
|
||||
w = canvas.width * size;
|
||||
h = canvas.height * size;
|
||||
|
||||
/// draw original image to the scaled size
|
||||
ctx.drawImage(img, 0, 0, w, h);
|
||||
|
||||
/// pixelated
|
||||
ctx.drawImage(canvas, 0, 0, w, h, 0, 0, canvas.width, canvas.height);
|
||||
|
||||
}
|
||||
|
||||
function img_error() {
|
||||
img.onerror=null;
|
||||
img.src="{{ url_for('static', filename='image/AIL.png') }}";
|
||||
blocks.value = 50;
|
||||
pixelate;
|
||||
}
|
||||
|
||||
blocks.addEventListener('change', pixelate, false);
|
||||
</script>
|
||||
|
||||
<div id="container-show-more" class="text-center">
|
||||
|
||||
</div>
|
||||
|
||||
</html>
|
||||
|
||||
</body>
|
||||
|
||||
|
||||
</html>
|
Loading…
Reference in a new issue