add in spinner, fix qunit and then alter far too much js

This commit is contained in:
Andy McKay 2011-03-29 12:38:15 -07:00
Родитель 177fa9899b
Коммит 33d7318f53
7 изменённых файлов: 249 добавлений и 239 удалений

Просмотреть файл

@ -9,11 +9,11 @@
</h3>
<div id="file-viewer">
{% if not status %}
<p id="waiting" data-url="{{ poll_url }}">
<p class="waiting" id="extracting" data-url="{{ poll_url }}">
{{ _('Add-on file being processed, please wait.') }}
</p>
{% endif %}
<div class="files">
<div id="files">
<ul>
{% if files %}
{% for key, value in files.items() %}
@ -38,20 +38,25 @@
<p>{{ _('No files in the uploaded file.') }}</p>
{% endif %}
</div>
<div class="content-wrapper">
<div id="thinking" class="hidden">
<p class="waiting">
{{ _('Fetching file.') }}
</p>
</div>
<div id="content-wrapper">
{% if msg %}
<p><strong>{{ msg }}</strong></p>
{% endif %}
<div class="numbers">
<div id="numbers">
</div>
<div class="content">
<div>
{% if viewer %}
{% if selected['binary'] and not selected['directory'] %}
<p>{{ _('Binary file, hash:') }} {{ selected.md5 }}</p>
{% else %}
{% if selected and content %}
<pre id="content">{{ content }}</pre>
{% else %}
{% elif content == '' %}
<p>{{ _('No content.') }}</p>
{% endif %}
{% endif %}
@ -79,7 +84,7 @@
</div>
</div>
<p class="help">
{{ _('Shortcuts: prev j, next k') }}
{{ _('Shortcuts: prev j, next k') }}
</p>
</div>
{% endblock %}

Просмотреть файл

@ -1,6 +1,7 @@
import hashlib
from django import http
from django.conf import settings
from django.utils.http import http_date
from django.views.decorators.cache import never_cache
@ -56,9 +57,10 @@ def files_list(request, viewer, key='install.rdf'):
extract_file.delay(viewer)
response = jingo.render(request, 'files/viewer.html', data)
response['ETag'] = '"%s"' % hashlib.md5(response.content).hexdigest()
response['Last-Modified'] = http_date(data['selected']['modified'] if
data['selected'] else None)
if not settings.DEBUG:
response['ETag'] = '"%s"' % hashlib.md5(response.content).hexdigest()
response['Last-Modified'] = http_date(data['selected']['modified'] if
data['selected'] else None)
return response
@ -70,7 +72,7 @@ def files_compare_poll(request, diff):
@compare_file_view
def files_compare(request, diff, key=None):
def files_compare(request, diff, key='install.rdf'):
data = setup_viewer(request, diff.file_one.file)
data['diff'] = diff
data['poll_url'] = reverse('files.compare.poll',
@ -99,7 +101,8 @@ def files_compare(request, diff, key=None):
extract_file.delay(diff)
response = jingo.render(request, 'files/viewer.html', data)
response['ETag'] = '"%s"' % hashlib.md5(response.content).hexdigest()
response['Last-Modified'] = http_date(data['selected']['modified'] if
data['selected'] else None)
if not settings.DEBUG:
response['ETag'] = '"%s"' % hashlib.md5(response.content).hexdigest()
response['Last-Modified'] = http_date(data['selected']['modified'] if
data['selected'] else None)
return response

Просмотреть файл

@ -3,23 +3,23 @@
padding-top: 1em
}
#file-viewer .files {
#file-viewer #files {
width: 15em;
float: left;
}
#file-viewer .files li a {
#file-viewer #files li a {
display: inline-block;
}
#file-viewer .numbers {
#file-viewer #numbers {
width: 3em;
float: left;
padding-left: 4em;
border-top: 1px solid white;
}
#file-viewer .content {
#file-viewer #thinking, #file-viewer #content {
width: 50em;
float: left;
padding-left: 1em;
@ -37,7 +37,7 @@ pre div:hover, pre ins:hover, pre del:hover {
}
pre, #file-viewer .numbers {
pre, #file-viewer #numbers {
line-height: 1.15em;
}
@ -53,7 +53,7 @@ pre del {
line-height: inherit;
}
#file-viewer .content {
#file-viewer #content {
width: 10em;
float: left;
}
@ -92,7 +92,7 @@ pre del {
background: url('/media/img/icons/minus.gif') 0 no-repeat;
}
#file-viewer #waiting {
#file-viewer .waiting {
background-image: url(../../img/zamboni/loading-white.gif);
background-repeat: no-repeat;
background-position: left top;

