From a442d656b50c63beb1488c7d6c1dce57ff99d113 Mon Sep 17 00:00:00 2001 From: tpearson Date: Tue, 29 Dec 2009 00:07:09 +0000 Subject: * Added searchbar * Added directory filter * Added adblocker * Fixed webarchiver * Automake fixups git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdeaddons@1067137 283d02a7-25f6-0310-bc7c-ecb5cbfe19da --- konq-plugins/Makefile.am | 2 +- konq-plugins/dirfilter/Makefile.am | 4 +- konq-plugins/dirfilter/dirfilter_plugin.desktop | 124 ++++++++++++++++ konq-plugins/dirfilter/dirfilterplugin.desktop | 1 - konq-plugins/fsview/fsview.desktop | 1 + konq-plugins/searchbar/searchbar.cpp | 183 +++++++++++++++++++++++- konq-plugins/searchbar/searchbar.h | 27 ++++ konq-plugins/webarchiver/plugin_webarchiver.cpp | 4 +- 8 files changed, 337 insertions(+), 9 deletions(-) create mode 100644 konq-plugins/dirfilter/dirfilter_plugin.desktop diff --git a/konq-plugins/Makefile.am b/konq-plugins/Makefile.am index 5949b5a..16a5bc7 100644 --- a/konq-plugins/Makefile.am +++ b/konq-plugins/Makefile.am @@ -1,4 +1,4 @@ -SUBDIRS = crashes khtmlsettingsplugin kimgalleryplugin dirfilter uachanger \ +SUBDIRS = adblock crashes khtmlsettingsplugin kimgalleryplugin dirfilter uachanger \ babelfish validators domtreeviewer webarchiver sidebar kuick \ imagerotation minitools microformat autorefresh fsview searchbar \ arkplugin akregator rellinks mediarealfolder diff --git a/konq-plugins/dirfilter/Makefile.am b/konq-plugins/dirfilter/Makefile.am index fe81189..998f7a0 100644 --- a/konq-plugins/dirfilter/Makefile.am +++ b/konq-plugins/dirfilter/Makefile.am @@ -8,9 +8,9 @@ libdirfilterplugin_la_LIBADD = -lkonq libdirfilterplugin_la_LDFLAGS = -module $(KDE_PLUGIN) $(all_libraries) iconviewdir = $(kde_datadir)/konqiconview/kpartplugins -iconview_DATA = dirfilterplugin.rc dirfilterplugin.desktop +iconview_DATA = dirfilterplugin.rc dirfilter_plugin.desktop listviewdir = $(kde_datadir)/konqlistview/kpartplugins -listview_DATA = dirfilterplugin.rc dirfilterplugin.desktop +listview_DATA = dirfilterplugin.rc dirfilter_plugin.desktop appsdir = $(kde_appsdir)/.hidden apps_DATA = dirfilterplugin.desktop diff --git a/konq-plugins/dirfilter/dirfilter_plugin.desktop b/konq-plugins/dirfilter/dirfilter_plugin.desktop new file mode 100644 index 0000000..5135a48 --- /dev/null +++ b/konq-plugins/dirfilter/dirfilter_plugin.desktop @@ -0,0 +1,124 @@ +[Desktop Entry] +Encoding=UTF-8 +Type=Service +X-KDE-PluginInfo-Author=Dawit Alemayehu,Martin Koller +X-KDE-PluginInfo-Email=adawit@kde.org,m.koller@surfeu.at +X-KDE-PluginInfo-Name=DirFilter +X-KDE-PluginInfo-Version=3.4 +X-KDE-PluginInfo-Website= +X-KDE-PluginInfo-Category=Tools +X-KDE-PluginInfo-Depends= +X-KDE-PluginInfo-License=GPL +X-KDE-PluginInfo-EnabledByDefault=true +Name=Directory Filter Plugin +Name[bg]=Приставка за филтриране на директории +Name[br]=Lugent sil ar renkelloù +Name[bs]=Plugin za filtriranje direktorija +Name[ca]=Connector filtre del directori +Name[cs]=Modul pro filtrování adresářů +Name[da]=Mappefilter-plugin +Name[de]=Ordnerfilter-Modul +Name[el]=Πρόσθετο φίλτρου καταλόγων +Name[eo]=Dosierujfiltrila kromaĵo +Name[es]=Extensión de filtrado de carpetas +Name[et]=Kataloogifiltri plugin +Name[eu]=Direktorioen iragazki plugina +Name[fa]=وصلۀ پالایۀ فهرست راهنما +Name[fi]=Hakemistosuodinsovelma +Name[fr]=Module de filtrage de dossiers +Name[fy]=Mappenfilterplugin +Name[gl]=Plugin de Filtraxe de Cartafoles +Name[he]=תוסף מסנן ספריות +Name[hi]=डिरेक्ट्री फ़िल्टर प्लगइन +Name[hr]=Dodatak filtra mapa +Name[hu]=Könyvtárszűrő bővítőmodul +Name[is]=Íforrit sem síar möppur +Name[it]=Plugin per il filtro delle directory +Name[ja]=ディレクトリ フィルタ プラグイン +Name[kk]=Каталог сүзгісі +Name[km]=កម្មវិធី​ជំនួយ​តម្រង​ថត +Name[lt]=Aplanko filtro priedas +Name[mk]=Приклучок за филтирање папки +Name[ms]=Plugin Penapis Direktori +Name[nb]=Programtillegg til mappefilter +Name[nds]=Ornerfilter-Moduul +Name[ne]=डाइरेक्टरी फिल्टर प्लगइन +Name[nl]=Mappenfilterplugin +Name[nn]=Progamtillegg for katalogfilter +Name[pa]=ਡਾਇਰੈਕਟਰੀ ਫਿਲਟਰ ਪਲੱਗਇਨ +Name[pl]=Wtyczka filtrowania katalogów +Name[pt]='Plugin' de Filtragem de Directorias +Name[pt_BR]=Pasta do Plug-in de Filtro +Name[ru]=Фильтр папок +Name[sk]=Modul adresárového filtra +Name[sl]=Vstavek za filtriranje imenikov +Name[sr]=Прикључак за филтрирање директоријума +Name[sr@Latn]=Priključak za filtriranje direktorijuma +Name[sv]=Insticksprogram för katalogfilter +Name[ta]=அகராதி வடிகட்டி சொருகுப் பொருள் +Name[tg]=Филтри каталогҳо +Name[tr]=Dizin Filtre Eklentisi +Name[uk]=Втулок фільтра каталогів +Name[vi]=Bổ sung lọc thư mục +Name[zh_CN]=目录过滤器插件 +Name[zh_TW]=目錄過濾器外掛程式 +Comment=Directory filter plugin +Comment[af]=Gids filter inplak +Comment[ar]=ملحق تنقية المجلدات +Comment[az]=Qovluq filtr əlavəsi +Comment[bg]=Приставка за филтриране на директории +Comment[br]=Lugent sil ar renkelloù +Comment[bs]=Plugin za filtriranje direktorija +Comment[ca]=Connector filtre del directori +Comment[cs]=Modul pro filtrování adresářů +Comment[cy]=Ategyn hidl cyfeiriadur +Comment[da]=Mappefilter-plugin +Comment[de]=Ein Modul zum Filtern von Ordnerinhalten +Comment[el]=Πρόσθετο φίλτρου καταλόγου +Comment[eo]=Dosierujfiltrila kromaĵo +Comment[es]=Extensión de filtrado de carpetas +Comment[et]=Kataloogifiltri plugin +Comment[eu]=Direktorioen iragazki plugina +Comment[fa]=وصلۀ پالایۀ فهرست راهنما +Comment[fi]=Hakemistosuodinsovelma +Comment[fr]=Module de filtrage de dossiers +Comment[fy]=Mappenfilterplugin +Comment[gl]=Plugin de filtraxe de cartafoles +Comment[he]=תוסף מסנן ספריות +Comment[hi]=डिरेक्ट्री फ़िल्टर प्लगइन +Comment[hr]=Dodatak filtra mapa +Comment[hu]=Könyvtárszűrő modul +Comment[is]=Íforrit sem síar möppur +Comment[it]=Plugin per il filtro delle directory +Comment[ja]=ディレクトリフィルタプラグイン +Comment[kk]=Каталог сүзгі плагин модулі +Comment[km]=កម្មវិធី​ជំនួយ​តម្រង​ថត +Comment[lt]=Aplanko filtro priedas +Comment[mk]=Приклучок за филтирање папки +Comment[nb]=Programtillegg til mappefilter +Comment[nds]=Ornerfilter-Moduul +Comment[ne]=डाइरेक्टरी फिल्टर प्लगइन +Comment[nl]=Mappenfilterplugin +Comment[nn]=Progamtillegg for katalogfilter +Comment[pa]=ਡਾਇਰੈਕਟਰੀ ਫਿਲਟਰ ਪਲੱਗਇਨ +Comment[pl]=Wtyczka filtrowania katalogów +Comment[pt]='Plugin' de filtragem de directorias +Comment[pt_BR]=Pasta de plug-in de filtragem +Comment[ro]=Modul de filtrare a directoarelor de afişat +Comment[ru]=Модуль фильтра папок +Comment[sk]=Modul adresárového filtra +Comment[sl]=Vstavek za filtriranje imenikov +Comment[sr]=Прикључак за филтрирање директоријума +Comment[sr@Latn]=Priključak za filtriranje direktorijuma +Comment[sv]=Insticksprogram för katalogfilter +Comment[ta]= அகராதி வடிகட்டி சொருகுப்பொருள் +Comment[tg]=Модули филтри каталогҳо +Comment[tr]=Dizin filtre eklentisi +Comment[uk]=Втулок фільтра каталогів +Comment[vi]=Bổ sung lọc thư mục +Comment[xh]=Isilawulo secebo lokucoca ulwelo seplagi yangaphakathi +Comment[zh_CN]=目录过滤器插件 +Comment[zh_TW]=目錄過濾器外掛程式 +Icon=filter +X-KDE-ParentApp=konqueror +DocPath=konq-plugins/dirfilter/index.html diff --git a/konq-plugins/dirfilter/dirfilterplugin.desktop b/konq-plugins/dirfilter/dirfilterplugin.desktop index 31dfcf0..0d03ce2 100644 --- a/konq-plugins/dirfilter/dirfilterplugin.desktop +++ b/konq-plugins/dirfilter/dirfilterplugin.desktop @@ -1,5 +1,4 @@ [Desktop Entry] -Type=Service X-KDE-PluginInfo-Author=Dawit Alemayehu,Martin Koller X-KDE-PluginInfo-Email=adawit@kde.org,m.koller@surfeu.at X-KDE-PluginInfo-Name=DirFilter diff --git a/konq-plugins/fsview/fsview.desktop b/konq-plugins/fsview/fsview.desktop index 6f7f163..611fff6 100644 --- a/konq-plugins/fsview/fsview.desktop +++ b/konq-plugins/fsview/fsview.desktop @@ -118,3 +118,4 @@ Comment[zh_TW]=將您的作業系統以樹狀圖檢視 X-DCOP-ServiceType=Multi Categories=Qt;KDE;Utility; X-KDE-ParentApp=konqueror +Hidden=true diff --git a/konq-plugins/searchbar/searchbar.cpp b/konq-plugins/searchbar/searchbar.cpp index 51f5455..739a871 100644 --- a/konq-plugins/searchbar/searchbar.cpp +++ b/konq-plugins/searchbar/searchbar.cpp @@ -1,4 +1,6 @@ /* This file is part of the KDE project + Copyright (C) 2005 by Tobi Vollebregt + Copyright (C) 2004 by Vinay Khaitan Copyright (C) 2004 Arend van Beelen jr. This program is free software; you can redistribute it and/or @@ -33,10 +35,13 @@ #include #include #include +#include #include #include #include +#include + #include #include @@ -57,7 +62,9 @@ SearchBarPlugin::SearchBarPlugin(QObject *parent, const char *name, KParts::Plugin(parent, name), m_searchCombo(0), m_searchMode(UseSearchProvider), - m_urlEnterLock(false) + m_urlEnterLock(false), + m_gsTimer(this), + m_googleMode(GoogleOnly) { m_searchCombo = new SearchBarCombo(0L, "search combo"); m_searchCombo->setDuplicatesEnabled(false); @@ -65,8 +72,10 @@ SearchBarPlugin::SearchBarPlugin(QObject *parent, const char *name, m_searchCombo->setFixedWidth(180); m_searchCombo->setLineEdit(new KLineEdit(m_searchCombo)); m_searchCombo->lineEdit()->installEventFilter(this); + m_searchCombo->listBox()->setFocusProxy(m_searchCombo); m_popupMenu = 0; + m_googleMenu = 0; m_searchComboAction = new KWidgetAction(m_searchCombo, i18n("Search Bar"), 0, 0, 0, actionCollection(), "toolbar_search_bar"); @@ -95,6 +104,11 @@ SearchBarPlugin::SearchBarPlugin(QObject *parent, const char *name, SLOT (partChanged (KParts::Part*))); partChanged(partMan->activePart()); } + + connect(this, SIGNAL(gsCompleteDelayed()), SLOT(gsStartDelay())); + connect(&m_gsTimer, SIGNAL(timeout()), SLOT(gsMakeCompletionList())); + connect(m_searchCombo->listBox(), SIGNAL(highlighted(const QString&)), SLOT(gsSetCompletedText(const QString&))); + connect(m_searchCombo, SIGNAL(activated(const QString&)), SLOT(gsPutTextInBox(const QString&))); } SearchBarPlugin::~SearchBarPlugin() @@ -103,6 +117,7 @@ SearchBarPlugin::~SearchBarPlugin() config->setGroup("SearchBar"); config->writeEntry("Mode", (int) m_searchMode); config->writeEntry("CurrentEngine", m_currentEngine); + config->writeEntry("GoogleSuggestMode", m_googleMode); delete m_searchCombo; m_searchCombo = 0L; @@ -120,6 +135,14 @@ bool SearchBarPlugin::eventFilter(QObject *o, QEvent *e) if( o==m_searchCombo->lineEdit() && e->type() == QEvent::KeyPress ) { QKeyEvent *k = (QKeyEvent *)e; + QString text = k->text(); + if(!text.isEmpty()) + { + if(k->key() != Qt::Key_Return && k->key() != Key_Enter && k->key() != Key_Escape) + { + emit gsCompleteDelayed(); + } + } if(k->state() & ControlButton) { if(k->key()==Key_Down) @@ -133,6 +156,36 @@ bool SearchBarPlugin::eventFilter(QObject *o, QEvent *e) return true; } } + else + { + if (k->key() == Key_Up || k->key() == Key_Down) + { + if(m_searchCombo->listBox()->isVisible()) + { + qApp->sendEvent(m_searchCombo->listBox(), e); + return true; + } + } + } + if (k->key() == Key_Enter || k->key() == Key_Return) + { + /*- Fix a bug which caused the searchbar to search for the completed + input instead of the literal input when enter was pressed and + the listbox was visible. + if(m_searchCombo->listBox()->isVisible()) + { + qApp->sendEvent(m_searchCombo->listBox(),e); + }*/ + } + if (k->key() == Key_Escape) + { + m_searchCombo->listBox()->hide(); + if (m_searchCombo->lineEdit()->hasSelectedText()) + { + m_searchCombo->lineEdit()->setText(m_searchCombo->currentText().left(m_searchCombo->lineEdit()->selectionStart())); + } + m_gsTimer.stop(); + } } return false; } @@ -197,11 +250,16 @@ void SearchBarPlugin::previousSearchEntry() setIcon(); } -void SearchBarPlugin::startSearch(const QString &search) +void SearchBarPlugin::startSearch(const QString &_search) { - if(m_urlEnterLock || search.isEmpty() || !m_part) + if(m_urlEnterLock || _search.isEmpty() || !m_part) return; + m_gsTimer.stop(); + m_searchCombo->listBox()->hide(); + + QString search = _search.section('(', 0, 0).stripWhiteSpace(); + if(m_searchMode == FindInThisPage) { m_part->findText(search, 0); @@ -355,10 +413,16 @@ void SearchBarPlugin::showSelectionMenu() } m_popupMenu->insertSeparator(); + m_googleMenu = new KSelectAction(i18n("Use Google Suggest"), SmallIconSet("ktip"), 0, this, SLOT(selectGoogleSuggestMode()), m_popupMenu); + QStringList google_modes; + google_modes << i18n("For Google Only") << i18n("For All Searches") << i18n("Never"); + m_googleMenu->setItems(google_modes); + m_googleMenu->plug(m_popupMenu); m_popupMenu->insertItem(SmallIcon("enhanced_browsing"), i18n("Select Search Engines..."), this, SLOT(selectSearchEngines()), 0, 1000); connect(m_popupMenu, SIGNAL(activated(int)), SLOT(useSearchProvider(int))); } + m_googleMenu->setCurrentItem(m_googleMode); m_popupMenu->popup(m_searchCombo->mapToGlobal(QPoint(0, m_searchCombo->height() + 1)), 0); } @@ -441,6 +505,7 @@ void SearchBarPlugin::configurationChanged() config->setGroup("SearchBar"); m_searchMode = (SearchModes) config->readNumEntry("Mode", (int) UseSearchProvider); m_currentEngine = config->readEntry("CurrentEngine", engine); + m_googleMode=(GoogleMode)config->readNumEntry("GoogleSuggestMode", GoogleOnly); if ( m_currentEngine.isEmpty() ) m_currentEngine = "google"; @@ -553,4 +618,116 @@ void SearchBarCombo::show() } } +// Google Suggest code + +void SearchBarPlugin::selectGoogleSuggestMode() +{ + m_googleMode = (GoogleMode)m_googleMenu->currentItem(); + KConfig *config = kapp->config(); + config->setGroup("SearchBar"); + config->writeEntry("GoogleSuggestMode", m_googleMode); + config->sync(); +} + +// adapted and modified by Tobi Vollebregt +// original code from Googlebar by Vinay Khaitan + +void SearchBarPlugin::gsStartDelay() +{ + m_gsTimer.stop(); + m_searchCombo->listBox()->hide(); + // FIXME: make configurable + m_gsTimer.start(500, true); +} + +void SearchBarPlugin::gsMakeCompletionList() +{ + if ((m_googleMode==GoogleOnly && m_currentEngine != "google") || m_googleMode==Never) + return; + + if (!m_searchCombo->currentText().isEmpty()) + { + KIO::TransferJob* tj = + KIO::get(KURL("http://www.google.com/complete/search?hl=en&js=true&qu=" + m_searchCombo->currentText()), false, false); + connect(tj, SIGNAL(data(KIO::Job*, const QByteArray&)), this, SLOT(gsDataArrived(KIO::Job*, const QByteArray&))); + connect(tj, SIGNAL(result(KIO::Job*)), this, SLOT(gsJobFinished(KIO::Job*))); + } +} + +void SearchBarPlugin::gsDataArrived(KIO::Job*, const QByteArray& data) +{ + m_gsData += QString::fromUtf8(data.data()); +} + +static QString reformatNumber(const QString& number) +{ + static const char suffix[] = { 'k', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y' }; + QString s = number.stripWhiteSpace(); + uint c = 0; + for (int i = s.length() - 1; i > 0 && s[i] == '0'; --i) ++c; + c /= 3; + if (c >= sizeof(suffix)/sizeof(suffix[0])) + c = sizeof(suffix)/sizeof(suffix[0]) - 1; + s = s.left(s.length() - c * 3) + suffix[c]; + return s; +} + +void SearchBarPlugin::gsJobFinished(KIO::Job* job) +{ + if (((KIO::TransferJob*)job)->error() == 0) + { + QString temp; + temp = m_gsData.mid(m_gsData.find('(') + 1, m_gsData.findRev(')') - m_gsData.find('(') - 1); + temp = temp.mid(temp.find('(') + 1, temp.find(')') - temp.find('(') - 1); + temp.remove('"'); + QStringList compList1 = QStringList::split(',', temp); + temp = m_gsData.mid(m_gsData.find(')') + 1, m_gsData.findRev(')') - m_gsData.find('(') - 1); + temp = temp.mid(temp.find('(') + 1, temp.find(')') - temp.find('(') - 1); + temp.remove('"'); + temp.remove(','); + temp.remove('s'); + QStringList compList2 = QStringList::split("reult", temp); + QStringList finalList; + for(uint j = 0; j < compList1.count(); j++) + { + if (m_googleMode!=ForAll || m_currentEngine == "google") + finalList.append(compList1[j].stripWhiteSpace() + " (" + reformatNumber(compList2[j]) + ")"); + else + finalList.append(compList1[j].stripWhiteSpace()); + } + //store text so that we can restore it if it gets erased after GS returns no results + temp = m_searchCombo->currentText(); + m_searchCombo->listBox()->clear(); + m_searchCombo->listBox()->insertStringList(finalList); + m_searchCombo->setIcon(m_searchIcon); + //restore text + m_searchCombo->lineEdit()->setText(temp); + if (finalList.count() != 0 && !m_gsTimer.isActive()) + { + m_searchCombo->popup(); + } + } + m_gsData = ""; +} + +void SearchBarPlugin::gsSetCompletedText(const QString& text) +{ + QString currentText; + if (m_searchCombo->lineEdit()->hasSelectedText()) + currentText = m_searchCombo->currentText().left(m_searchCombo->lineEdit()->selectionStart()); + else + currentText = m_searchCombo->currentText(); + if (currentText == text.left(currentText.length())) + { + m_searchCombo->lineEdit()->setText(text.left(text.find('(') - 1)); + m_searchCombo->lineEdit()->setCursorPosition(currentText.length()); + m_searchCombo->lineEdit()->setSelection(currentText.length(), m_searchCombo->currentText().length() - currentText.length()); + } +} + +void SearchBarPlugin::gsPutTextInBox(const QString& text) +{ + m_searchCombo->lineEdit()->setText(text.section('(', 0, 0).stripWhiteSpace()); +} + #include "searchbar.moc" diff --git a/konq-plugins/searchbar/searchbar.h b/konq-plugins/searchbar/searchbar.h index 3f03eb9..25e7ded 100644 --- a/konq-plugins/searchbar/searchbar.h +++ b/konq-plugins/searchbar/searchbar.h @@ -1,4 +1,6 @@ /* This file is part of the KDE project + Copyright (C) 2005 by Tobi Vollebregt + Copyright (C) 2004 by Vinay Khaitan Copyright (C) 2004 Arend van Beelen jr. This program is free software; you can redistribute it and/or @@ -32,6 +34,7 @@ class KHTMLPart; class KProcess; class QPopupMenu; +class QTimer; /** * Combo box which catches mouse clicks on the pixmap. @@ -149,6 +152,22 @@ class SearchBarPlugin : public KParts::Plugin void updateComboVisibility(); void focusSearchbar(); + + // Google Suggest private slots + void selectGoogleSuggestMode(); + void gsStartDelay(); + void gsMakeCompletionList(); + void gsDataArrived(KIO::Job*, const QByteArray& data); + void gsJobFinished(KIO::Job* job); + void gsSetCompletedText(const QString& text); + void gsPutTextInBox(const QString& text); + + signals: + + // Google Suggest signals + + void gsCompleteDelayed(); + private: void nextSearchEntry(); void previousSearchEntry(); @@ -157,12 +176,20 @@ class SearchBarPlugin : public KParts::Plugin SearchBarCombo *m_searchCombo; KWidgetAction *m_searchComboAction; QPopupMenu *m_popupMenu; + KSelectAction *m_googleMenu; QPixmap m_searchIcon; SearchModes m_searchMode; QString m_providerName; bool m_urlEnterLock; QString m_currentEngine; QStringList m_searchEngines; + + // Google Suggest private members + + QTimer m_gsTimer; + QString m_gsData; + enum GoogleMode {GoogleOnly,ForAll,Never}; + GoogleMode m_googleMode; }; #endif // SEARCHBAR_PLUGIN diff --git a/konq-plugins/webarchiver/plugin_webarchiver.cpp b/konq-plugins/webarchiver/plugin_webarchiver.cpp index fca9bc7..81dc9ba 100644 --- a/konq-plugins/webarchiver/plugin_webarchiver.cpp +++ b/konq-plugins/webarchiver/plugin_webarchiver.cpp @@ -78,12 +78,12 @@ void PluginWebArchiver::slotSaveToArchive() // Replace space with underscore, proposed Frank Pieczynski - archiveName = archiveName.simplifyWhiteSpace(); archiveName.replace( "\\s:", " "); + archiveName = archiveName.simplifyWhiteSpace(); archiveName.replace( "?", ""); archiveName.replace( ":", ""); archiveName.replace( "/", ""); - archiveName = archiveName.replace( QRegExp("\\s+"), " "); + archiveName = archiveName.replace( QRegExp("\\s+"), "_"); archiveName = KGlobalSettings::documentPath() + "/" + archiveName + ".war" ; -- cgit v1.2.3