[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#762944: debsources: make .pc/ exclusion a configuration parameter



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 <jason@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 <jason@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 <jason@jpleau.ca>
+/* Copyright (C) 2015  Jason Pleau <jason@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


Reply to: