projects/booket

changeset 7:a1a934adff8d version-2

Add support for favicons
author Guido Berhoerster <guido+booket@berhoerster.name>
date Sun Sep 14 23:12:37 2014 +0200 (2014-09-14)
parents e9ad4c625b7a
children aca8d797d569
files .hgignore Makefile booket.css booket.html booket.js missing-favicon.src.svg
line diff
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/.hgignore	Sun Sep 14 23:12:37 2014 +0200
     1.3 @@ -0,0 +1,3 @@
     1.4 +syntax: regexp
     1.5 +
     1.6 +(?<!\.src)\.svg$
     2.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     2.2 +++ b/Makefile	Sun Sep 14 23:12:37 2014 +0200
     2.3 @@ -0,0 +1,44 @@
     2.4 +#
     2.5 +# Copyright (C) 2014 Guido Berhoerster <guido+booket@berhoerster.name>
     2.6 +#
     2.7 +# Permission is hereby granted, free of charge, to any person obtaining
     2.8 +# a copy of this software and associated documentation files (the
     2.9 +# "Software"), to deal in the Software without restriction, including
    2.10 +# without limitation the rights to use, copy, modify, merge, publish,
    2.11 +# distribute, sublicense, and/or sell copies of the Software, and to
    2.12 +# permit persons to whom the Software is furnished to do so, subject to
    2.13 +# the following conditions:
    2.14 +#
    2.15 +# The above copyright notice and this permission notice shall be included
    2.16 +# in all copies or substantial portions of the Software.
    2.17 +#
    2.18 +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
    2.19 +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
    2.20 +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
    2.21 +# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
    2.22 +# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
    2.23 +# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
    2.24 +# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    2.25 +#
    2.26 +
    2.27 +SCOUR :=	scour
    2.28 +XMLLINT :=	xmllint
    2.29 +
    2.30 +SVGIMAGES :=	missing-favicon.svg
    2.31 +
    2.32 +.DEFAULT_TARGET = all
    2.33 +
    2.34 +.PHONY: all clean clobber
    2.35 +
    2.36 +all: $(SVGIMAGES)
    2.37 +
    2.38 +%.svg: %.src.svg
    2.39 +	$(SCOUR) --quiet -i $< --create-groups --enable-id-stripping \
    2.40 +	    --enable-comment-stripping --remove-metadata \
    2.41 +	    --no-renderer-workaround --strip-xml-prolog --enable-viewboxing \
    2.42 +	    --set-precision=5 | $(XMLLINT) --format --noblanks --output $@ -
    2.43 +
    2.44 +clean:
    2.45 +	rm -f $(SVGIMAGES)
    2.46 +
    2.47 +clobber: clean
     3.1 --- a/booket.css	Wed Sep 10 19:45:23 2014 +0200
     3.2 +++ b/booket.css	Sun Sep 14 23:12:37 2014 +0200
     3.3 @@ -63,6 +63,10 @@
     3.4      white-space: nowrap;
     3.5  }
     3.6  
     3.7 +img {
     3.8 +    border: none;
     3.9 +}
    3.10 +
    3.11  h1 {
    3.12      font-size: 2em;
    3.13      margin: .67em 0
    3.14 @@ -130,6 +134,11 @@
    3.15      margin: 1em 0 0 0;
    3.16  }
    3.17  
    3.18 +form.bookmark-editor-form img.bookmark-favicon {
    3.19 +    display: block;
    3.20 +    padding: 1px 0;
    3.21 +}
    3.22 +
    3.23  #bookmarklet,
    3.24  #keyboard-shortcuts {
    3.25      float: right;
     4.1 --- a/booket.html	Wed Sep 10 19:45:23 2014 +0200
     4.2 +++ b/booket.html	Sun Sep 14 23:12:37 2014 +0200
     4.3 @@ -51,6 +51,9 @@
     4.4          name="url" size="60" placeholder="http://example.com/"></input></label>
     4.5          <label>Title <input type="text" name="title" size="60"
     4.6          placeholder="A Title"></input></label>
     4.7 +        <label>Favicon <img width="16" height="16" src="missing-favicon.svg"
     4.8 +        class="bookmark-favicon" alt=""></img><input type="hidden"
     4.9 +        name="favicon"></input></label>
    4.10          <div>
    4.11            <ul class="tag-input-list"></ul>
    4.12            <button type="button" name="more-tags">Add more tags</button>
    4.13 @@ -144,7 +147,8 @@
    4.14          <template id="bookmark-template">
    4.15            <li>
    4.16              <details>
    4.17 -              <summary><p>
    4.18 +              <summary><p><img width="16" height="16"
    4.19 +                  class="bookmark-favicon"></img>
    4.20                    <a class="bookmark-link" target="_blank"></a>
    4.21                    <span class="bookmark-hostname"></span>
    4.22                  </p>
     5.1 --- a/booket.js	Wed Sep 10 19:45:23 2014 +0200
     5.2 +++ b/booket.js	Sun Sep 14 23:12:37 2014 +0200
     5.3 @@ -25,15 +25,73 @@
     5.4  'use strict';
     5.5  
     5.6  var BOOKMARKLET_URI =
     5.7 -    'javascript:(function () {' +
     5.8 -        '\'use strict\';' +
     5.9 +    'javascript:(function() {' +
    5.10 +         '\'use strict\';' +
    5.11 +     '' +
    5.12 +        'function displayBookmarkData(bookmarkData) {' +
    5.13 +            'window.alert(\'Copy the following data and paste it into \' +' +
    5.14 +                '\'Booket:\\n\\n\' + JSON.stringify(bookmarkData));' +
    5.15 +        '}' +
    5.16      '' +
    5.17 -        'window.alert(\'Copy the following data and paste it into \' +' +
    5.18 -            '\'Booket:\\n\\n\' + JSON.stringify({' +
    5.19 -                '\'url\': document.URL,' +
    5.20 -                '\'title\': document.title' +
    5.21 -            '}));' +
    5.22 -    '}) ();';
    5.23 +        'var bookmarkData = {' +
    5.24 +            '\'url\': document.URL,' +
    5.25 +            '\'title\': document.title,' +
    5.26 +            '\'favicon\': undefined' +
    5.27 +        '};' +
    5.28 +        'var faviconLinkElement;' +
    5.29 +        'var faviconUrls = [];' +
    5.30 +        'var aElement;' +
    5.31 +        'var canvasElement;' +
    5.32 +        'var canvasCtx;' +
    5.33 +        'var imgElement;' +
    5.34 +    '' +
    5.35 +        'aElement = document.createElement(\'a\');' +
    5.36 +        'aElement.href = document.URL;' +
    5.37 +    '' +
    5.38 +        'faviconUrls.push(aElement.protocol + \'//\' + aElement.host + ' +
    5.39 +            '\'/favicon.ico\');' +
    5.40 +    '' +
    5.41 +        'faviconLinkElement = document.querySelector(' +
    5.42 +            '\'link[rel~=\\\'icon\\\']\');' +
    5.43 +        'if (faviconLinkElement !== null) {' +
    5.44 +            'faviconUrls.push(faviconLinkElement.href);' +
    5.45 +        '}' +
    5.46 +    '' +
    5.47 +        'canvasElement = document.createElement(\'canvas\');' +
    5.48 +        'canvasCtx = canvasElement.getContext(\'2d\');' +
    5.49 +    '' +
    5.50 +        'imgElement = new Image();' +
    5.51 +        'imgElement.addEventListener(\'load\', function(e) {' +
    5.52 +            'var faviconUrl;' +
    5.53 +    '' +
    5.54 +            'canvasElement.width = 16;' +
    5.55 +            'canvasElement.height = 16;' +
    5.56 +            'canvasCtx.clearRect(0, 0, 16, 16);' +
    5.57 +            'try {' +
    5.58 +                'canvasCtx.drawImage(this, 0, 0, 16, 16);' +
    5.59 +                'bookmarkData.favicon = canvasElement.toDataURL();' +
    5.60 +            '} catch (exception) {' +
    5.61 +                'faviconUrl = faviconUrls.pop();' +
    5.62 +            '}' +
    5.63 +            'if (bookmarkData.favicon !== undefined || ' +
    5.64 +                    'faviconUrl === undefined) {' +
    5.65 +                'displayBookmarkData(bookmarkData);' +
    5.66 +            '} else {' +
    5.67 +                'imgElement.src = faviconUrl;' +
    5.68 +            '}' +
    5.69 +        '});' +
    5.70 +        'imgElement.addEventListener(\'error\', function(e) {' +
    5.71 +            'var faviconUrl;' +
    5.72 +    '' +
    5.73 +            'faviconUrl = faviconUrls.pop();' +
    5.74 +            'if (faviconUrl !== undefined) {' +
    5.75 +                'imgElement.src = faviconUrl;' +
    5.76 +            '} else {' +
    5.77 +                'displayBookmarkData(bookmarkData);' +
    5.78 +            '}' +
    5.79 +        '});' +
    5.80 +        'imgElement.src = faviconUrls.pop();' +
    5.81 +    '})();';
    5.82  
    5.83  
    5.84  /*
    5.85 @@ -348,7 +406,7 @@
    5.86   * model
    5.87   */
    5.88  
    5.89 -var Bookmark = function (url, title, tags, ctime, mtime) {
    5.90 +var Bookmark = function (url, title, favicon, tags, ctime, mtime) {
    5.91      var parsedTime;
    5.92  
    5.93      if (!isString(url)) {
    5.94 @@ -358,6 +416,12 @@
    5.95  
    5.96      this.title = (isString(title) && title !== '') ? title : url;
    5.97  
    5.98 +    if (isString(favicon) && favicon.match(/^data:image\/png;base64,/)) {
    5.99 +        this.favicon = favicon;
   5.100 +    } else {
   5.101 +        this.favicon = undefined;
   5.102 +    }
   5.103 +
   5.104      if (Array.isArray(tags)) {
   5.105          // remove duplicates, non-string or empty tags and tags containing
   5.106          // commas
   5.107 @@ -647,7 +711,8 @@
   5.108      parsedData.bookmarks.forEach(function (bookmark) {
   5.109          if (isString(bookmark.url) && bookmark.url !== '') {
   5.110              bookmarks.push(new Bookmark(bookmark.url, bookmark.title,
   5.111 -                bookmark.tags, bookmark.ctime, bookmark.mtime));
   5.112 +                bookmark.favicon, bookmark.tags, bookmark.ctime,
   5.113 +                bookmark.mtime));
   5.114          }
   5.115      }, this);
   5.116  
   5.117 @@ -822,7 +887,6 @@
   5.118      var saveFormElement;
   5.119      var loadFormElement;
   5.120      var newNode;
   5.121 -    var editorFormElement;
   5.122  
   5.123      ObservableMixin.call(this);
   5.124  
   5.125 @@ -839,17 +903,24 @@
   5.126      newNode = document.importNode(
   5.127          document.querySelector('#bookmark-editor-template').content, true);
   5.128  
   5.129 -    editorFormElement = newNode.querySelector('form.bookmark-editor-form');
   5.130 -    editorFormElement.querySelector('legend').textContent = 'Add Bookmark';
   5.131 -    editorFormElement.querySelector('input:not([type="hidden"])').accessKey =
   5.132 -        'a';
   5.133 -    editorFormElement.addEventListener('input', this);
   5.134 -    editorFormElement.addEventListener('click', this);
   5.135 -    editorFormElement.addEventListener('submit', this);
   5.136 -    editorFormElement.addEventListener('reset', this);
   5.137 +    this.editorFormElement = newNode.querySelector('form.bookmark-editor-form');
   5.138 +    this.editorFormElement.querySelector('legend').textContent = 'Add Bookmark';
   5.139 +    this.editorFormElement.querySelector(
   5.140 +        'input:not([type="hidden"])').accessKey = 'a';
   5.141 +    this.editorFormElement.addEventListener('input', this);
   5.142 +    this.editorFormElement.addEventListener('click', this);
   5.143 +    this.editorFormElement.addEventListener('submit', this);
   5.144 +    this.editorFormElement.addEventListener('reset', this);
   5.145 +
   5.146 +    this.faviconImageElement =
   5.147 +        this.editorFormElement.querySelector('img.bookmark-favicon');
   5.148 +    this.faviconImageElement.addEventListener('load', this);
   5.149 +    this.faviconImageElement.addEventListener('error', this);
   5.150 +
   5.151 +    this.missingFaviconUri = this.faviconImageElement.src;
   5.152  
   5.153      this.editTagListElement =
   5.154 -        editorFormElement.querySelector('ul.tag-input-list');
   5.155 +        this.editorFormElement.querySelector('ul.tag-input-list');
   5.156      this.editTagListElement.appendChild(this.createTagInputElement(''));
   5.157  
   5.158      saveFormElement.parentNode.insertBefore(newNode,
   5.159 @@ -876,6 +947,19 @@
   5.160      var i;
   5.161  
   5.162      switch (e.type) {
   5.163 +    case 'error':
   5.164 +        if (e.target.classList.contains('bookmark-favicon')) {
   5.165 +            if (e.target.src !== this.missingFaviconUri) {
   5.166 +                e.target.src = this.missingFaviconUri;
   5.167 +            }
   5.168 +        }
   5.169 +        break;
   5.170 +    case 'load':
   5.171 +        if (e.target.classList.contains('bookmark-favicon')) {
   5.172 +            this.editorFormElement.favicon.value =
   5.173 +                (e.target.src !== this.missingFaviconUri) ?  e.target.src : '';
   5.174 +        }
   5.175 +        break;
   5.176      case 'input':
   5.177          if (e.target.name === 'bookmarklet-import') {
   5.178              // get rid of any preceding text
   5.179 @@ -893,6 +977,10 @@
   5.180              if (isString(parsedData.title) && parsedData.title !== '') {
   5.181                  e.target.form.elements.title.value = parsedData.title;
   5.182              }
   5.183 +            if (isString(parsedData.favicon) &&
   5.184 +                    parsedData.favicon.match(/^data:image\/png;base64,/)) {
   5.185 +                this.faviconImageElement.src = parsedData.favicon;
   5.186 +            }
   5.187          }
   5.188          break;
   5.189      case 'click':
   5.190 @@ -928,7 +1016,7 @@
   5.191              }
   5.192  
   5.193              this.notify('save-bookmark', e.target.url.value,
   5.194 -                e.target.title.value, tags);
   5.195 +                e.target.title.value, e.target.favicon.value, tags);
   5.196  
   5.197              e.target.reset();
   5.198          }
   5.199 @@ -937,6 +1025,9 @@
   5.200          if (e.target.classList.contains('bookmark-editor-form')) {
   5.201              e.target.blur();
   5.202  
   5.203 +            e.target.querySelector('img.bookmark-favicon').src =
   5.204 +                this.missingFaviconUri;
   5.205 +
   5.206              // remove all but one tag input element
   5.207              while (this.editTagListElement.firstChild !== null) {
   5.208                  this.editTagListElement.removeChild(
   5.209 @@ -979,6 +1070,7 @@
   5.210      this.tagInputTemplate = document.querySelector('#tag-input-template');
   5.211  
   5.212      this.bookmarkListElement = document.querySelector('ul#bookmark-list');
   5.213 +    this.bookmarkListElement.addEventListener('input', this);
   5.214      this.bookmarkListElement.addEventListener('click', this);
   5.215      this.bookmarkListElement.addEventListener('submit', this);
   5.216      this.bookmarkListElement.addEventListener('reset', this);
   5.217 @@ -991,17 +1083,65 @@
   5.218  
   5.219      this.bookmarkMessageElement = document.querySelector('#bookmark-message');
   5.220  
   5.221 +    this.missingFaviconUri = '';
   5.222 +
   5.223      this.updateBookmarkMessage();
   5.224  };
   5.225  
   5.226  extend(BookmarkView, ObservableMixin);
   5.227  
   5.228  BookmarkView.prototype.handleEvent = function (e) {
   5.229 +    var bookmarkletData;
   5.230 +    var parsedData;
   5.231      var i;
   5.232      var tags = [];
   5.233      var node;
   5.234  
   5.235      switch (e.type) {
   5.236 +    case 'error':
   5.237 +        if (e.target.classList.contains('bookmark-favicon')) {
   5.238 +            if (e.target.src !== this.missingFaviconUri) {
   5.239 +                e.target.src = this.missingFaviconUri;
   5.240 +            }
   5.241 +        }
   5.242 +        break;
   5.243 +    case 'load':
   5.244 +        if (e.target.classList.contains('bookmark-favicon')) {
   5.245 +            node = e.target;
   5.246 +            while ((node = node.parentNode) !== null) {
   5.247 +                if (node.classList.contains('bookmark-editor-form')) {
   5.248 +                    node.favicon.value =
   5.249 +                        (e.target.src !== this.missingFaviconUri) ?
   5.250 +                        e.target.src : '';
   5.251 +                    break;
   5.252 +                }
   5.253 +            }
   5.254 +        }
   5.255 +        break;
   5.256 +    case 'input':
   5.257 +        if (e.target.name === 'bookmarklet-import') {
   5.258 +            // get rid of any preceding text
   5.259 +            bookmarkletData = e.target.value.replace(/^[^{]*/, '');
   5.260 +
   5.261 +            try {
   5.262 +                parsedData = JSON.parse(bookmarkletData);
   5.263 +            } catch (exception) {
   5.264 +                return;
   5.265 +            }
   5.266 +
   5.267 +            if (isString(parsedData.url) && parsedData.url !== '') {
   5.268 +                e.target.form.elements.url.value = parsedData.url;
   5.269 +            }
   5.270 +            if (isString(parsedData.title) && parsedData.title !== '') {
   5.271 +                e.target.form.elements.title.value = parsedData.title;
   5.272 +            }
   5.273 +            if (isString(parsedData.favicon) &&
   5.274 +                    parsedData.favicon.match(/^data:image\/png;base64,/)) {
   5.275 +                e.target.form.querySelector('img.bookmark-favicon').src =
   5.276 +                    parsedData.favicon;
   5.277 +            }
   5.278 +        }
   5.279 +        break;
   5.280      case 'click':
   5.281          switch (e.target.name) {
   5.282          case 'edit-bookmark':
   5.283 @@ -1040,7 +1180,8 @@
   5.284              }
   5.285  
   5.286              this.notify('save-bookmark', e.target.url.value,
   5.287 -                e.target.title.value, tags, e.target['original-url'].value);
   5.288 +                e.target.title.value, e.target.favicon.value, tags,
   5.289 +                e.target['original-url'].value);
   5.290          } else if (e.target.id === 'search-form') {
   5.291              // search
   5.292              e.preventDefault();
   5.293 @@ -1083,6 +1224,7 @@
   5.294  BookmarkView.prototype.onBookmarkAdded = function (bookmark) {
   5.295      var newNode;
   5.296      var bookmarkElement;
   5.297 +    var faviconElement;
   5.298      var linkElement;
   5.299      var hostnameElement;
   5.300      var urlElement;
   5.301 @@ -1095,6 +1237,11 @@
   5.302      bookmarkElement = newNode.querySelector('li');
   5.303      bookmarkElement.dataset.bookmarkUrl = bookmark.url;
   5.304  
   5.305 +    faviconElement = bookmarkElement.querySelector('img.bookmark-favicon');
   5.306 +    faviconElement.src = (bookmark.favicon) ? bookmark.favicon :
   5.307 +        this.missingFaviconUri;
   5.308 +    faviconElement.alt = '';
   5.309 +
   5.310      linkElement = bookmarkElement.querySelector('a.bookmark-link');
   5.311      linkElement.textContent = linkElement.title = bookmark.title;
   5.312      linkElement.href = bookmark.url;
   5.313 @@ -1211,6 +1358,7 @@
   5.314      var bookmarkElement;
   5.315      var newNode;
   5.316      var formElement;
   5.317 +    var faviconImageElement;
   5.318      var editTagListElement;
   5.319  
   5.320      bookmarkElement =
   5.321 @@ -1231,6 +1379,14 @@
   5.322      formElement.url.value = bookmark.url;
   5.323      formElement.title.value = bookmark.title;
   5.324  
   5.325 +    faviconImageElement = formElement.querySelector('img.bookmark-favicon');
   5.326 +    faviconImageElement.addEventListener('load', this);
   5.327 +    faviconImageElement.addEventListener('error', this);
   5.328 +    this.missingFaviconUri = faviconImageElement.src;
   5.329 +    if (bookmark.favicon) {
   5.330 +        faviconImageElement.src = bookmark.favicon;
   5.331 +    }
   5.332 +
   5.333      editTagListElement = formElement.querySelector('ul.tag-input-list');
   5.334      bookmark.tags.forEach(function (tag) {
   5.335          editTagListElement.appendChild(this.createTagInputElement(tag));
   5.336 @@ -1379,8 +1535,8 @@
   5.337          this.bookmarkModel.get(bookmarkUrl));
   5.338  };
   5.339  
   5.340 -BooketController.prototype.onSaveBookmark = function (url, title, tags,
   5.341 -        originalUrl) {
   5.342 +BooketController.prototype.onSaveBookmark = function (url, title,
   5.343 +        favicon, tags, originalUrl) {
   5.344      var ctime;
   5.345  
   5.346      if (originalUrl === undefined) {
   5.347 @@ -1403,7 +1559,7 @@
   5.348  
   5.349          this.bookmarkModel.delete(originalUrl);
   5.350      }
   5.351 -    this.bookmarkModel.add(new Bookmark(url, title, tags, ctime));
   5.352 +    this.bookmarkModel.add(new Bookmark(url, title, favicon, tags, ctime));
   5.353  };
   5.354  
   5.355  BooketController.prototype.onDeleteBookmark = function (bookmarkUrl) {
     6.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     6.2 +++ b/missing-favicon.src.svg	Sun Sep 14 23:12:37 2014 +0200
     6.3 @@ -0,0 +1,90 @@
     6.4 +<?xml version="1.0" encoding="UTF-8" standalone="no"?>
     6.5 +<!-- Created with Inkscape (http://www.inkscape.org/) -->
     6.6 +
     6.7 +<svg
     6.8 +   xmlns:dc="http://purl.org/dc/elements/1.1/"
     6.9 +   xmlns:cc="http://creativecommons.org/ns#"
    6.10 +   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
    6.11 +   xmlns:svg="http://www.w3.org/2000/svg"
    6.12 +   xmlns="http://www.w3.org/2000/svg"
    6.13 +   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
    6.14 +   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
    6.15 +   width="16"
    6.16 +   height="16"
    6.17 +   id="svg2"
    6.18 +   version="1.1"
    6.19 +   inkscape:version="0.48.4 r9939"
    6.20 +   sodipodi:docname="missing-favicon.svg">
    6.21 +  <title
    6.22 +     id="title2985">Missing Favicon</title>
    6.23 +  <defs
    6.24 +     id="defs4" />
    6.25 +  <sodipodi:namedview
    6.26 +     id="base"
    6.27 +     pagecolor="#ffffff"
    6.28 +     bordercolor="#666666"
    6.29 +     borderopacity="1.0"
    6.30 +     inkscape:pageopacity="0.0"
    6.31 +     inkscape:pageshadow="2"
    6.32 +     inkscape:zoom="43.9375"
    6.33 +     inkscape:cx="7.4651494"
    6.34 +     inkscape:cy="8"
    6.35 +     inkscape:document-units="px"
    6.36 +     inkscape:current-layer="layer1"
    6.37 +     showgrid="true"
    6.38 +     inkscape:window-width="1542"
    6.39 +     inkscape:window-height="947"
    6.40 +     inkscape:window-x="54"
    6.41 +     inkscape:window-y="0"
    6.42 +     inkscape:window-maximized="0">
    6.43 +    <inkscape:grid
    6.44 +       type="xygrid"
    6.45 +       id="grid3006"
    6.46 +       empspacing="5"
    6.47 +       visible="true"
    6.48 +       enabled="true"
    6.49 +       snapvisiblegridlinesonly="true" />
    6.50 +  </sodipodi:namedview>
    6.51 +  <metadata
    6.52 +     id="metadata7">
    6.53 +    <rdf:RDF>
    6.54 +      <cc:Work
    6.55 +         rdf:about="">
    6.56 +        <dc:format>image/svg+xml</dc:format>
    6.57 +        <dc:type
    6.58 +           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
    6.59 +        <dc:title>Missing Favicon</dc:title>
    6.60 +        <dc:creator>
    6.61 +          <cc:Agent>
    6.62 +            <dc:title>Guido Berhoerster</dc:title>
    6.63 +          </cc:Agent>
    6.64 +        </dc:creator>
    6.65 +        <dc:date>2014-09-14</dc:date>
    6.66 +        <cc:license
    6.67 +           rdf:resource="http://creativecommons.org/licenses/publicdomain/" />
    6.68 +      </cc:Work>
    6.69 +      <cc:License
    6.70 +         rdf:about="http://creativecommons.org/licenses/publicdomain/">
    6.71 +        <cc:permits
    6.72 +           rdf:resource="http://creativecommons.org/ns#Reproduction" />
    6.73 +        <cc:permits
    6.74 +           rdf:resource="http://creativecommons.org/ns#Distribution" />
    6.75 +        <cc:permits
    6.76 +           rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
    6.77 +      </cc:License>
    6.78 +    </rdf:RDF>
    6.79 +  </metadata>
    6.80 +  <g
    6.81 +     inkscape:label="Layer 1"
    6.82 +     inkscape:groupmode="layer"
    6.83 +     id="layer1"
    6.84 +     transform="translate(0,-1036.3622)">
    6.85 +    <rect
    6.86 +       style="fill:none;stroke:#555753;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4.00000029;stroke-opacity:1;stroke-dasharray:1,1;stroke-dashoffset:0.5"
    6.87 +       id="rect3008"
    6.88 +       width="15"
    6.89 +       height="15"
    6.90 +       x="0.5"
    6.91 +       y="1036.8622" />
    6.92 +  </g>
    6.93 +</svg>