Просмотреть файл

@ -1,163 +1,225 @@
diff_match_patch.prototype.diff_prettyHtml = function(diffs) {
/* An override of prettyHthml from diff_match_patch. This
one will not put any style attrs in the ins or del. It will
also as side effect write an array of line numbers, ignoring
deletions */
var html = [],
pattern_amp = /&/g,
pattern_lt = /</g,
pattern_gt = />/g,
pattern_para = /\n/g;
if (typeof diff_match_patch !== 'undefined') {
diff_match_patch.prototype.diff_prettyHtml = function(diffs) {
/* An override of prettyHthml from diff_match_patch. This
one will not put any style attrs in the ins or del. It will
also as side effect write an array of line numbers, ignoring
deletions */
var html = [],
pattern_amp = /&/g,
pattern_lt = /</g,
pattern_gt = />/g,
pattern_para = /\n/g;
this.line_numbers = [];
var k = 1,
i = 0;
for (var x = 0; x < diffs.length; x++) {
var op = diffs[x][0]; // Operation (insert, delete, equal)
var data = diffs[x][1]; // Text of change.
var text = data.replace(pattern_amp, '&amp;').replace(pattern_lt, '&lt;')
.replace(pattern_gt, '&gt;').replace(pattern_para, '\n');
/* As as side effect, append on to the line_numbers a list
of numbers, with false for empty ones. So that <del> don't
have a line number. To get the numbers balanced, we need
to strip a starting or ending "" in the text */
var lines = text.split('\n');
if (lines[lines.length-1] === '') {
lines.pop();
}
if (lines[0] === '') {
lines.splice(0, 1);
}
for (var t = 0; t < lines.length; t++) {
if (op === DIFF_DELETE) {
this.line_numbers.push(false);
} else {
this.line_numbers.push(k++);
this.line_numbers = [];
var k = 1,
i = 0;
for (var x = 0; x < diffs.length; x++) {
var op = diffs[x][0]; // Operation (insert, delete, equal)
var data = diffs[x][1]; // Text of change.
var text = data.replace(pattern_amp, '&amp;').replace(pattern_lt, '&lt;')
.replace(pattern_gt, '&gt;').replace(pattern_para, '\n');
/* As as side effect, append on to the line_numbers a list
of numbers, with false for empty ones. So that <del> don't
have a line number. To get the numbers balanced, we need
to strip a starting or ending "" in the text */
var lines = text.split('\n');
if (lines[lines.length-1] === '') {
lines.pop();
}
switch (op) {
case DIFF_INSERT:
html.push('<ins>+ ' + lines[t] + '</ins>');
break;
case DIFF_DELETE:
html.push('<del>- ' + lines[t] + '</del>');
break;
case DIFF_EQUAL:
html.push('<div> ' + lines[t] + '</div>');
break;
if (lines[0] === '') {
lines.splice(0, 1);
}
if (op !== DIFF_DELETE) {
i += data.length;
}
}
}
return html.join('\n');
};
function Tree() {
this.$node = $('.files ul li');
this.show_leaf = function(names) {
this.$node.each(function() {
var $this = $(this),
parent = $this.attr('data-parent'),
shrt = $this.attr('data-short'),
a = $this.find('a');
if (parent && (names.indexOf(parent) > -1) &&
$this.hasClass('hidden')) {
$this.removeClass('hidden').show();
}
else if (names.length === 1 &&
(shrt.length > names[0].length) &&
(shrt.indexOf(names[0]) === 0)) {
$this.addClass('hidden').hide();
if (a.hasClass('open')) {
a.removeClass('open').addClass('closed');
}
}
if (names.indexOf($this.attr('data-short')) > -1) {
if (a.hasClass('closed')) {
a.removeClass('closed').addClass('open');
for (var t = 0; t < lines.length; t++) {
if (op === DIFF_DELETE) {
this.line_numbers.push(false);
} else {
a.removeClass('open').addClass('closed');
this.line_numbers.push(k++);
}
switch (op) {
case DIFF_INSERT:
html.push('<ins>+ ' + lines[t] + '</ins>');
break;
case DIFF_DELETE:
html.push('<del>- ' + lines[t] + '</del>');
break;
case DIFF_EQUAL:
html.push('<div> ' + lines[t] + '</div>');
break;
}
if (op !== DIFF_DELETE) {
i += data.length;
}
}
});
}
return html.join('\n');
};
this.selected = function() {
var self = this;
$('#file-viewer .selected').each(function() {
var $curr = $(this).closest('li'),
leaf = $curr.attr('data-parent').split('/');
}
function bind_viewer() {
function Viewer() {
this.$tree = $('#files ul');
this.compute = function() {
/* Counts the line numbers.
If we've got diffs, computes the diffs and the line numbers.
The line number computation is so that we don't show line numbers
on - lines. */
if ($('#content').length) {
var text = $('#content').text(),
length = text.split('\n').length,
num = [];
for (var k = 1; k < Math.max(1, length); k++) {
num.push(k);
}
if (text.slice(text.length-1, text.length) !== '\n') {
num.push(num.slice(num.length-1, num.length) + 1);}
this.add_numbers(num);
}
if ($('#diff').length) {
var dmp = new diff_match_patch();
var diff = dmp.diff_main($('#file-one').text(), $('#file-two').text());
$('#diff').html(dmp.diff_prettyHtml(diff));
this.add_numbers(dmp.line_numbers);
}
this.$tree.show();
};
this.add_numbers = function(num) {
/* Adds the line numbers into the page after counting. */
var text = [];
for (var k = 0; k < num.length; k++) {
text.push(num[k] === false ? '<br/>' : format('<a href="#L{0}" name="L{0}">{0}</a><br/>', num[k]));
}
// Because the line numbers are generated dynamically,
// it won't go to the anchor.
if (window.location.hash) {
window.location = window.location;
}
$('#numbers').html(text.join('\n'));
};
this.show_leaf = function(names) {
/* Exposes the leaves for a given set of nodes. */
this.$tree.find('li').each(function() {
var $this = $(this),
parent = $this.attr('data-parent'),
shrt = $this.attr('data-short'),
a = $this.find('a');
if (parent && (names.indexOf(parent) > -1) &&
$this.hasClass('hidden')) {
$this.removeClass('hidden').show();
}
else if (names.length === 1 &&
(shrt.length > names[0].length) &&
(shrt.indexOf(names[0]) === 0)) {
$this.addClass('hidden').hide();
if (a.hasClass('open')) {
a.removeClass('open').addClass('closed');
}
}
if (names.indexOf($this.attr('data-short')) > -1) {
if (a.hasClass('closed')) {
a.removeClass('closed').addClass('open');
}
}
});
};
this.selected = function($link) {
/* Updates the tree, showing the leaves relevant to node */
var $curr = $link.closest('li'),
leaf = $curr.attr('data-parent').split('/'),
names = [];
$curr.removeClass('hidden').show();
if (leaf.length && (leaf[0])) {
for (var k = 0; k <= leaf.length; k += 1) {
names.push(leaf.slice(0, k).join('/'));
}
self.show_leaf(names);
this.show_leaf(names);
}
});
};
this.select = function($link) {
this.$node.find('a.selected').each(function() {
$(this).removeClass('selected');
});
$link.addClass('selected');
this.selected();
};
}
};
this.load = function($link) {
/* Accepts a jQuery wrapped node, which is part of the tree.
Hides content, shows spinner, gets the content and then
shows it all. */
var self = this,
$thinking = $('#thinking'),
$wrapper = $('#wrapper');
$wrapper.hide();
$thinking.removeClass('hidden').show();
history.pushState({ path: $link.text() }, '', $link.attr('href'));
$('#content-wrapper').load($link.attr('href') + ' #content-wrapper', function() {
$(this).children().unwrap();
self.compute();
$thinking.hide();
$wrapper.slideDown();
});
};
this.select = function($link) {
/* Given a node, alters the tree and then loads the content. */
this.$tree.find('a.selected').each(function() {
$(this).removeClass('selected');
});
$link.addClass('selected');
this.selected($link);
this.load($link);
};
this.get_selected = function() {
return this.$tree.find('a.selected');
};
}
function Numbers() {
this.count = function() {
this.$node = $('.numbers');
if ($('#content').length) {
var length = $('#content').text().split('\n').length,
num = [];
for (var k = 1; k < Math.max(2, length); k++) {
num.push(k);
}
this.add(num);
}
var viewer = new Viewer();
if ($('#diff').length) {
var dmp = new diff_match_patch();
var diff = dmp.diff_main($('#file-one').text(),
$('#file-two').text());
$('#diff').html(dmp.diff_prettyHtml(diff));
this.add(dmp.line_numbers);
viewer.$tree.find('.directory').click(_pd(function() {
viewer.show_leaf([$(this).closest('li').attr('data-short')]);
}));
$('#files-prev').click(_pd(function() {
var $curr = viewer.get_selected().closest('li'),
choices = $curr.prevUntil('ul').find('a.file');
if (choices.length) { viewer.select($(choices[0])); }
}));
$('#files-next').click(_pd(function() {
var $curr = viewer.get_selected().closest('li'),
choices = $curr.nextUntil('ul').find('a.file');
if (choices.length) { viewer.select($(choices[0])); }
}));
$('#files-expand-all').click(_pd(function() {
viewer.$tree.find('li.hidden').removeClass('hidden').show();
viewer.$tree.find('a.directory').removeClass('closed').addClass('open');
}));
$('#files li a').click(_pd(function() {
viewer.select($(this));
}));
$(document).bind('keyup', _pd(function(e) {
if (e.keyCode === 75) {
$('#files-next').trigger('click');
} else if (e.keyCode === 74) {
$('#files-prev').trigger('click');
}
};
this.add = function(num) {
this.$node = $('.numbers');
this.$node.html('');
for (var k = 0; k < num.length; k++) {
if (num[k] === false) {
this.$node.append('<br/>');
} else {
this.$node.append('<a href="#L' + num[k] + '" name="L' +
num[k] + '">' + num[k] + '</a><br/>');
}
}
// Because the line numbers are generated dynamically,
// it won't go to the anchor.
if (window.location.hash) {
window.location = window.location;
}
};
}));
return viewer;
}
$(document).ready(function() {
var viewer = null;
function poll_file_extraction() {
$.getJSON($('#waiting').attr('data-url'), function(json) {
$.getJSON($('#extracting').attr('data-url'), function(json) {
if (json && json.status) {
$('#file-viewer').load(window.location.pathname + ' #file-viewer', function() {
numbers.count();
bind_tree();
viewer = bind_viewer();
viewer.selected(viewer.$tree.find('a.selected'));
viewer.compute();
});
} else {
setTimeout(poll_file_extraction, 2000);
@ -165,72 +227,11 @@ $(document).ready(function() {
});
}
var numbers = new Numbers();
numbers.count();
if ($('#waiting').length) {
if ($('#extracting').length) {
poll_file_extraction();
} else if ($('#file-viewer').length) {
viewer = bind_viewer();
viewer.selected(viewer.$tree.find('a.selected'));
viewer.compute();
}
function bind_tree() {
var tree = new Tree();
tree.selected();
if ($('#file-viewer').length) {
$('#file-viewer .directory').click(function() {
tree.show_leaf([$(this).closest('li').attr('data-short')]);
return false;
});
$('.files li a').click(function() {
var $link = $(this);
history.pushState({ path: this.text }, '', this.href);
$('.content-wrapper').load(this.href + ' .content-wrapper', function() {
numbers.count();
tree.select($link);
});
return false;
});
$(window).bind('popstate', function() {
$('.content').load(location.pathname + ' .content-wrapper', function() {
numbers.count();
});
});
$('#files-prev').click(function() {
var $curr = $('#file-viewer a.selected').closest('li'),
choices = $curr.prevUntil('ul').find('a.file');
if (choices.length) {
$(choices[0]).trigger('click');
}
return false;
});
$('#files-next').click(function() {
var $curr = $('#file-viewer a.selected').closest('li'),
choices = $curr.nextUntil('ul').find('a.file');
if (choices.length) {
$(choices[0]).trigger('click');
}
return false;
});
$('#files-expand-all').click(function() {
$('#file-viewer .hidden').removeClass('hidden').show();
$('#file-viewer .directory').removeClass('closed').addClass('open');
return false;
});
$(document).bind('keyup', function(e) {
if (e.keyCode === 75) {
$('#files-next').trigger('click');
} else if (e.keyCode === 74) {
$('#files-prev').trigger('click');
}
return false;
});
}
}
bind_tree();
});

Просмотреть файл

@ -1,11 +1,12 @@
$(document).ready(function(){
module('File viewer');
module('File viewer');
test('Show leaf', function() {
var viewer = bind_viewer();
viewer.show_leaf(['foo']);
equal($($('#files li a')[1]).hasClass('open'), true);
equal($($('#files li')[2]).hasClass('hidden'), false);
});
test('Show leaf', function() {
$($('#file-viewer li a')[1]).trigger('click');
equal($($('#file-viewer li a')[1]).hasClass('open'), true);
equal($($('#file-viewer li a')[2]).hasClass('hidden'), false);
});
);

Просмотреть файл

@ -7,6 +7,7 @@
"js/zamboni/devhub.js",
"js/zamboni/l10n.js",
"js/zamboni/editors.js",
"js/zamboni/upload.js"
"js/zamboni/upload.js",
"js/zamboni/files.js"
]
}

Просмотреть файл

@ -251,21 +251,20 @@
</div>
</div>
</div>
<div id="file-viewer">
<div class="files">
<div id="files">
<ul>
<li class="" style="padding-left: 0em;" data-parent="">
<li class="" style="padding-left: 0em;" data-parent="" data-short="someurl">
<a class="file" href="">someurl</a>
</li>
<li style="padding-left: 1em; display: list-item;" data-parent="">
<a class="file directory closed" href="">foo</a>
<li style="padding-left: 1em; display: list-item;" data-parent="" data-short="foo">
<a class="directory closed" href="">foo</a>
</li>
<li class="hidden" style="padding-left: 1em; display: list-item;" data-parent="foo">
<li class="hidden" style="padding-left: 1em; display: list-item;" data-parent="foo" data-short="foo/bar.txt">
<a class="file" href="someurl">foo/bar.txt</a>
</li>
</ul>
</div>
</div>
<script src="{{ url('jsi18n') }}/build:{{ BUILD_ID_JS }}"></script>
{{ js('common') }}