Hello again, As discussed on IRC, I have rebased my patches for this bug.
Cheers -- Jason Pleau
>From ee67677ce389a844221e3a712598147faf1ff2ff Mon Sep 17 00:00:00 2001 From: Jason Pleau <ja...@jpleau.ca> Date: Mon, 9 Mar 2015 12:34:06 -0400 Subject: [PATCH 1/2] add a new hidden_files setting in config.ini Right now the .pc/ exclusion in directory listings is hardcoded. This new hidden_files configuration allows us to be more flexible in what is shown or hidden in those listings. */.pc/ is added as a default value for this configuration setting. Closes #762944 --- .../sources/templates/sources/source_folder.html | 48 ++++++++++++---------- debsources/app/sources/views.py | 6 +-- debsources/mainlib.py | 4 +- debsources/models.py | 18 +++++--- debsources/tests/test_webapp.py | 22 ++++++++++ doc/examples/sample-config.local.ini | 12 ++++++ etc/config.ini | 12 ++++++ 7 files changed, 89 insertions(+), 33 deletions(-) diff --git a/debsources/app/sources/templates/sources/source_folder.html b/debsources/app/sources/templates/sources/source_folder.html index ba6f894..ac3b5b4 100644 --- a/debsources/app/sources/templates/sources/source_folder.html +++ b/debsources/app/sources/templates/sources/source_folder.html @@ -32,31 +32,35 @@ </tr> {% for dir in subdirs %} - <tr> - <td class="item-img"><img src="{{ config['ICONS_FOLDER'] }}22x22/places/folder.png" alt="d " /></td> - {% if config["DIR_LS_LONG"] %}<td class="stat-type"><span>{{ dir.stat.type }}</span></td>{% endif %} - <td class="stat-perms"><span>{{ dir.stat.perms }}</span></td> - {% if config["DIR_LS_LONG"] %}<td class="stat-size"><span>{{ "{:,d}".format(dir.stat.size) }}</span></td>{% endif %} - <td class="item-name"><a href="{{ url_for('.source', path_to=path+'/'+dir.name) }}">{{ dir.name }}</a> - {% if config["DIR_LS_LONG"] %} - {% if dir.stat.symlink_dest is not none %}{{ " â " + dir.stat.symlink_dest }}{% endif %} - {% endif %} - </td> - </tr> + {% if not dir['hidden'] %} + <tr> + <td class="item-img"><img src="{{ config['ICONS_FOLDER'] }}22x22/places/folder.png" alt="d " /></td> + {% if config["DIR_LS_LONG"] %}<td class="stat-type"><span>{{ dir.stat.type }}</span></td>{% endif %} + <td class="stat-perms"><span>{{ dir.stat.perms }}</span></td> + {% if config["DIR_LS_LONG"] %}<td class="stat-size"><span>{{ "{:,d}".format(dir.stat.size) }}</span></td>{% endif %} + <td class="item-name"><a href="{{ url_for('.source', path_to=path+'/'+dir.name) }}">{{ dir.name }}</a> + {% if config["DIR_LS_LONG"] %} + {% if dir.stat.symlink_dest is not none %}{{ " â " + dir.stat.symlink_dest }}{% endif %} + {% endif %} + </td> + </tr> + {% endif %} {% endfor %} {% for file_ in subfiles %} - <tr> - <td class="item-img"><img src="{{ config['ICONS_FOLDER'] }}22x22/mimetypes/ascii.png" alt="- " /></td> - {% if config["DIR_LS_LONG"] %}<td class="stat-type"><span>{{ file_.stat.type }}</span></td>{% endif %} - <td class="stat-perms"><span>{{ file_.stat.perms }}</span></td> - {% if config["DIR_LS_LONG"] %}<td class="stat-size"><span>{{ "{:,d}".format(file_.stat.size) }}</span></td>{% endif %} - <td class="item-name"><a href="{{ url_for('.source', path_to=path+'/'+file_.name) }}">{{ file_.name }}</a> - {% if config["DIR_LS_LONG"] %} - {% if file_.stat.symlink_dest is not none %}{{ " â " + file_.stat.symlink_dest }}{% endif %} - {% endif %} - </td> - </tr> + {% if not file_['hidden'] %} + <tr> + <td class="item-img"><img src="{{ config['ICONS_FOLDER'] }}22x22/mimetypes/ascii.png" alt="- " /></td> + {% if config["DIR_LS_LONG"] %}<td class="stat-type"><span>{{ file_.stat.type }}</span></td>{% endif %} + <td class="stat-perms"><span>{{ file_.stat.perms }}</span></td> + {% if config["DIR_LS_LONG"] %}<td class="stat-size"><span>{{ "{:,d}".format(file_.stat.size) }}</span></td>{% endif %} + <td class="item-name"><a href="{{ url_for('.source', path_to=path+'/'+file_.name) }}">{{ file_.name }}</a> + {% if config["DIR_LS_LONG"] %} + {% if file_.stat.symlink_dest is not none %}{{ " â " + file_.stat.symlink_dest }}{% endif %} + {% endif %} + </td> + </tr> + {% endif %} {% endfor %} </table> diff --git a/debsources/app/sources/views.py b/debsources/app/sources/views.py index e6c6bf1..72c30b4 100644 --- a/debsources/app/sources/views.py +++ b/debsources/app/sources/views.py @@ -137,10 +137,8 @@ class SourceView(GeneralView): """ renders a directory, lists subdirs and subfiles """ - directory = Directory(location, toplevel=(location.get_path() == "")) - - # (if path == "", then the dir is toplevel, and we don't want - # the .pc directory) + hidden_files = app.config['HIDDEN_FILES'].split(" ") + directory = Directory(location, hidden_files) pkg_infos = Infobox(session, location.get_package(), location.get_version()).get_infos() diff --git a/debsources/mainlib.py b/debsources/mainlib.py index 45a206b..cc0acf6 100644 --- a/debsources/mainlib.py +++ b/debsources/mainlib.py @@ -41,7 +41,9 @@ DEFAULT_CONFIG.update({ 'force_triggers': [], 'single_transaction': 'true', }, - 'webapp': {}, + 'webapp': { + 'hidden_files': '*/*.pc/' + }, }) LOG_FMT_FILE = '%(asctime)s %(module)s:%(levelname)s %(message)s' diff --git a/debsources/models.py b/debsources/models.py index e4d05fc..8f72dc3 100644 --- a/debsources/models.py +++ b/debsources/models.py @@ -19,6 +19,7 @@ import os import magic import stat +import fnmatch from collections import namedtuple from sqlalchemy import Column, ForeignKey @@ -652,11 +653,10 @@ class Location(object): class Directory(object): """ a folder in a package """ - def __init__(self, location, toplevel=False): - # if the directory is a toplevel one, we remove the .pc folder + def __init__(self, location, hidden_files=[]): self.sources_path = location.sources_path - self.toplevel = toplevel self.location = location + self.hidden_files = hidden_files def get_listing(self): """ @@ -670,11 +670,17 @@ class Directory(object): else: return "file" get_stat, join_path = self.location.get_stat, os.path.join - listing = sorted(dict(name=f, type=get_type(f), + listing = sorted(dict(name=f, type=get_type(f), hidden=False, stat=get_stat(join_path(self.sources_path, f))) for f in os.listdir(self.sources_path)) - if self.toplevel: - listing = filter(lambda x: x['name'] != ".pc", listing) + + for hidden_file in self.hidden_files: + for f in listing: + full_path = os.path.join(self.location.sources_path, f['name']) + if f['type'] == "directory": + full_path += "/" + f['hidden'] = (f['hidden'] + or fnmatch.fnmatch(full_path, hidden_file)) return listing diff --git a/debsources/tests/test_webapp.py b/debsources/tests/test_webapp.py index d4eb3c8..dd943ca 100644 --- a/debsources/tests/test_webapp.py +++ b/debsources/tests/test_webapp.py @@ -272,16 +272,38 @@ class DebsourcesTestCase(unittest.TestCase, DbTestFixture): self.assertEqual(rv['directory'], "2.01-6") self.assertIn({"type": "file", "name": "ledit.ml", + "hidden": False, "stat": {"perms": "rw-r--r--", "size": 45858, "type": "-", "symlink_dest": None} }, rv['content']) + def test_api_hidden_files_folder(self): + rv = json.loads(self.app.get('/api/src/nvidia-xconfig/319.72-1/').data) + self.assertIn({"type": "directory", + "name": ".pc", + "hidden": True, + "stat": {"perms": "rwxr-xr-x", + "size": 4096, + "type": "d", + "symlink_dest": None} + }, rv['content']) + + self.assertIn({"type": "file", + "name": "lscf.c", + "hidden": False, + "stat": {"perms": "rw-r--r--", + "size": 11940, + "type": "-", + "symlink_dest": None} + }, rv['content']) + def test_api_symlink_dest(self): rv = json.loads(self.app.get('/api/src/beignet/1.0.0-1/').data) self.assertIn({"type": "file", "name": "README.md", + "hidden": False, "stat": {"perms": "rwxrwxrwx", "size": 17, "type": "l", diff --git a/doc/examples/sample-config.local.ini b/doc/examples/sample-config.local.ini index 7722c03..789289d 100644 --- a/doc/examples/sample-config.local.ini +++ b/doc/examples/sample-config.local.ini @@ -87,3 +87,15 @@ blueprint_sources: true # the url for sources, either subdomain or url_prefix sources_url: 127.0.0.1:5000 + +# Space-separated list of files or directories patterns to hide in +# directory listings. +# +# The *full path* of files / directories from a source package's directory will +# be compared using fnmatch.fnmatch. +# +# Example, we have a */.pc/ pattern, it means that +# /srv/[...]/package_name/version/.pc/ will be hidden, as well as +# /src/[...]/package_name/version/debian/submodule/.pc/ + +hidden_files: */.pc/ diff --git a/etc/config.ini b/etc/config.ini index 8f1c645..9cd83bb 100644 --- a/etc/config.ini +++ b/etc/config.ini @@ -91,3 +91,15 @@ blueprint_sources: true # the url for sources, either subdomain or url_prefix sources_url: sources.debian.net + +# Space-separated list of files or directories patterns to hide in +# directory listings. +# +# The *full path* of files / directories from a source package's directory will +# be compared using fnmatch.fnmatch. +# +# Example, we have a */.pc/ pattern, it means that +# /srv/[...]/package_name/version/.pc/ will be hidden, as well as +# /src/[...]/package_name/version/debian/submodule/.pc/ + +hidden_files: */.pc/ -- 2.1.4
>From de8286485b816650915c87d2ad66d036da03a7f2 Mon Sep 17 00:00:00 2001 From: Jason Pleau <ja...@jpleau.ca> Date: Tue, 10 Mar 2015 20:37:22 -0400 Subject: [PATCH 2/2] source_folder: add a show/hide hidden files button Since that hidden files and directories are still passed to the view, but are now given an 'hidden' boolean attribute, we can show or hide them once the page is loaded. I also changed the way javascript was loaded in debsources.js, so that it does not run unnecessary code (for example we don't need to print line numbers in folders). --- .../app/sources/static/css/source_folder.css | 12 + .../app/sources/templates/sources/source_file.html | 2 +- .../sources/templates/sources/source_folder.html | 57 +++-- debsources/app/sources/views.py | 1 + debsources/app/static/javascript/debsources.js | 273 +++++++++++---------- 5 files changed, 193 insertions(+), 152 deletions(-) diff --git a/debsources/app/sources/static/css/source_folder.css b/debsources/app/sources/static/css/source_folder.css index 1a45b8f..4d7d609 100644 --- a/debsources/app/sources/static/css/source_folder.css +++ b/debsources/app/sources/static/css/source_folder.css @@ -40,3 +40,15 @@ License: GNU Affero General Public License, version 3 or above. font-size: small; text-align: right; } + +.hidden_file { + display: none; +} + +.hidden_file.visible { + display: table-row; +} + +#btn_toggle_hidden_files { + font-size: 0.8em; +} diff --git a/debsources/app/sources/templates/sources/source_file.html b/debsources/app/sources/templates/sources/source_file.html index 7e69166..3f3a078 100644 --- a/debsources/app/sources/templates/sources/source_file.html +++ b/debsources/app/sources/templates/sources/source_file.html @@ -32,7 +32,7 @@ {% include "sources/source_file_code.inc.html" %} <script type="text/javascript"> - debsources("{{ msg.position }}"); + debsources.source_file("{{ msg.position }}"); hljs.highlightBlock(document.getElementById('sourcecode')) </script> diff --git a/debsources/app/sources/templates/sources/source_folder.html b/debsources/app/sources/templates/sources/source_folder.html index ac3b5b4..08911ad 100644 --- a/debsources/app/sources/templates/sources/source_folder.html +++ b/debsources/app/sources/templates/sources/source_folder.html @@ -9,6 +9,7 @@ {{ super() }} <link rel="stylesheet" type="text/css" href="{{ url_for('.static', filename='css/source_folder.css') }}" /> +<script src="{{ url_for('static', filename='javascript/debsources.js') }}"></script> {% endblock %} {% block title %}Folder: {{ directory }}{% endblock %} @@ -22,6 +23,10 @@ <h2 id="folder_title">{{ self.title() }}</h2> +{% if nb_hidden_files %} + <a href="#" id="btn_toggle_hidden_files" data-action="show"><span>Show</span> hidden files ({{ nb_hidden_files }})</a> +{% endif %} + <table class="dir-listing"> <tr> <td class="item-img"><img src="{{ config['ICONS_FOLDER'] }}22x22/places/folder.png" alt="d " /></td> @@ -32,36 +37,36 @@ </tr> {% for dir in subdirs %} - {% if not dir['hidden'] %} - <tr> - <td class="item-img"><img src="{{ config['ICONS_FOLDER'] }}22x22/places/folder.png" alt="d " /></td> - {% if config["DIR_LS_LONG"] %}<td class="stat-type"><span>{{ dir.stat.type }}</span></td>{% endif %} - <td class="stat-perms"><span>{{ dir.stat.perms }}</span></td> - {% if config["DIR_LS_LONG"] %}<td class="stat-size"><span>{{ "{:,d}".format(dir.stat.size) }}</span></td>{% endif %} - <td class="item-name"><a href="{{ url_for('.source', path_to=path+'/'+dir.name) }}">{{ dir.name }}</a> - {% if config["DIR_LS_LONG"] %} - {% if dir.stat.symlink_dest is not none %}{{ " â " + dir.stat.symlink_dest }}{% endif %} - {% endif %} - </td> - </tr> - {% endif %} + <tr{% if dir['hidden'] %} class="hidden_file"{% endif %}> + <td class="item-img"><img src="{{ config['ICONS_FOLDER'] }}22x22/places/folder.png" alt="d " /></td> + {% if config["DIR_LS_LONG"] %}<td class="stat-type"><span>{{ dir.stat.type }}</span></td>{% endif %} + <td class="stat-perms"><span>{{ dir.stat.perms }}</span></td> + {% if config["DIR_LS_LONG"] %}<td class="stat-size"><span>{{ "{:,d}".format(dir.stat.size) }}</span></td>{% endif %} + <td class="item-name"><a href="{{ url_for('.source', path_to=path+'/'+dir.name) }}">{{ dir.name }}</a> + {% if config["DIR_LS_LONG"] %} + {% if dir.stat.symlink_dest is not none %}{{ " â " + dir.stat.symlink_dest }}{% endif %} + {% endif %} + </td> + </tr> {% endfor %} {% for file_ in subfiles %} - {% if not file_['hidden'] %} - <tr> - <td class="item-img"><img src="{{ config['ICONS_FOLDER'] }}22x22/mimetypes/ascii.png" alt="- " /></td> - {% if config["DIR_LS_LONG"] %}<td class="stat-type"><span>{{ file_.stat.type }}</span></td>{% endif %} - <td class="stat-perms"><span>{{ file_.stat.perms }}</span></td> - {% if config["DIR_LS_LONG"] %}<td class="stat-size"><span>{{ "{:,d}".format(file_.stat.size) }}</span></td>{% endif %} - <td class="item-name"><a href="{{ url_for('.source', path_to=path+'/'+file_.name) }}">{{ file_.name }}</a> - {% if config["DIR_LS_LONG"] %} - {% if file_.stat.symlink_dest is not none %}{{ " â " + file_.stat.symlink_dest }}{% endif %} - {% endif %} - </td> - </tr> - {% endif %} + <tr{% if file_['hidden'] %} class="hidden_file"{% endif %}> + <td class="item-img"><img src="{{ config['ICONS_FOLDER'] }}22x22/mimetypes/ascii.png" alt="- " /></td> + {% if config["DIR_LS_LONG"] %}<td class="stat-type"><span>{{ file_.stat.type }}</span></td>{% endif %} + <td class="stat-perms"><span>{{ file_.stat.perms }}</span></td> + {% if config["DIR_LS_LONG"] %}<td class="stat-size"><span>{{ "{:,d}".format(file_.stat.size) }}</span></td>{% endif %} + <td class="item-name"><a href="{{ url_for('.source', path_to=path+'/'+file_.name) }}">{{ file_.name }}</a> + {% if config["DIR_LS_LONG"] %} + {% if file_.stat.symlink_dest is not none %}{{ " â " + file_.stat.symlink_dest }}{% endif %} + {% endif %} + </td> + </tr> {% endfor %} </table> +<script type="text/javascript"> + debsources.source_folder(); +</script> + {% endblock %} diff --git a/debsources/app/sources/views.py b/debsources/app/sources/views.py index 72c30b4..b31a3c5 100644 --- a/debsources/app/sources/views.py +++ b/debsources/app/sources/views.py @@ -153,6 +153,7 @@ class SourceView(GeneralView): 'sources/source_folder.html', subdirs=filter(lambda x: x['type'] == "directory", content), subfiles=filter(lambda x: x['type'] == "file", content), + nb_hidden_files=sum(1 for f in content if f['hidden']), pathl=Location.get_path_links(".source", path),) return dict(type="directory", diff --git a/debsources/app/static/javascript/debsources.js b/debsources/app/static/javascript/debsources.js index 844e58a..ced5cf6 100644 --- a/debsources/app/static/javascript/debsources.js +++ b/debsources/app/static/javascript/debsources.js @@ -1,4 +1,4 @@ -/* Copyright (C) 2014 Jason Pleau <ja...@jpleau.ca> +/* Copyright (C) 2015 Jason Pleau <ja...@jpleau.ca> * * This file is part of Debsources. * @@ -27,128 +27,151 @@ * */ -var debsources = function(message_pos) { - var print_lines = function() { - var position = message_pos; - var msgbox = document.getElementById('messages'); - var index = document.getElementById('sourceslinenumbers'); - var divHeight = msgbox.offsetHeight; - var lineHeight = parseInt(window.getComputedStyle(index).getPropertyValue('line-height'),10); - var lines = Math.ceil(divHeight / lineHeight)+1; // always insert one more line below the last line of code - - for(i=0; i<lines; ++i){ - var element = document.createElement('a'); - var s = '<a></a><br>'; // lines corr. messages do no need indexes - element.innerHTML = s; - var refnode = document.getElementById('L'+position.toString()); - refnode.parentNode.insertBefore(element,refnode.nextSibling);} //insert after the node with assigned position - }; - - function highlight_lines(start, end) { - // First, remove the highlight class from elements that might already have it - var elements = document.querySelectorAll("span.highlight"); - for (i = 0; i < elements.length; ++i) { - var element = elements[i]; - element.className = element.className.replace(/\bhighlight\b/, ''); - } - - // Then, add the highlight class to elements that contain the lines we want to highlight - for (i = start; i <= end; ++i) { - var element = document.getElementById("line" + i); - element.className = element.className + " highlight "; - } - } - - var hash_changed = function(event, scroll) { - - event = typeof event !== 'undefined' ? event: null; - scroll = typeof scroll !== 'undefined' ? scroll: false; - - // Will match strings like #L15 and #L15-L20 - var regex = /#L(\d+)(-L(\d+))*$/; - - var match = regex.exec(window.location.hash); - if (match != null) { - var first_line = second_line = null; - first_line = parseInt(match[1]); - - if (typeof match[3] !== 'undefined' && match[3].length > 0) { - second_line = parseInt(match[3]); - } else { - second_line = first_line; - } - - // If we get something like #L20-L15, just swap the two line numbers so the loop will work - if (second_line < first_line) { - var tmp = first_line; - first_line = second_line; - second_line = tmp; - } - - highlight_lines(first_line, second_line); - - if (scroll) { - window.scroll(0, document.getElementById("L"+first_line).offsetTop); - } - } - }; - - - function change_hash_without_scroll(element, hash) { - // This is necessary because when changing window.location.hash, the window will - // scroll to the element's id if it matches the hash - var id = element.id; - element.id = id+'-tmpNoScroll'; - window.location.hash = hash; - element.id = id; - } - - var last_clicked; - var line_click_handler = function(event) { - if (event.preventDefault) { - event.preventDefault(); - } else { - event.returnValue = false; - } - - var callerElement = event.target || event.srcElement; - - if (!event.shiftKey || !last_clicked) { - last_clicked = callerElement; - change_hash_without_scroll(callerElement, "L" + (callerElement.textContent || callerElement.innerText)); - } else { - var first_line = parseInt(last_clicked.textContent || last_clicked.innerText); - var second_line = parseInt(callerElement.textContent || callerElement.innerText); - - if (second_line < first_line) { - var tmp = first_line; - first_line = second_line; - second_line = tmp; - } - - change_hash_without_scroll(callerElement, "L" + first_line + "-L" + second_line); - } - }; - - var window_load_sourcecode = function(event) { - var line_numbers = document.querySelectorAll("#sourceslinenumbers a"); - for (i = 0; i < line_numbers.length; ++i) { - var line_number_element = line_numbers[i]; - if (line_number_element.addEventListener) { - line_number_element.addEventListener('click', line_click_handler, false); - } else { - line_number_element.attachEvent('onclick', line_click_handler); - } - } - hash_changed(null, true); - }; - - if (window.addEventListener) { - window.addEventListener('load', window_load_sourcecode, false); - } else { - window.attachEvent('onload', window_load_sourcecode); - } - - window.onhashchange = hash_changed; - window.onload = print_lines; +var debsources = { + source_file: function(message_pos) { + var print_lines = function() { + var position = message_pos; + var msgbox = document.getElementById('messages'); + var index = document.getElementById('sourceslinenumbers'); + var divHeight = msgbox.offsetHeight; + var lineHeight = parseInt(window.getComputedStyle(index).getPropertyValue('line-height'),10); + var lines = Math.ceil(divHeight / lineHeight)+1; // always insert one more line below the last line of code + + for(i=0; i<lines; ++i){ + var element = document.createElement('a'); + var s = '<a></a><br>'; // lines corr. messages do no need indexes + element.innerHTML = s; + var refnode = document.getElementById('L'+position.toString()); + refnode.parentNode.insertBefore(element,refnode.nextSibling);} //insert after the node with assigned position + }; + + function highlight_lines(start, end) { + // First, remove the highlight class from elements that might already have it + var elements = document.querySelectorAll("span.highlight"); + for (i = 0; i < elements.length; ++i) { + var element = elements[i]; + element.className = element.className.replace(/\bhighlight\b/, ''); + } + + // Then, add the highlight class to elements that contain the lines we want to highlight + for (i = start; i <= end; ++i) { + var element = document.getElementById("line" + i); + element.className = element.className + " highlight "; + } + } + + var hash_changed = function(event, scroll) { + + event = typeof event !== 'undefined' ? event: null; + scroll = typeof scroll !== 'undefined' ? scroll: false; + + // Will match strings like #L15 and #L15-L20 + var regex = /#L(\d+)(-L(\d+))*$/; + + var match = regex.exec(window.location.hash); + if (match != null) { + var first_line = second_line = null; + first_line = parseInt(match[1]); + + if (typeof match[3] !== 'undefined' && match[3].length > 0) { + second_line = parseInt(match[3]); + } else { + second_line = first_line; + } + + // If we get something like #L20-L15, just swap the two line numbers so the loop will work + if (second_line < first_line) { + var tmp = first_line; + first_line = second_line; + second_line = tmp; + } + + highlight_lines(first_line, second_line); + + if (scroll) { + window.scroll(0, document.getElementById("L"+first_line).offsetTop); + } + } + }; + + + function change_hash_without_scroll(element, hash) { + // This is necessary because when changing window.location.hash, the window will + // scroll to the element's id if it matches the hash + var id = element.id; + element.id = id+'-tmpNoScroll'; + window.location.hash = hash; + element.id = id; + } + + var last_clicked; + var line_click_handler = function(event) { + if (event.preventDefault) { + event.preventDefault(); + } else { + event.returnValue = false; + } + + var callerElement = event.target || event.srcElement; + + if (!event.shiftKey || !last_clicked) { + last_clicked = callerElement; + change_hash_without_scroll(callerElement, "L" + (callerElement.textContent || callerElement.innerText)); + } else { + var first_line = parseInt(last_clicked.textContent || last_clicked.innerText); + var second_line = parseInt(callerElement.textContent || callerElement.innerText); + + if (second_line < first_line) { + var tmp = first_line; + first_line = second_line; + second_line = tmp; + } + + change_hash_without_scroll(callerElement, "L" + first_line + "-L" + second_line); + } + }; + + var window_load_sourcecode = function(event) { + var line_numbers = document.querySelectorAll("#sourceslinenumbers a"); + for (i = 0; i < line_numbers.length; ++i) { + var line_number_element = line_numbers[i]; + if (line_number_element.addEventListener) { + line_number_element.addEventListener('click', line_click_handler, false); + } else { + line_number_element.attachEvent('onclick', line_click_handler); + } + } + hash_changed(null, true); + }; + + if (window.addEventListener) { + window.addEventListener('load', window_load_sourcecode, false); + } else { + window.attachEvent('onload', window_load_sourcecode); + } + + window.onhashchange = hash_changed; + window.onload = print_lines; + }, + + source_folder: function() { + document.getElementById("btn_toggle_hidden_files").onclick = function(event) { + event.preventDefault(); + var action = this.getAttribute('data-action'); + var actionTextElement = document.querySelectorAll("#btn_toggle_hidden_files span")[0]; + var elements = document.querySelectorAll(".dir-listing tr.hidden_file"); + for (i = 0; i < elements.length; ++i) { + var element = elements[i]; + if (action == "show") { + element.className = element.className + " visible"; + action = "Hide"; + } else { + element.className = element.className.replace(/\bvisible\b/, ''); + action = "Show"; + } + actionTextElement.innerText = action; + this.setAttribute('data-action', action.toLowerCase()); + } + } + } }; -- 2.1.4