├── Thawab ├── __init__.py ├── whooshSymbolicQParser.py ├── tags.py ├── platform.py ├── wiki.py ├── stemming.py ├── asyncIndex.py ├── userDb.py ├── meta.py ├── baseSearchEngine.py └── whooshSearchEngine.py ├── readme ├── po ├── POTFILES.am ├── Makefile ├── ar.po └── de.po ├── waqf2-ar.pdf ├── thawab-gtk ├── thawab-data └── themes │ ├── neo │ ├── static │ │ ├── print.css │ │ ├── img │ │ │ ├── up.gif │ │ │ ├── about.gif │ │ │ ├── book.png │ │ │ ├── close.png │ │ │ ├── code.gif │ │ │ ├── forum.gif │ │ │ ├── go-up.png │ │ │ ├── home.png │ │ │ ├── logo.png │ │ │ ├── mail.gif │ │ │ ├── print.png │ │ │ ├── quote.gif │ │ │ ├── rpm.gif │ │ │ ├── text.png │ │ │ ├── tree.png │ │ │ ├── favicon.ico │ │ │ ├── get_ffx.png │ │ │ ├── go-back.png │ │ │ ├── go-next.png │ │ │ ├── loading.gif │ │ │ ├── search.png │ │ │ ├── spacer.png │ │ │ ├── external.gif │ │ │ ├── scrolldn.png │ │ │ ├── scrollup.png │ │ │ ├── valid-css-blue.gif │ │ │ └── valid-xhtml10-blue.gif │ │ ├── manual │ │ │ ├── images │ │ │ │ ├── note.png │ │ │ │ ├── tip.png │ │ │ │ ├── warning.png │ │ │ │ └── important.png │ │ │ ├── all.css │ │ │ └── manual.html │ │ ├── ie-fx.css │ │ ├── fx.css │ │ ├── th-main.js │ │ ├── th-view.js │ │ ├── main.js │ │ └── main.css │ └── templates │ │ ├── footer.html │ │ ├── minisearch.html │ │ ├── layout.html │ │ ├── view.html │ │ └── main.html │ └── default │ ├── static │ ├── print.css │ ├── img │ │ ├── rpm.gif │ │ ├── up.gif │ │ ├── about.gif │ │ ├── bg-d.png │ │ ├── bg-l.png │ │ ├── close.gif │ │ ├── code.gif │ │ ├── forum.gif │ │ ├── go-up.gif │ │ ├── kutub.gif │ │ ├── mail.gif │ │ ├── print.gif │ │ ├── quote.gif │ │ ├── search.gif │ │ ├── tree.png │ │ ├── external.gif │ │ ├── favicon.ico │ │ ├── get_ffx.png │ │ ├── go-home.gif │ │ ├── go-next.gif │ │ ├── go-prev.gif │ │ ├── loading.gif │ │ ├── scrolldn.png │ │ ├── scrollup.png │ │ ├── valid-css-blue.gif │ │ └── valid-xhtml10-blue.gif │ ├── manual │ │ ├── images │ │ │ ├── tip.png │ │ │ ├── note.png │ │ │ ├── warning.png │ │ │ └── important.png │ │ └── all.css │ ├── ie-fx.css │ ├── fx.css │ ├── th-view.js │ ├── th-main.js │ ├── main.js │ └── main.css │ └── templates │ ├── footer.html │ ├── minisearch.html │ ├── view.html │ ├── layout.html │ └── main.html ├── TODO ├── thawab.desktop.in ├── wiki2th.py ├── thApiTest.py ├── webAppTest.py ├── setup.py ├── update-manual-from-site.sh ├── th-set-meta.py ├── Makefile ├── bok2ki.py ├── thawab.spec └── thawab-server /Thawab/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /readme: -------------------------------------------------------------------------------- 1 | إقرأني أولا 2 | Read me first 3 | -------------------------------------------------------------------------------- /po/POTFILES.am: -------------------------------------------------------------------------------- 1 | thawab.desktop.in 2 | Thawab/*.py 3 | 4 | -------------------------------------------------------------------------------- /waqf2-ar.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ojuba-org/thawab/HEAD/waqf2-ar.pdf -------------------------------------------------------------------------------- /thawab-gtk: -------------------------------------------------------------------------------- 1 | #! /usr/bin/python3 2 | # -*- coding: UTF-8 -*- 3 | from Thawab.gtkUi import main 4 | main() 5 | -------------------------------------------------------------------------------- /thawab-data/themes/neo/static/print.css: -------------------------------------------------------------------------------- 1 | .noprint { display: none; } 2 | #wrapper, #container { background:#fff; } 3 | 4 | -------------------------------------------------------------------------------- /thawab-data/themes/default/static/print.css: -------------------------------------------------------------------------------- 1 | .noprint { display: none; } 2 | #wrapper, #container { background:#fff; } 3 | 4 | -------------------------------------------------------------------------------- /thawab-data/themes/neo/static/img/up.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ojuba-org/thawab/HEAD/thawab-data/themes/neo/static/img/up.gif -------------------------------------------------------------------------------- /thawab-data/themes/neo/static/img/about.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ojuba-org/thawab/HEAD/thawab-data/themes/neo/static/img/about.gif -------------------------------------------------------------------------------- /thawab-data/themes/neo/static/img/book.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ojuba-org/thawab/HEAD/thawab-data/themes/neo/static/img/book.png -------------------------------------------------------------------------------- /thawab-data/themes/neo/static/img/close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ojuba-org/thawab/HEAD/thawab-data/themes/neo/static/img/close.png -------------------------------------------------------------------------------- /thawab-data/themes/neo/static/img/code.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ojuba-org/thawab/HEAD/thawab-data/themes/neo/static/img/code.gif -------------------------------------------------------------------------------- /thawab-data/themes/neo/static/img/forum.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ojuba-org/thawab/HEAD/thawab-data/themes/neo/static/img/forum.gif -------------------------------------------------------------------------------- /thawab-data/themes/neo/static/img/go-up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ojuba-org/thawab/HEAD/thawab-data/themes/neo/static/img/go-up.png -------------------------------------------------------------------------------- /thawab-data/themes/neo/static/img/home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ojuba-org/thawab/HEAD/thawab-data/themes/neo/static/img/home.png -------------------------------------------------------------------------------- /thawab-data/themes/neo/static/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ojuba-org/thawab/HEAD/thawab-data/themes/neo/static/img/logo.png -------------------------------------------------------------------------------- /thawab-data/themes/neo/static/img/mail.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ojuba-org/thawab/HEAD/thawab-data/themes/neo/static/img/mail.gif -------------------------------------------------------------------------------- /thawab-data/themes/neo/static/img/print.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ojuba-org/thawab/HEAD/thawab-data/themes/neo/static/img/print.png -------------------------------------------------------------------------------- /thawab-data/themes/neo/static/img/quote.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ojuba-org/thawab/HEAD/thawab-data/themes/neo/static/img/quote.gif -------------------------------------------------------------------------------- /thawab-data/themes/neo/static/img/rpm.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ojuba-org/thawab/HEAD/thawab-data/themes/neo/static/img/rpm.gif -------------------------------------------------------------------------------- /thawab-data/themes/neo/static/img/text.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ojuba-org/thawab/HEAD/thawab-data/themes/neo/static/img/text.png -------------------------------------------------------------------------------- /thawab-data/themes/neo/static/img/tree.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ojuba-org/thawab/HEAD/thawab-data/themes/neo/static/img/tree.png -------------------------------------------------------------------------------- /thawab-data/themes/default/static/img/rpm.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ojuba-org/thawab/HEAD/thawab-data/themes/default/static/img/rpm.gif -------------------------------------------------------------------------------- /thawab-data/themes/default/static/img/up.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ojuba-org/thawab/HEAD/thawab-data/themes/default/static/img/up.gif -------------------------------------------------------------------------------- /thawab-data/themes/neo/static/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ojuba-org/thawab/HEAD/thawab-data/themes/neo/static/img/favicon.ico -------------------------------------------------------------------------------- /thawab-data/themes/neo/static/img/get_ffx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ojuba-org/thawab/HEAD/thawab-data/themes/neo/static/img/get_ffx.png -------------------------------------------------------------------------------- /thawab-data/themes/neo/static/img/go-back.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ojuba-org/thawab/HEAD/thawab-data/themes/neo/static/img/go-back.png -------------------------------------------------------------------------------- /thawab-data/themes/neo/static/img/go-next.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ojuba-org/thawab/HEAD/thawab-data/themes/neo/static/img/go-next.png -------------------------------------------------------------------------------- /thawab-data/themes/neo/static/img/loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ojuba-org/thawab/HEAD/thawab-data/themes/neo/static/img/loading.gif -------------------------------------------------------------------------------- /thawab-data/themes/neo/static/img/search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ojuba-org/thawab/HEAD/thawab-data/themes/neo/static/img/search.png -------------------------------------------------------------------------------- /thawab-data/themes/neo/static/img/spacer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ojuba-org/thawab/HEAD/thawab-data/themes/neo/static/img/spacer.png -------------------------------------------------------------------------------- /thawab-data/themes/default/static/img/about.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ojuba-org/thawab/HEAD/thawab-data/themes/default/static/img/about.gif -------------------------------------------------------------------------------- /thawab-data/themes/default/static/img/bg-d.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ojuba-org/thawab/HEAD/thawab-data/themes/default/static/img/bg-d.png -------------------------------------------------------------------------------- /thawab-data/themes/default/static/img/bg-l.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ojuba-org/thawab/HEAD/thawab-data/themes/default/static/img/bg-l.png -------------------------------------------------------------------------------- /thawab-data/themes/default/static/img/close.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ojuba-org/thawab/HEAD/thawab-data/themes/default/static/img/close.gif -------------------------------------------------------------------------------- /thawab-data/themes/default/static/img/code.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ojuba-org/thawab/HEAD/thawab-data/themes/default/static/img/code.gif -------------------------------------------------------------------------------- /thawab-data/themes/default/static/img/forum.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ojuba-org/thawab/HEAD/thawab-data/themes/default/static/img/forum.gif -------------------------------------------------------------------------------- /thawab-data/themes/default/static/img/go-up.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ojuba-org/thawab/HEAD/thawab-data/themes/default/static/img/go-up.gif -------------------------------------------------------------------------------- /thawab-data/themes/default/static/img/kutub.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ojuba-org/thawab/HEAD/thawab-data/themes/default/static/img/kutub.gif -------------------------------------------------------------------------------- /thawab-data/themes/default/static/img/mail.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ojuba-org/thawab/HEAD/thawab-data/themes/default/static/img/mail.gif -------------------------------------------------------------------------------- /thawab-data/themes/default/static/img/print.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ojuba-org/thawab/HEAD/thawab-data/themes/default/static/img/print.gif -------------------------------------------------------------------------------- /thawab-data/themes/default/static/img/quote.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ojuba-org/thawab/HEAD/thawab-data/themes/default/static/img/quote.gif -------------------------------------------------------------------------------- /thawab-data/themes/default/static/img/search.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ojuba-org/thawab/HEAD/thawab-data/themes/default/static/img/search.gif -------------------------------------------------------------------------------- /thawab-data/themes/default/static/img/tree.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ojuba-org/thawab/HEAD/thawab-data/themes/default/static/img/tree.png -------------------------------------------------------------------------------- /thawab-data/themes/neo/static/img/external.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ojuba-org/thawab/HEAD/thawab-data/themes/neo/static/img/external.gif -------------------------------------------------------------------------------- /thawab-data/themes/neo/static/img/scrolldn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ojuba-org/thawab/HEAD/thawab-data/themes/neo/static/img/scrolldn.png -------------------------------------------------------------------------------- /thawab-data/themes/neo/static/img/scrollup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ojuba-org/thawab/HEAD/thawab-data/themes/neo/static/img/scrollup.png -------------------------------------------------------------------------------- /thawab-data/themes/default/static/img/external.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ojuba-org/thawab/HEAD/thawab-data/themes/default/static/img/external.gif -------------------------------------------------------------------------------- /thawab-data/themes/default/static/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ojuba-org/thawab/HEAD/thawab-data/themes/default/static/img/favicon.ico -------------------------------------------------------------------------------- /thawab-data/themes/default/static/img/get_ffx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ojuba-org/thawab/HEAD/thawab-data/themes/default/static/img/get_ffx.png -------------------------------------------------------------------------------- /thawab-data/themes/default/static/img/go-home.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ojuba-org/thawab/HEAD/thawab-data/themes/default/static/img/go-home.gif -------------------------------------------------------------------------------- /thawab-data/themes/default/static/img/go-next.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ojuba-org/thawab/HEAD/thawab-data/themes/default/static/img/go-next.gif -------------------------------------------------------------------------------- /thawab-data/themes/default/static/img/go-prev.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ojuba-org/thawab/HEAD/thawab-data/themes/default/static/img/go-prev.gif -------------------------------------------------------------------------------- /thawab-data/themes/default/static/img/loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ojuba-org/thawab/HEAD/thawab-data/themes/default/static/img/loading.gif -------------------------------------------------------------------------------- /thawab-data/themes/default/static/img/scrolldn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ojuba-org/thawab/HEAD/thawab-data/themes/default/static/img/scrolldn.png -------------------------------------------------------------------------------- /thawab-data/themes/default/static/img/scrollup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ojuba-org/thawab/HEAD/thawab-data/themes/default/static/img/scrollup.png -------------------------------------------------------------------------------- /thawab-data/themes/neo/static/img/valid-css-blue.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ojuba-org/thawab/HEAD/thawab-data/themes/neo/static/img/valid-css-blue.gif -------------------------------------------------------------------------------- /thawab-data/themes/neo/static/manual/images/note.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ojuba-org/thawab/HEAD/thawab-data/themes/neo/static/manual/images/note.png -------------------------------------------------------------------------------- /thawab-data/themes/neo/static/manual/images/tip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ojuba-org/thawab/HEAD/thawab-data/themes/neo/static/manual/images/tip.png -------------------------------------------------------------------------------- /thawab-data/themes/default/static/manual/images/tip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ojuba-org/thawab/HEAD/thawab-data/themes/default/static/manual/images/tip.png -------------------------------------------------------------------------------- /thawab-data/themes/neo/static/manual/images/warning.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ojuba-org/thawab/HEAD/thawab-data/themes/neo/static/manual/images/warning.png -------------------------------------------------------------------------------- /thawab-data/themes/default/static/img/valid-css-blue.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ojuba-org/thawab/HEAD/thawab-data/themes/default/static/img/valid-css-blue.gif -------------------------------------------------------------------------------- /thawab-data/themes/default/static/manual/images/note.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ojuba-org/thawab/HEAD/thawab-data/themes/default/static/manual/images/note.png -------------------------------------------------------------------------------- /thawab-data/themes/neo/static/img/valid-xhtml10-blue.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ojuba-org/thawab/HEAD/thawab-data/themes/neo/static/img/valid-xhtml10-blue.gif -------------------------------------------------------------------------------- /thawab-data/themes/neo/static/manual/images/important.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ojuba-org/thawab/HEAD/thawab-data/themes/neo/static/manual/images/important.png -------------------------------------------------------------------------------- /thawab-data/themes/default/static/img/valid-xhtml10-blue.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ojuba-org/thawab/HEAD/thawab-data/themes/default/static/img/valid-xhtml10-blue.gif -------------------------------------------------------------------------------- /thawab-data/themes/default/static/manual/images/warning.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ojuba-org/thawab/HEAD/thawab-data/themes/default/static/manual/images/warning.png -------------------------------------------------------------------------------- /thawab-data/themes/default/static/manual/images/important.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ojuba-org/thawab/HEAD/thawab-data/themes/default/static/manual/images/important.png -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | بسم الله الرحمن الرحيم 2 | - port to python 3 3 | - fix indixing after clear cache 4 | - fix indexing repete jobs in current session ( reload search cache ) 5 | - fix tabs title 6 | - fix FIXME lines 7 | 8 | -------------------------------------------------------------------------------- /thawab.desktop.in: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Type=Application 3 | _Name=Thawab 4 | _GenericName=Electronic Arabic/Islamic Encyclopedia 5 | Terminal=false 6 | TryExec=thawab-gtk 7 | Exec=thawab-gtk 8 | Icon=thawab 9 | Categories=X-Islamic-Software;GTK; 10 | StartupNotify=false 11 | -------------------------------------------------------------------------------- /thawab-data/themes/neo/static/ie-fx.css: -------------------------------------------------------------------------------- 1 | /* fx-ie */ 2 | #overlay { 3 | filter:alpha(opacity=70); 4 | } 5 | .showOnFocus { 6 | filter:alpha(opacity=50); 7 | } 8 | .showOnFocus:hover { 9 | filter:alpha(opacity=100); 10 | } 11 | .blurOnFocus{ 12 | filter:alpha(opacity=100); 13 | } 14 | .showOnFocus:hover { 15 | filter:alpha(opacity=40); 16 | } 17 | #absnav3{ display:none; } 18 | -------------------------------------------------------------------------------- /thawab-data/themes/default/static/ie-fx.css: -------------------------------------------------------------------------------- 1 | /* fx-ie */ 2 | #overlay { 3 | filter:alpha(opacity=70); 4 | } 5 | .showOnFocus { 6 | filter:alpha(opacity=50); 7 | } 8 | .showOnFocus:hover { 9 | filter:alpha(opacity=100); 10 | } 11 | .blurOnFocus{ 12 | filter:alpha(opacity=100); 13 | } 14 | .showOnFocus:hover { 15 | filter:alpha(opacity=40); 16 | } 17 | #absnav3{ display:none; } 18 | -------------------------------------------------------------------------------- /Thawab/whooshSymbolicQParser.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | """ 3 | 4 | Copyright © 2010, Muayyad Alsadi 5 | 6 | """ 7 | 8 | import sys, os, os.path, re 9 | 10 | from whoosh import query 11 | from whoosh.qparser import * 12 | 13 | def MultifieldSQParser(fieldnames, schema = None, fieldboosts=None, **kwargs): 14 | p = MultifieldParser(fieldnames, schema, fieldboosts, **kwargs) 15 | cp = OperatorsPlugin(And = r"&", Or = r"\|", AndNot = r"&!", AndMaybe = r"&~", Not = r'!') 16 | p.replace_plugin(cp) 17 | return p 18 | 19 | -------------------------------------------------------------------------------- /wiki2th.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/python3 2 | # -*- coding: UTF-8 -*- 3 | """ 4 | The is wiki importing tool for testing and demonestration of thawab 5 | Copyright © 2008, Muayyad Alsadi 6 | 7 | Released under terms of Waqf Public License. 8 | This program is free software; you can redistribute it and/or modify 9 | it under the terms of the latest version Waqf Public License as 10 | published by Ojuba.org. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 15 | 16 | The Latest version of the license can be found on 17 | "http://waqf.ojuba.org/license" 18 | 19 | """ 20 | 21 | import sys, os, os.path 22 | from Thawab.wiki import wiki2th 23 | for f in sys.argv[1:]: 24 | wiki2th(f,'.') 25 | -------------------------------------------------------------------------------- /thawab-data/themes/neo/templates/footer.html: -------------------------------------------------------------------------------- 1 | %if _r.rq.webapp._typ=='web': 2 | 19 | %end 20 | -------------------------------------------------------------------------------- /thApiTest.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/python3 2 | # -*- coding: UTF-8 -*- 3 | import os, os.path, Thawab.core 4 | th=Thawab.core.ThawabMan() 5 | th.searchEngine.reindexAll() 6 | # th.loadMeta() # to detect new files and add them ..etc. 7 | meta=th.getMeta() 8 | print(meta.getUriList()) 9 | th.searchEngine.reindexKitab('/home/alsadi/.thawab/db/uthaymine.ki') 10 | 11 | ## export to xml 12 | #from cStringIO import StringIO 13 | #s=StringIO() 14 | #ki=Thawab.core.Kitab('/home/alsadi/.thawab/tmp/THAWAB_xqkca0.ki3001') 15 | #n=ki.root.toXml(s) 16 | #print s.getvalue() 17 | 18 | ## export to HTML or wiki 19 | #import Thawab.core 20 | #ki=Thawab.core.Kitab('/home/alsadi/.thawab/tmp/THAWAB_xqkca0.ki3001') 21 | #s=ki.root.toHTML() 22 | #print s 23 | 24 | ##searching the index 25 | #for i in th.searchEngine.queryIndex('إنشاء'.decode('utf-8')): print i['title'] 26 | #for i in th.searchEngine.queryIndex('إنشاء kitab:pyqt4'.decode('utf-8')): print i['title'] 27 | #for i in th.searchEngine.queryIndex('إنشاء kitab:test'.decode('utf-8')): print i['title'] 28 | -------------------------------------------------------------------------------- /webAppTest.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/python3 2 | # -*- coding: UTF-8 -*- 3 | import sys, os, os.path, logging 4 | import Thawab.core 5 | from Thawab.webApp import webApp, get_theme_dirs 6 | 7 | prefix=os.path.dirname(sys.argv[0]) 8 | th=Thawab.core.ThawabMan() 9 | 10 | myLogger=logging.getLogger('ThawabWebAppTest') 11 | h=logging.StreamHandler() # in production use WatchedFileHandler or RotatingFileHandler 12 | h.setFormatter(logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")) 13 | myLogger.addHandler(h) 14 | myLogger.setLevel(logging.INFO) 15 | 16 | from paste import httpserver 17 | 18 | lookup=[os.path.join(prefix,'thawab-themes')] 19 | lookup.extend([os.path.join(i, 'themes') for i in th.prefixes]) 20 | app=webApp( 21 | th,'web', 22 | lookup, th.conf.get('theme','default'), '/_theme/', 23 | logger=myLogger 24 | ); 25 | # for options see http://pythonpaste.org/modules/httpserver.html 26 | httpserver.serve(app, host='0.0.0.0', port='8080') # to serve publically 27 | #httpserver.serve(app, host='127.0.0.1', port='8080') # to serve localhost 28 | 29 | -------------------------------------------------------------------------------- /thawab-data/themes/default/templates/footer.html: -------------------------------------------------------------------------------- 1 | 25 | 26 | -------------------------------------------------------------------------------- /po/Makefile: -------------------------------------------------------------------------------- 1 | APPNAME := thawab 2 | POFILES := $(wildcard *.po) 3 | MOFILES := $(patsubst %.po,%.mo,$(POFILES)) 4 | CRE_POTFILESin := for i in $(shell cat POTFILES.am ); do echo ../$${i} | sed 's/\s/\n/g; s/\.\.\///g' ; done > POTFILES.in 5 | 6 | CAT := cat 7 | ECHO := echo 8 | MKDIR := mkdir 9 | MSGFMT := msgfmt 10 | INTLTOOL_UPDATE := intltool-update 11 | RM := $(shell which rm | egrep '/' | sed 's/\s*//g') 12 | MV := $(shell which mv | egrep '/' | sed 's/\s*//g') 13 | 14 | all: $(APPNAME).pot $(MOFILES) 15 | 16 | $(APPNAME).pot: 17 | @$(CRE_POTFILESin) 18 | @$(ECHO) "*** Building $(APPNAME).pot: $(SOURCES)" 19 | @$(CAT) POTFILES.in 20 | @$(INTLTOOL_UPDATE) -g $(APPNAME) -p 21 | 22 | %.mo: %.po 23 | @$(ECHO) "- Merging translations into $*.mo" 24 | @$(MSGFMT) $*.po -o $*.mo 25 | @$(MKDIR) -p ../locale/$*/LC_MESSAGES/ || : 26 | @$(ECHO) "- Moving: $*.mo -> ../locale/$*/LC_MESSAGES/$(APPNAME).mo" 27 | @$(MV) $*.mo ../locale/$*/LC_MESSAGES/$(APPNAME).mo 28 | @$(RM) -f *.tmp 29 | 30 | %.po: $(APPNAME).pot 31 | @$(ECHO) "- Updating: $*.po" 32 | @$(INTLTOOL_UPDATE) -g $(APPNAME) -d $* 33 | 34 | clean: 35 | @$(ECHO) "*** Cleaning pos..." 36 | @$(ECHO) "- Removing: $(APPNAME).pot" 37 | @$(RM) -f $(APPNAME).pot 38 | @$(ECHO) "- Removing: *.tmp" 39 | @$(RM) -f *.tmp 40 | @$(ECHO) "- Removing: *.mo" 41 | @$(RM) -f *.mo 42 | @$(ECHO) "- Removing: POTFILES.in" 43 | @$(RM) -f POTFILES.in 44 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/python3 2 | import sys, os, os.path 3 | from distutils.core import setup 4 | from glob import glob 5 | 6 | # to install type: 7 | # python setup.py install --root=/ 8 | 9 | def no_empty(l): 10 | return [i_j for i_j in l if i_j[1]] 11 | 12 | def recusive_data_dir(to, src, l=None): 13 | D=glob(os.path.join(src,'*')) 14 | files=[i for i in D if os.path.isfile(i)] 15 | dirs=[i for i in D if os.path.isdir(i)] 16 | if l==None: l=[] 17 | l.append( (to , files ) ) 18 | for d in dirs: recusive_data_dir( os.path.join(to,os.path.basename(d)), d , l) 19 | return l 20 | 21 | locales=[('share/'+i,[''+i+'/thawab.mo',]) for i in glob('locale/*/LC_MESSAGES')] 22 | data_files=no_empty(recusive_data_dir('share/thawab/', 'thawab-data')) 23 | data_files.extend(locales) 24 | setup (name='thawab', version='3.0.10', 25 | description='Thawab Arabic/Islamic encyclopedia system', 26 | author='Muayyad Saleh Alsadi', 27 | author_email='alsadi@ojuba.org', 28 | url='http://thawab.ojuba.org/', 29 | license='Waqf', 30 | packages=['Thawab'], 31 | scripts=['thawab-gtk','thawab-server'], 32 | classifiers=[ 33 | 'Development Status :: 4 - Beta', 34 | 'Intended Audience :: End Users/Desktop', 35 | 'Operating System :: POSIX', 36 | 'Programming Language :: Python', 37 | ], 38 | data_files=data_files 39 | ) 40 | 41 | 42 | -------------------------------------------------------------------------------- /update-manual-from-site.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | exit 0 3 | pushd thawab-data/themes/default/static/manual || { 4 | echo "can't change dir" 5 | exit 1 6 | } 7 | baseurl="http://www.ojuba.org/wiki/_export/xhtml/thawab/" 8 | for i in manual 9 | do 10 | fn="${i:-index}.html" 11 | i="${i:-الصفحة_الأولى}" 12 | echo "getting $fn from ${baseurl}${i}" 13 | rm "$fn" 2>/dev/null || : 14 | curl -L -o "$fn" "${baseurl}${i}" 15 | 16 | perl -i -lwne 'BEGIN{$echo=1;} 17 | s:href="/wiki/thawab/([^"]+)":href="${1}.html":g; 18 | s:src="/wiki/_media/thawab/([^?"]+)(\?[^"]*)?":src="images/$1":g; 19 | s:href="/wiki/_detail/thawab/([^?"]+)(\?[^"]*)?":href="images/$1":g; 20 | s!a href="http://!a target="_blank" href="http://!g; 21 | if(/\]*\>/){$echo=0;} 22 | if(/#discussion__section|\<(link|meta|script)[^>]*\>/){next;}if (/class="tags"/) {$echo=0;} 23 | if($echo){print $_;}if (/\<\/div\>/) {$echo=1;} 24 | if(/\<\/head\>/) { 25 | print ""; 26 | print ""; 27 | print "دليل استخدام ثواب"; 28 | print ""; 29 | print ""; 30 | print ""; 31 | print ""; 32 | $echo=1; 33 | } 34 | ' "$fn" 35 | 36 | done 37 | popd 38 | 39 | -------------------------------------------------------------------------------- /Thawab/tags.py: -------------------------------------------------------------------------------- 1 | # the following flags are Or-ed in a node-wide (not tag-wide) 2 | TAG_FLAGS_EXTERNAL_SOURCE = 1 # some external source pointed by param 3 | TAG_FLAGS_BYBOT = 2 # the content and descendant nodes are generated by a bot, CHANGES WILL BE LOST 4 | TAG_FLAGS_HEADER = 4 # index content in a separated document then consume content 5 | TAG_FLAGS_IX_TAG = 8 # add this tag name into index tags list, if it has a param append it to the tag name 6 | TAG_FLAGS_IX_FIELD = 16 # index content (again) in a separated document without consuming content 7 | TAG_FLAGS_IX_SKIP = 32 # don't index content 8 | TAG_FLAGS_PAD_CONTENT = 64 # append a space/LF after content 9 | TAG_FLAGS_FLOW_BLOCK = 128 # in a separated block eg.
10 | TAG_FLAGS_FLOW_FLOAT = 256 # marked text does not flow normally, but float in a box 11 | TAG_FLAGS_FLOW_FOOTER = 512 # marked text does not flow normally, but accumelated in the tail 12 | TAG_FLAGS_FLOW_HIDDEN = 1024 # marked text does not appear in usual cases 13 | 14 | 15 | # NOTE: validaty of data: a node of type TAG_FLAGS_HEADER can't be a child of TAG_FLAGS_IX_FIELD 16 | # NOTE: validaty of data: both TAG_FLAGS_IX_SKIP and TAG_FLAGS_IX_FIELD should not be applied to the same node 17 | # NOTE: validaty of data: both TAG_FLAGS_HEADER and TAG_FLAGS_IX_FIELD will case redudancy if applied to same node (as things will be indexed twice without any befinit) 18 | -------------------------------------------------------------------------------- /thawab-data/themes/default/templates/minisearch.html: -------------------------------------------------------------------------------- 1 | 2 |
3 |
4 | فضلا أضف هذا الكتاب للفهرس. 5 |
6 |
7 |
8 | 9 |
10 |
11 | 12 | 13 |
-
-
14 |
15 |
16 | 17 |
18 |
19 | 20 |
21 | 22 |
23 |
24 | 25 | 26 | -------------------------------------------------------------------------------- /thawab-data/themes/neo/templates/minisearch.html: -------------------------------------------------------------------------------- 1 | 2 |
3 |
4 | فضلا أضف هذا الكتاب للفهرس. 5 |
6 |
7 |
8 | 9 |
10 |
11 | 12 | 13 |
-
-
14 |
15 |
16 | 17 |
18 |
19 | 20 |
21 | 22 |
23 |
24 | 25 | 26 | -------------------------------------------------------------------------------- /thawab-data/themes/neo/templates/layout.html: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | {{_r.title or '::'}} :: ثواب :: 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | {{!_r.render_css_links()}} 15 | %if x_js_script: x_js_script() 16 | 17 | {{!_r.render_js_links('head')}} 18 | 21 | 22 | 23 | 24 | {{!_r.render_js_links('begin')}} 25 | 26 | 27 | %topnav() 28 | 29 | %if typ!="main": 30 | %include minisearch 31 | %end 32 | 33 |
34 | 35 | %include 36 | 37 |
38 | 39 | %include footer 40 | 41 | {{!_r.render_js_links('end')}} 42 | 43 | 44 | -------------------------------------------------------------------------------- /thawab-data/themes/default/templates/view.html: -------------------------------------------------------------------------------- 1 | %_r.title=title 2 | %_r.add_js_link('th-main.js') 3 | %_r.add_js_link('th-view.js') 4 | %def x_js_script_f(): 5 | 14 | %end 15 | 16 | %def topnav_f(): 17 | prev 18 | up 19 | home 20 | next 21 | 22 | %end 23 | 24 | 25 |
26 |

مكتبة ثواب - {{title}}

27 | 28 |
29 |
30 | 31 |
{{!content}}
32 |
{{!childrenLinks}}
33 | 34 |
35 | 36 | %rebase layout typ="view", topnav=topnav_f, x_js_script=x_js_script_f 37 | 38 | -------------------------------------------------------------------------------- /thawab-data/themes/neo/templates/view.html: -------------------------------------------------------------------------------- 1 | %_r.title=title 2 | %_r.add_js_link('th-main.js') 3 | %_r.add_js_link('th-view.js') 4 | %def x_js_script_f(): 5 | 14 | %end 15 | %def topnav_f(): 16 | 17 | %end 18 | 19 | 20 | 21 |
22 | 23 | السابق 24 | لأعلى 25 | التالي 26 | 27 |
28 |
29 |
30 | عودة للصفحة الرئيسية{{title}} 31 | 32 |
33 |
34 | 35 | 36 |
37 |
38 |
{{!content}}
39 |
{{!childrenLinks}}
40 | 41 |
42 | 43 | %rebase layout typ="view", topnav=topnav_f, x_js_script=x_js_script_f 44 | 45 | -------------------------------------------------------------------------------- /thawab-data/themes/default/static/fx.css: -------------------------------------------------------------------------------- 1 | /* fx */ 2 | #async_tips_div { 3 | background-color:rgba(255,255,200,0.9); 4 | } 5 | 6 | #overlay { 7 | opacity:0.7; 8 | } 9 | .showOnFocus { 10 | opacity:0.4; 11 | } 12 | .showOnFocus:active, .showOnFocus:focus { 13 | opacity:0.75; 14 | } 15 | .showOnFocus:hover { 16 | opacity:1; 17 | } 18 | 19 | .blurOnFocus { 20 | opacity:1.0; 21 | } 22 | .blurOnFocus:active, .showOnFocus:focus { 23 | opacity:0.75; 24 | } 25 | .blurOnFocus:hover { 26 | opacity:0.4; 27 | } 28 | 29 | #minisearch input { 30 | background:rgba(255,255,255,0.6); 31 | } 32 | #absnav { 33 | -webkit-border-radius:20px; 34 | -moz-border-radius:20px; 35 | border-radius:20px; 36 | padding:0 20px; 37 | 38 | -webkit-box-shadow:0 0 8px rgba(240,240,240,0.4); 39 | -moz-box-shadow:0 0 8px 8px rgba(240,240,240,0.4); 40 | } 41 | #absnav2, #absnav3{ 42 | -webkit-border-radius:20px; 43 | -moz-border-radius:20px; 44 | border-radius:20px; 45 | padding:0 20px; 46 | 47 | -webkit-box-shadow:0 0 8px rgba(240,240,240,0.4); 48 | -moz-box-shadow:0 0 8px 8px rgba(240,240,240,0.4); 49 | } 50 | #absnav3{ 51 | padding:0 5px; 52 | } 53 | 54 | #minisearch { 55 | -webkit-border-bottom-left-radius:8px; 56 | -moz-border-radius-bottomleft:8px; 57 | border-bottom-left-radius:8px; 58 | -webkit-border-bottom-right-radius:32px; 59 | -moz-border-radius-bottomright:32px; 60 | border-bottom-right-radius:32px; 61 | padding:0 8px 2px 0; 62 | } 63 | #container{ 64 | -webkit-border-radius:10px; 65 | -moz-border-radius:10px; 66 | border-radius:10px; 67 | 68 | /* -webkit-box-shadow:0 0 8px rgba(128,128,128,0.4); */ 69 | -moz-box-shadow:0 0 8px 4px rgba(128,128,128,0.4); 70 | } 71 | #absnav3 { position: fixed; } 72 | 73 | -------------------------------------------------------------------------------- /thawab-data/themes/neo/static/fx.css: -------------------------------------------------------------------------------- 1 | /* fx */ 2 | 3 | #async_tips_div { 4 | background-color:rgba(255,255,200,0.9); 5 | } 6 | 7 | #overlay { 8 | opacity:0.7; 9 | } 10 | .showOnFocus { 11 | opacity:0.4; 12 | } 13 | .showOnFocus:active, .showOnFocus:focus { 14 | opacity:0.75; 15 | } 16 | .showOnFocus:hover { 17 | opacity:1; 18 | } 19 | 20 | .blurOnFocus { 21 | opacity:1.0; 22 | } 23 | .blurOnFocus:active, .showOnFocus:focus { 24 | opacity:0.75; 25 | } 26 | .blurOnFocus:hover { 27 | opacity:0.4; 28 | } 29 | 30 | #minisearch input { 31 | background:rgba(255,255,255,0.6); 32 | } 33 | #absnav { 34 | -webkit-border-radius:20px; 35 | -moz-border-radius:20px; 36 | border-radius:20px; 37 | padding:0 20px; 38 | 39 | -webkit-box-shadow:0 0 8px rgba(240,240,240,0.4); 40 | -moz-box-shadow:0 0 8px 8px rgba(240,240,240,0.4); 41 | } 42 | #absnav2, #absnav3{ 43 | -webkit-border-radius:20px; 44 | -moz-border-radius:20px; 45 | border-radius:20px; 46 | padding:0 20px; 47 | 48 | -webkit-box-shadow:0 0 8px rgba(240,240,240,0.4); 49 | -moz-box-shadow:0 0 8px 8px rgba(240,240,240,0.4); 50 | } 51 | #absnav3{ 52 | padding:0 5px; 53 | } 54 | 55 | #minisearch { 56 | -webkit-border-bottom-left-radius:8px; 57 | -moz-border-radius-bottomleft:8px; 58 | border-bottom-left-radius:8px; 59 | -webkit-border-bottom-right-radius:32px; 60 | -moz-border-radius-bottomright:32px; 61 | border-bottom-right-radius:32px; 62 | padding:0 8px 2px 0; 63 | } 64 | #container{ 65 | -webkit-border-radius:10px; 66 | -moz-border-radius:10px; 67 | border-radius:10px; 68 | 69 | /* -webkit-box-shadow:0 0 8px rgba(128,128,128,0.4); */ 70 | -moz-box-shadow:0 0 8px 4px rgba(128,128,128,0.4); 71 | } 72 | #absnav3 { position: fixed; } 73 | 74 | -------------------------------------------------------------------------------- /thawab-data/themes/default/templates/layout.html: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | :: ثواب :: 8 | %if _r.title: 9 | {{_r.title}} :: 10 | %end 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | {{!_r.render_css_links()}} 21 | %if x_js_script: x_js_script() 22 | 23 | {{!_r.render_js_links('head')}} 24 | 27 | 28 | 29 | 30 | {{!_r.render_js_links('begin')}} 31 | 32 | 33 | 34 |
35 | %topnav() 36 |
37 | 38 | 39 | 40 |
41 | /\ 42 | 43 |
44 | 45 | \/ 46 |
47 | 48 | %if typ!="main": 49 | %include minisearch 50 | %end 51 | 52 |
53 |
54 | 55 | %include 56 | 57 |
58 |
59 | 60 | %include footer 61 | 62 | {{!_r.render_js_links('end')}} 63 | 64 | 65 | -------------------------------------------------------------------------------- /th-set-meta.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/python3 2 | # -*- coding: UTF-8 -*- 3 | """ 4 | Setting meta data for thawab files 5 | Copyright © 2010, Muayyad Alsadi 6 | 7 | Released under terms of Waqf Public License. 8 | This program is free software; you can redistribute it and/or modify 9 | it under the terms of the latest version Waqf Public License as 10 | published by Ojuba.org. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 15 | 16 | The Latest version of the license can be found on 17 | "http://waqf.ojuba.org/license" 18 | 19 | """ 20 | import sys, os, os.path 21 | import Thawab.core 22 | 23 | from getopt import getopt, GetoptError 24 | 25 | def usage(): 26 | print('''\ 27 | Usage: %s [plbvRrtayAYBVck] VALUE ... FILES ... 28 | Where: 29 | \t-p VALUE\t set repo name to VALUE 30 | \t-l VALUE\t set language name to VALUE 31 | \t-b VALUE\t set kitab name to VALUE 32 | \t-v VALUE\t set version name to VALUE 33 | \t-R VALUE\t set release major name to VALUE 34 | \t-r VALUE\t set release minor name to VALUE 35 | \t-t VALUE\t set kitab type to VALUE 36 | \t-a VALUE\t set author name to VALUE 37 | \t-y VALUE\t set author death year to VALUE 38 | \t-A VALUE\t set original kitab author name to VALUE 39 | \t-Y VALUE\t set original kitab author death year to VALUE 40 | \t-B VALUE\t set original kitab name to VALUE 41 | \t-V VALUE\t set original kitab version to VALUE 42 | \t-c VALUE\t set classification to VALUE 43 | \t-k VALUE\t set keywords to VALUE 44 | ''' % os.path.basename(sys.argv[0])) 45 | 46 | meta_keys={ 47 | '-p':'repo', '-l':'lang', '-b':'kitab', 48 | '-v':'version', '-R':'releaseMajor', '-r':'releaseMinor', 49 | '-t':'type', '-a':'author', '-y':'year', 50 | '-A':'originalAuthor', '-Y':'originalYear', '-B':'originalKitab', '-V':'originalVersion', 51 | '-c':'classification', '-k':'keywords' 52 | } 53 | metas=set(meta_keys.values()) 54 | try: 55 | opts, args = getopt(sys.argv[1:], "hp:l:b:v:r:R:t:a:y:A:Y:B:V:c:k:", ["help"]) 56 | except GetoptError as err: 57 | print(str(err)) # will print something like "option -a not recognized" 58 | usage() 59 | sys.exit(1) 60 | opts=dict([(meta_keys.get(i,i),j) for i,j in opts]) 61 | if "-h" in opts or "--help" in opts or len(opts)==0 or not args: 62 | usage() 63 | sys.exit(1) 64 | 65 | th=Thawab.core.ThawabMan() 66 | for uri in args: 67 | ki=th.getKitabByUri(uri) 68 | #print ki.meta 69 | for i in opts: 70 | ki.meta[i]=opts[i] 71 | #print ki.meta 72 | ki.setMCache(ki.meta) 73 | 74 | -------------------------------------------------------------------------------- /Thawab/platform.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | """ 3 | Platform specific routines of thawab 4 | Copyright © 2008-2010, Muayyad Alsadi 5 | 6 | Released under terms of Waqf Public License. 7 | This program is free software; you can redistribute it and/or modify 8 | it under the terms of the latest version Waqf Public License as 9 | published by Ojuba.org. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 14 | 15 | The Latest version of the license can be found on 16 | "http://waqf.ojuba.org/license" 17 | 18 | """ 19 | import sys, os, os.path 20 | from glob import glob 21 | 22 | if sys.platform == 'win32': 23 | def uri_to_filename(u): 24 | if len(u) <= 1: 25 | return u 26 | return u[1:].replace('/','\\') 27 | 28 | def get_drives(): 29 | return [j for j in [chr(i)+':\\' for i in range(67,91)] if os.path.exists(j)] 30 | 31 | try: 32 | from winpaths import get_appdata as application_data 33 | except ImportError: 34 | try: 35 | from winshell import application_data 36 | except ImportError: 37 | try: 38 | import win32com.shell as shell 39 | def application_data(): 40 | return shell.SHGetFolderPath(0, 26, 0, 0) 41 | except ImportError: 42 | application_data = None 43 | if application_data: 44 | app_data = application_data() 45 | th_conf = os.path.join(app_data, "thawab", "conf", "main.conf") 46 | else: 47 | app_data = "C:\\" 48 | th_conf = "C:\\thawab.conf" 49 | 50 | else: 51 | app_data = "/usr/share/" 52 | application_data = None 53 | def uri_to_filename(u): 54 | return u 55 | 56 | def get_drives(): 57 | return [] 58 | th_conf = os.path.expanduser('~/.thawab/conf/main.conf') 59 | 60 | def guess_prefixes(): 61 | l = [] 62 | ed = os.path.join(os.path.dirname(sys.argv[0]), 'thawab-data') 63 | ed_1st = False 64 | if os.path.isdir(ed) and os.access(ed, os.W_OK): 65 | l.append(ed) 66 | ed_1st = True 67 | if sys.platform == 'win32': 68 | l.append(os.path.join(app_data,'thawab')) 69 | if not ed_1st: 70 | l.append(ed) 71 | l.extend([os.path.join(d, 'thawab-data') for d in get_drives()]) 72 | else: 73 | l.append(os.path.expanduser('~/.thawab')) 74 | if not ed_1st: 75 | l.append(ed) 76 | l.append('/usr/local/share/thawab') 77 | l.append('/usr/share/thawab') 78 | return l 79 | 80 | 81 | -------------------------------------------------------------------------------- /thawab-data/themes/neo/templates/main.html: -------------------------------------------------------------------------------- 1 | %_r.add_js_link('th-main.js') 2 | %def x_js_script_f(): 3 | 6 | %end 7 | 8 | 9 | %def topnav_f(): 10 | about 11 | get package 12 | contact us 13 | forum 14 | code 15 | %end 16 | 17 |
18 | 19 | 20 |

مكتبة ثواب - بحث ذكي وسريع في أمهات الكتب

21 |
22 | 23 | 24 |
25 |
26 | دليل الاستخدام ( البحث المتقدم ) 27 |
28 |
29 | 30 |
31 | 32 |
33 |
34 |
35 | 36 | في صندوق البحث أعلاه اكتب الكلمات التي تريد البحث عنها. 37 | يمكنك وضع الكلمات بين علامتي اقتباس " " للبحث عن عبارة (كلمات متتالية). عملية البحث ذكية تستوي فيها الحركات وغالبا ما تستوي التغييرات التي تطرأ على الكلمة كالإفراد والتثنية والجمع والإسناد للضمائر وغير ذلك. يمكنك استخدام استعلامات متقدمة عبر الأقواس والرموز 38 | "&" ، "|" ، "!" 39 | من أجل عمليات "و" ، "أو"، "النفي" على الترتيب. كما يمكنك تحديد مجال البحث عبر ذكر اسم الكتاب بعد "كتاب:". يمكنك البحث في عناوين الأبواب أيضا وذلك بكتابة "عنوان:". 40 | للمزيد انظر 41 | دليل الاستخدام 42 |
43 |
44 | 45 | 59 | 60 | 61 | 62 |
63 |

الكتب المتوفرة

64 |
65 | 66 | 67 |
68 |
69 |
    70 | {{!kutublinks}} 71 |
72 |
73 |
74 |
75 |

76 | 77 |
78 | 79 | %rebase layout typ="main", topnav=topnav_f, x_js_script=x_js_script_f 80 | 81 | 82 | -------------------------------------------------------------------------------- /thawab-data/themes/neo/static/manual/all.css: -------------------------------------------------------------------------------- 1 | .clear {clear:both; padding-bottom:2px;} 2 | pre, code { direction: ltr;} 3 | a img { border: none; } 4 | a[href] { text-decoration: none; color:#150;} 5 | a[href]:hover { text-decoration: underline; } 6 | a[target] { background: transparent url('../img/external.gif') top left no-repeat; padding: 0 0 0 12px;} 7 | 8 | body { 9 | font-family: "amiri", "Liberation Sans", "KacstOne", "Simplified Naskh", "KFGQPC Uthman Taha Naskh", "ArabeyesQr", "Times New Roman", "sans", "Sans"; 10 | background:#999; 11 | margin:0;padding:3em;border:0; 12 | } 13 | .export { 14 | background:#fff; 15 | margin:0;padding:3em; 16 | border: #aaa solid 4px; 17 | } 18 | h1, h2, h3, h4 { 19 | font-family: "amiri", "Liberation Sans", "KacstOne", "Simplified Naskh", "KFGQPC Uthman Taha Naskh", "ArabeyesQr", "Times New Roman", "sans", "Sans", sans-serif; 20 | color: #437f22; 21 | font-weight: bold; 22 | } 23 | div.toc { 24 | width:30%; float:left; 25 | background: #cda; 26 | padding:20px;margin:10px; 27 | border: #999 solid 2px; 28 | } 29 | 30 | div.footnotes { 31 | margin: 0.5em -2em 0; 32 | padding: 1em 4em 0; 33 | border-top: 2px dotted #CCC 34 | } 35 | 36 | pre { 37 | background: #cda; 38 | border: #cdc solid 2px; 39 | margin: 2em; 40 | padding: 0.5em; 41 | } 42 | 43 | table { 44 | margin: 2em; 45 | padding:0; 46 | border-collapse:collapse; 47 | } 48 | tr { padding:0; margin:0; border:0;} 49 | td,th { 50 | border: 2px solid #999; 51 | padding:2px 10px; margin:0; 52 | } 53 | th { background: #cda; } 54 | 55 | /* syntax highlighting code */ 56 | .code .br0 { color: #66cc66; } 57 | .code .co1 { color: #808080; font-style: italic; } 58 | .code .co2 { color: #808080; font-style: italic; } 59 | .code .co3 { color: #808080; } 60 | .code .coMULTI { color: #808080; font-style: italic; } 61 | .code .es0 { color: #000099; font-weight: bold; } 62 | .code .kw1 { color: #b1b100; } 63 | .code .kw2 { color: #000000; font-weight: bold; } 64 | .code .kw3 { color: #000066; } 65 | .code .kw4 { color: #993333; } 66 | .code .kw5 { color: #0000ff; } 67 | .code .me1 { color: #006600; } 68 | .code .me2 { color: #006600; } 69 | .code .nu0 { color: #cc66cc; } 70 | .code .re0 { color: #0000ff; } 71 | .code .re1 { color: #0000ff; } 72 | .code .re2 { color: #0000ff; } 73 | .code .re3 { color:#ff3333; font-weight:bold; } 74 | .code .re4 { color: #009999; } 75 | .code .st0 { color: #ff0000; } 76 | .code .sy0 { color: #66cc66; } 77 | 78 | /* notes */ 79 | .noteclassic, .noteimportant, .notewarning, .notetip { 80 | margin: 2em; 81 | margin-left: auto; 82 | margin-right: auto; 83 | width: 70% !important; 84 | min-height: 40px; 85 | clear: both; 86 | text-align: justify; 87 | vertical-align: middle; 88 | border-collapse: collapse; 89 | border: 2px solid #999; 90 | padding: 15px 60px 15px 20px; 91 | background-position: right 50%; 92 | background-repeat: no-repeat; 93 | -moz-border-radius: 20px; 94 | -khtml-border-radius: 20px; 95 | border-radius: 20px; 96 | } 97 | 98 | .noteclassic { 99 | /*border: 1px solid #99D;*/ 100 | background-color: #eef; 101 | background-image: url(images/note.png); 102 | } 103 | 104 | .noteimportant { 105 | /*border: 1px solid #ff0;*/ 106 | background-color: #ffc; 107 | background-image: url(images/important.png); 108 | } 109 | 110 | .notewarning { 111 | /*border: 1px solid #d99;*/ 112 | background-color: #fdd; 113 | background-image: url(images/warning.png); 114 | } 115 | 116 | .notetip { 117 | /*border: 1px solid #9d9;*/ 118 | background-color: #dfd; 119 | background-image: url(images/tip.png); 120 | } 121 | -------------------------------------------------------------------------------- /thawab-data/themes/default/static/manual/all.css: -------------------------------------------------------------------------------- 1 | .clear {clear:both; padding-bottom:2px;} 2 | pre, code { direction: ltr;} 3 | a img { border: none; } 4 | a[href] { text-decoration: none; color:#150;} 5 | a[href]:hover { text-decoration: underline; } 6 | a[target] { background: transparent url('../img/external.gif') top left no-repeat; padding: 0 0 0 12px;} 7 | 8 | body { 9 | font-family: "amiri", "Liberation Sans", "KacstOne", "Simplified Naskh", "KFGQPC Uthman Taha Naskh", "ArabeyesQr", "Times New Roman", "sans", "Sans"; 10 | background:#999; 11 | margin:0;padding:3em;border:0; 12 | } 13 | .export { 14 | background:#fff; 15 | margin:0;padding:3em; 16 | border: #aaa solid 4px; 17 | } 18 | h1, h2, h3, h4 { 19 | font-family: "amiri", "Liberation Sans", "KacstOne", "Simplified Naskh", "KFGQPC Uthman Taha Naskh", "ArabeyesQr", "Times New Roman", "sans", "Sans", sans-serif; 20 | color: #437f22; 21 | font-weight: bold; 22 | } 23 | div.toc { 24 | width:30%; float:left; 25 | background: #cda; 26 | padding:20px;margin:10px; 27 | border: #999 solid 2px; 28 | } 29 | 30 | div.footnotes { 31 | margin: 0.5em -2em 0; 32 | padding: 1em 4em 0; 33 | border-top: 2px dotted #CCC 34 | } 35 | 36 | pre { 37 | background: #cda; 38 | border: #cdc solid 2px; 39 | margin: 2em; 40 | padding: 0.5em; 41 | } 42 | 43 | table { 44 | margin: 2em; 45 | padding:0; 46 | border-collapse:collapse; 47 | } 48 | tr { padding:0; margin:0; border:0;} 49 | td,th { 50 | border: 2px solid #999; 51 | padding:2px 10px; margin:0; 52 | } 53 | th { background: #cda; } 54 | 55 | /* syntax highlighting code */ 56 | .code .br0 { color: #66cc66; } 57 | .code .co1 { color: #808080; font-style: italic; } 58 | .code .co2 { color: #808080; font-style: italic; } 59 | .code .co3 { color: #808080; } 60 | .code .coMULTI { color: #808080; font-style: italic; } 61 | .code .es0 { color: #000099; font-weight: bold; } 62 | .code .kw1 { color: #b1b100; } 63 | .code .kw2 { color: #000000; font-weight: bold; } 64 | .code .kw3 { color: #000066; } 65 | .code .kw4 { color: #993333; } 66 | .code .kw5 { color: #0000ff; } 67 | .code .me1 { color: #006600; } 68 | .code .me2 { color: #006600; } 69 | .code .nu0 { color: #cc66cc; } 70 | .code .re0 { color: #0000ff; } 71 | .code .re1 { color: #0000ff; } 72 | .code .re2 { color: #0000ff; } 73 | .code .re3 { color:#ff3333; font-weight:bold; } 74 | .code .re4 { color: #009999; } 75 | .code .st0 { color: #ff0000; } 76 | .code .sy0 { color: #66cc66; } 77 | 78 | /* notes */ 79 | .noteclassic, .noteimportant, .notewarning, .notetip { 80 | margin: 2em; 81 | margin-left: auto; 82 | margin-right: auto; 83 | width: 70% !important; 84 | min-height: 40px; 85 | clear: both; 86 | text-align: justify; 87 | vertical-align: middle; 88 | border-collapse: collapse; 89 | border: 2px solid #999; 90 | padding: 15px 60px 15px 20px; 91 | background-position: right 50%; 92 | background-repeat: no-repeat; 93 | -moz-border-radius: 20px; 94 | -khtml-border-radius: 20px; 95 | border-radius: 20px; 96 | } 97 | 98 | .noteclassic { 99 | /*border: 1px solid #99D;*/ 100 | background-color: #eef; 101 | background-image: url(images/note.png); 102 | } 103 | 104 | .noteimportant { 105 | /*border: 1px solid #ff0;*/ 106 | background-color: #ffc; 107 | background-image: url(images/important.png); 108 | } 109 | 110 | .notewarning { 111 | /*border: 1px solid #d99;*/ 112 | background-color: #fdd; 113 | background-image: url(images/warning.png); 114 | } 115 | 116 | .notetip { 117 | /*border: 1px solid #9d9;*/ 118 | background-color: #dfd; 119 | background-image: url(images/tip.png); 120 | } 121 | -------------------------------------------------------------------------------- /thawab-data/themes/default/templates/main.html: -------------------------------------------------------------------------------- 1 | %_r.add_js_link('th-main.js') 2 | %def x_js_script_f(): 3 | 6 | %end 7 | 8 | 9 | %def topnav_f(): 10 | about 11 | 12 | 13 | 14 | code 15 | %end 16 | 17 |
18 |

مكتبة ثواب - بحث ذكي وسريع في أمهات الكتب

19 | البحث | الكتب المتوفرة 20 |
21 |
22 |

البحث

23 | 29 |
30 |
31 |
32 | 33 | في صندوق البحث أعلاه اكتب الكلمات التي تريد البحث عنها. 34 | يمكنك وضع الكلمات بين علامتي اقتباس " " للبحث عن عبارة (كلمات متتالية). عملية البحث ذكية تستوي فيها الحركات وغالبا ما تستوي التغييرات التي تطرأ على الكلمة كالإفراد والتثنية والجمع والإسناد للضمائر وغير ذلك. يمكنك استخدام استعلامات متقدمة عبر الأقواس والرموز 35 | "&" ، "|" ، "!" 36 | من أجل عمليات "و" ، "أو"، "النفي" على الترتيب. كما يمكنك تحديد مجال البحث عبر ذكر اسم الكتاب بعد "كتاب:". يمكنك البحث في عناوين الأبواب أيضا وذلك بكتابة "عنوان:". 37 | للمزيد انظر 38 | دليل الاستخدام 39 |
40 |
41 | 42 | 56 | 57 |

الكتب المتوفرة

58 |
59 | 60 |
61 |
62 |
63 |
    64 | {{!kutublinks}} 65 |
66 |
67 |
68 |

69 | 70 |
71 | 72 | %rebase layout typ="main", topnav=topnav_f, x_js_script=x_js_script_f 73 | 74 | 75 | -------------------------------------------------------------------------------- /Thawab/wiki.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | """ 3 | Copyright © 2008, Muayyad Alsadi 4 | 5 | Released under terms of Waqf Public License. 6 | This program is free software; you can redistribute it and/or modify 7 | it under the terms of the latest version Waqf Public License as 8 | published by Ojuba.org. 9 | 10 | This program is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 13 | 14 | The Latest version of the license can be found on 15 | "http://waqf.ojuba.org/license" 16 | 17 | """ 18 | import time, re 19 | #################################### 20 | header_re = re.compile(r'^\s*( = +)\s*(.+?)\s*\1\s*$') 21 | def importFromWiki(c, wiki): 22 | """import a wiki-like into a thawab""" 23 | ki = c.ki 24 | txt = "" 25 | parents = [ki.root] 26 | wikidepths = [0] 27 | title = None 28 | wiki_started = 0 29 | meta = { 30 | 'cache_hash': time.time(), 31 | 'repo': '_local', 32 | 'lang': None, 33 | 'kitab': None, 34 | 'version': '1', 35 | 'releaseMajor': '0', 36 | 'releaseMinor': '0', 37 | 'author': None, 38 | 'year': 0, 39 | 'originalAuthor': None, 40 | 'originalYear': 0, 41 | 'originalKitab': None, 42 | 'originalVersion': None, 43 | 'classification': '_misc'} 44 | for l in wiki: 45 | #l = l.decode('utf-8') 46 | if wiki_started == 0: 47 | if l.startswith('@'): 48 | kv = l.split(' = ',1) 49 | key = kv[0][1:].strip() 50 | if len(kv) == 2: 51 | value = kv[1].strip() 52 | meta[key] = value 53 | continue 54 | else: 55 | wiki_started = 1 56 | m = header_re.match(l) 57 | if not m: 58 | # textbody line: add the line to accumelated textbody variable 59 | txt += l 60 | else: 61 | # new header: 62 | # add the accumelated textbody of a previous header (if exists) to the Kitab 63 | if txt and title: 64 | c.appendNode(parents[-1], txt, {'textbody': None}) 65 | # elif txt and not title: pass # it's leading noise, as title can't be empty because of + in the RE 66 | # reset the accumelated textbody 67 | txt = "" 68 | # now get the title of matched by RE 69 | title = m.group(2) 70 | newwikidepth = 7 - len(m.group(1)) 71 | # several methods, first one is to use: 72 | while(wikidepths[-1] >= newwikidepth): 73 | wikidepths.pop() 74 | parents.pop() 75 | wikidepths = wikidepths + [newwikidepth] 76 | parent = c.appendNode(parents[-1], title, {'header': None}) 77 | parents = parents + [parent] 78 | if (txt): 79 | c.appendNode(parents[-1], txt, {'textbody': None}) 80 | ki.setMCache(meta) 81 | 82 | def wiki2th(w, dst): 83 | import os 84 | import os.path 85 | import Thawab.core 86 | import shutil 87 | n = os.path.basename(w) 88 | if n.endswith('.txt'): 89 | n = n[:-4] + ".ki" 90 | th = Thawab.core.ThawabMan(os.path.expanduser('~/.thawab')) 91 | ki = th.mktemp() 92 | wiki = open(w, "rt").read().decode('utf-8').splitlines() 93 | c = ki.seek(-1, -1) 94 | importFromWiki(c, wiki) 95 | c.flush() 96 | o = ki.uri 97 | del ki 98 | shutil.move(o, os.path.join(dst, n)) 99 | 100 | -------------------------------------------------------------------------------- /Thawab/stemming.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | """ 3 | 4 | Copyright © 2008, Muayyad Alsadi 5 | 6 | Released under terms of Waqf Public License. 7 | This program is free software; you can redistribute it and/or modify 8 | it under the terms of the latest version Waqf Public License as 9 | published by Ojuba.org. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 14 | 15 | The Latest version of the license can be found on 16 | "http://waqf.ojuba.org/license" 17 | 18 | """ 19 | 20 | import sys, os, os.path, re 21 | #harakat = "ًٌٍَُِّْـ".decode('utf-8') 22 | #normalize_tb = dict(map(lambda i: (ord(i),None),list(harakat))) 23 | #normalize_tb[ord('ة'.decode('utf-8'))] = ord('ه'.decode('utf-8')) 24 | #for i in list("ىئإؤأآء".decode('utf-8')): 25 | # normalize_tb[ord(i)] = ord('ا'.decode('utf-8')) 26 | 27 | normalize_tb = { 28 | 65: 97, 66: 98, 67: 99, 68: 100, 69: 101, 70: 102, 29 | 71: 103, 72: 104, 73: 105, 74: 106, 75: 107, 76: 108, 30 | 77: 109, 78: 110, 79: 111, 80: 112, 81: 113, 82: 114, 31 | 83: 115, 84: 116, 85: 117, 86: 118, 87: 119, 88: 120, 32 | 89: 121, 90: 122, 1600: None, 1569: 1575, 1570: 1575, 1571: 1575, 33 | 1572: 1575, 1573: 1575, 1574: 1575, 1577: 1607, # teh marboota -> haa 34 | 1611: None, 1612: None, 1613: None, 1614: None, 1615: None, 35 | 1616: None, 1617: None, 1618: None, 1609: 1575} 36 | 37 | rm_prefix = re.compile("^(?:ا?[وف]?((?:[بك]?ال|لل?)|[اينت])?)") 38 | # TODO: reconsider the suffex re 39 | rm_suffix = re.compile("(?:ا[نت]|[يهة]|ها|ي[هنة]|ون)$") 40 | 41 | #rm_prefix = u"^(?:ا?[وف]?((?:[بك]?ال|لل?)|[اينت])?)" 42 | #rm_suffix = u"(?:ا[نت]|[يهة]|ها|ي[هنة]|ون)$" 43 | #stem_re = rm_prefix+"(\w{3,}?)"+rm_suffix 44 | # أواستقدمتموني 45 | # استفهام عطف جر وتعريف (مثال: "أفككتابي تؤلف ؟" "وللآخرة فلنعد العدة" "فالاستغفار") أو مضارعة 46 | # الجر والتعريف لا تجتمع مع المضارعة 47 | prefix_re = ''.join( ( 48 | "^\u0627?" , # optional hamza 49 | "[\u0648\u0641]?", # optional Atf (with Waw or Faa) 50 | "(?:" , # nouns specific prefixes (Jar and definite article) 51 | "[\u0628\u0643]?\u0627\u0644?|" , # optional Jar (with ba or kaf) with optional AL 52 | "\u0644\u0644|" , # optional LL (Jar with Lam and article ) 53 | "\u0644" , # optional LL (Jar with Lam and article) 54 | ")?" , # end nouns specific prefixes 55 | "(\\w{2,})$" ) ) # the stem is grouped 56 | 57 | # [اتني]|نا|ان|تا|ون|ين|تما 58 | verb_some_subject_re = "[\u0627\u062a\u0646\u064a]|\u0646\u0627|\u0627\u0646|\u062a\u0627|\u0648\u0646|\u064a\u0646|\u062a\u0645\u0627" 59 | # [هن]|ني|نا|ها|هما|هم|هن|كما|كم|كن 60 | verb_object_re = "(?[\u0647\u0646]|\u0646\u064a|\u0646\u0627|\u0647\u0627|\u0647\u0645\u0627|\u0647\u0645|\u0647\u0646|\u0643\u0645\u0627|\u0643\u0645|\u0643\u0646)" 61 | 62 | verb_suffix_re = ''.join( [ 63 | "(?:(?:\u0648\u0627|\u062a\u0645)|" , # وا|تم 64 | "(?:", 65 | "(?:", 66 | verb_some_subject_re, 67 | '|\u0648|\u062a\u0645\u0648', # و|تمو 68 | ")", 69 | verb_object_re,'{1,2}' 70 | ")|(?:", 71 | verb_some_subject_re, 72 | "))?$"]) 73 | 74 | 75 | def removeArabicSuffix(word): 76 | if len(word) > 4: 77 | w = rm_suffix.sub("", word, 1) 78 | if len(w) > 2: 79 | return w 80 | return word 81 | 82 | def removeArabicPrefix(word): 83 | if len(word) > 3: 84 | w = rm_prefix.sub("", word, 1) 85 | if len(w)>2: 86 | return w 87 | return word 88 | 89 | def stemArabic(word): 90 | return removeArabicPrefix(removeArabicSuffix(str(word).translate(normalize_tb))) 91 | 92 | -------------------------------------------------------------------------------- /Thawab/asyncIndex.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | """ 3 | The async threaded indexing class of thawab 4 | Copyright © 2010, Muayyad Alsadi 5 | 6 | Released under terms of Waqf Public License. 7 | This program is free software; you can redistribute it and/or modify 8 | it under the terms of the latest version Waqf Public License as 9 | published by Ojuba.org. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 14 | 15 | The Latest version of the license can be found on 16 | "http://waqf.ojuba.org/license" 17 | 18 | """ 19 | from queue import Queue 20 | from threading import Thread, Lock 21 | from time import sleep 22 | 23 | class AsyncIndex(): 24 | def __init__(self, searchEngine, queueSize = 0, workers = 1): 25 | """ 26 | if number of workers>1 then queued jobs need not be executed in order 27 | """ 28 | self.searchEngine = searchEngine 29 | self.workers_n = workers 30 | self.running = 0 31 | self.lock = Lock() # used to report running tasks correctly 32 | self._q = Queue(queueSize) 33 | self.start() 34 | # we enqueue jobs like this 35 | #for item in source(): self._q.put(item) 36 | 37 | def queueIndexNew(self): 38 | """ 39 | index all non-indexed 40 | """ 41 | self.searchEngine.indexingStart() 42 | for n in self.searchEngine.th.getKitabList(): 43 | vr = self.searchEngine.getIndexedVersion(n) 44 | if not vr: 45 | self.queue("indexKitab", n) 46 | 47 | 48 | def queue(self, method, *args, **kw): 49 | """ 50 | examples: queue("indexNew"); queue("indexKitab","kitab_name"); 51 | """ 52 | self._q.put((method, args, kw)) 53 | 54 | def start(self): 55 | self.keepworking = True 56 | self.end_when_done = False 57 | self.started = False 58 | # here we create our thread pool of workers 59 | for i in range(self.workers_n): 60 | t = Thread(target = self._worker) 61 | t.setDaemon(True) 62 | t.start() 63 | # sleep to make sure all threads are waiting for jobs (inside loop) 64 | while not self.started: sleep(0.25) 65 | 66 | def jobs(self, with_running = True): 67 | """ 68 | return number of queued jobs. 69 | """ 70 | if with_running: 71 | return self._q.qsize()+self.running 72 | else: 73 | return self._q.qsize() 74 | 75 | def join(self): 76 | """ 77 | block till queued jobs are done. 78 | """ 79 | return self._q.join() 80 | 81 | def cancelQueued(self): 82 | self.keepworking = False 83 | self._q.join() 84 | self.started = False 85 | 86 | def endWhenDone(self): 87 | self.end_when_done = True 88 | self._q.join() 89 | self.started = False 90 | 91 | def _worker(self): 92 | while self.keepworking: 93 | self.started = True 94 | # get a job from queue or block sleeping till one is available 95 | item = self._q.get(not self.end_when_done) 96 | if item: 97 | self.lock.acquire() 98 | self.running += 1 99 | self.lock.release() 100 | 101 | method, args, kw = item 102 | f = getattr(self.searchEngine, method) 103 | f(*args,**kw) 104 | if self._q.qsize() == 0: 105 | self.searchEngine.indexingEnd() 106 | self._q.task_done() 107 | 108 | self.lock.acquire() 109 | self.running -= 1 110 | self.lock.release() 111 | 112 | elif self._q.empty(): 113 | if self.end_when_done: 114 | self.keepworking = False 115 | 116 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | APPNAME=thawab 2 | DESTDIR?=/ 3 | DATADIR?=$(DESTDIR)/usr/share 4 | 5 | SOURCES=$(wildcard *.desktop.in) 6 | TARGETS=${SOURCES:.in=} 7 | 8 | ECHO := echo 9 | MAKE := make 10 | PYTHON := python2 11 | INSTALL := install 12 | INTLTOOL_MERGE := intltool-merge 13 | RM := $(shell which rm | egrep '/' | sed 's/\s//g') 14 | GTK_UPDATE_ICON_CACHE := $(shell which gtk-update-icon-cache) 15 | UPDATE_DESKTOP_DATABASE := $(shell which update-desktop-database) 16 | 17 | all: $(TARGETS) icons 18 | 19 | icons: 20 | @for i in 96 72 64 48 36 32 24 22 16; do \ 21 | convert -background none $(APPNAME).svg -resize $${i}x$${i} $(APPNAME)-$${i}.png; \ 22 | done 23 | pos: 24 | $(MAKE) -C po all 25 | 26 | install: locale 27 | @$(ECHO) "*** Installing..." 28 | @$(PYTHON) setup.py install -O2 --root $(DESTDIR) 29 | @$(ECHO) "Copying: $(APPNAME).desktop -> $(DATADIR)/applications/" 30 | @$(INSTALL) -d $(DATADIR)/applications/ 31 | @$(INSTALL) -d $(DATADIR)/$(APPNAME)/ 32 | @$(INSTALL) -m 0644 $(APPNAME).desktop $(DATADIR)/applications/ 33 | @$(INSTALL) -m 0644 -D $(APPNAME).svg $(DATADIR)/icons/hicolor/scalable/apps/$(APPNAME).svg; 34 | @for i in 96 72 64 48 36 32 24 22 16; do \ 35 | $(INSTALL) -d $(DATADIR)/icons/hicolor/$${i}x$${i}/apps; \ 36 | $(INSTALL) -m 0644 -D $(APPNAME)-$${i}.png $(DATADIR)/icons/hicolor/$${i}x$${i}/apps/$(APPNAME).png; \ 37 | done 38 | @$(RM) -rf build 39 | @$(DESTDIR)/$(UPDATE_DESKTOP_DATABASE) --quiet $(DATADIR)/applications &> /dev/null || : 40 | @$(DESTDIR)/$(GTK_UPDATE_ICON_CACHE) --quiet $(DATADIR)/icons/hicolor &> /dev/null || : 41 | 42 | uninstall: 43 | @$(ECHO) "*** Uninstalling..." 44 | @$(ECHO) "- Removing: $(DATADIR)/applications/$(APPNAME).desktop" 45 | @$(RM) -f $(DATADIR)/applications/$(APPNAME).desktop 46 | @$(ECHO) "- Removing: $(DESTDIR)/usr/share/locale/*/LC_MESSAGES/$(APPNAME).mo" 47 | @$(RM) -f $(DESTDIR)/usr/share/locale/*/LC_MESSAGES/$(APPNAME).mo 48 | @$(ECHO) "- Removing: $(DESTDIR)/usr/bin/$(APPNAME)" 49 | @$(RM) -f $(DESTDIR)/usr/bin/$(APPNAME)-gtk 50 | @$(RM) -f $(DESTDIR)/usr/bin/$(APPNAME)-server 51 | @$(ECHO) "- Removing: $(DESTDIR)/usr/lib/python*/*-packages/Thawab" 52 | @$(RM) -rf $(DESTDIR)/usr/lib/python*/*-packages/Thawab 53 | @$(ECHO) "- Removing: $(DESTDIR)/usr/lib/python*/*-packages/$(APPNAME)*" 54 | @$(RM) -rf $(DESTDIR)/usr/lib/python*/*-packages/$(APPNAME)* 55 | @$(ECHO) "- Removing: $(DESTDIR)/usr/share/$(APPNAME)" 56 | @$(RM) -rf $(DESTDIR)/usr/share/$(APPNAME) 57 | 58 | @$(ECHO) "- Removing: $(DESTDIR)/usr/*/share/locale/*/LC_MESSAGES/$(APPNAME).mo" 59 | @$(RM) -f $(DESTDIR)/usr/*/share/locale/*/LC_MESSAGES/$(APPNAME).mo 60 | @$(ECHO) "- Removing: $(DESTDIR)/usr/*/bin/$(APPNAME)" 61 | @$(RM) -f $(DESTDIR)/usr/*/bin/$(APPNAME)-gtk 62 | @$(RM) -f $(DESTDIR)/usr/*/bin/$(APPNAME)-server 63 | @$(ECHO) "- Removing: $(DESTDIR)/usr/*/lib/python*/*-packages/Thawab" 64 | @$(RM) -rf $(DESTDIR)/usr/*/lib/python*/*-packages/Thawab 65 | @$(ECHO) "- Removing: $(DESTDIR)/usr/*/lib/python*/*-packages/$(APPNAME)*" 66 | @$(RM) -rf $(DESTDIR)/usr/*/lib/python*/*-packages/$(APPNAME)* 67 | @$(ECHO) "- Removing: $(DESTDIR)/usr/*/share/$(APPNAME)" 68 | @$(RM) -rf $(DESTDIR)/usr/*/share/$(APPNAME) 69 | 70 | @$(RM) -f $(DATADIR)/icons/hicolor/scalable/apps/$(APPNAME).svg 71 | @$(RM) -f $(DATADIR)/icons/hicolor/*/apps/$(APPNAME).png; 72 | @$(DESTDIR)/$(UPDATE_DESKTOP_DATABASE) --quiet $(DATADIR)/applications &> /dev/null || : 73 | @$(DESTDIR)/$(GTK_UPDATE_ICON_CACHE) --quiet $(DATADIR)/icons/hicolor &> /dev/null || : 74 | 75 | %.desktop: %.desktop.in pos 76 | intltool-merge -d po $< $@ 77 | 78 | clean: 79 | @$(ECHO) "*** Cleaning..." 80 | @$(MAKE) -C po clean 81 | @$(ECHO) "- Removing: $(TARGETS)" 82 | @$(RM) -f $(TARGETS) 83 | @$(ECHO) "- Removing: locale build" 84 | @$(RM) -rf locale build 85 | @$(ECHO) "- Removing: *.pyc" 86 | @$(RM) -f *.pyc 87 | @$(ECHO) "- Removing: */*.pyc" 88 | @$(RM) -f */*.pyc 89 | @$(ECHO) "- Removing: */__pycache__/" 90 | @$(RM) -rf */__pycache__/ 91 | @$(ECHO) "- Removing: $(APPNAME)-*.png" 92 | @$(RM) -f $(APPNAME)-*.png 93 | @$(ECHO) "- Removing Cache directories" 94 | @$(RM) -f thawab-data/user.db 95 | @$(RM) -rf thawab-data/cache 96 | @$(RM) -rf thawab-data/index 97 | @$(RM) -rf thawab-data/tmp 98 | @$(RM) -rf thawab-data/db 99 | @$(RM) -rf thawab-data/conf 100 | -------------------------------------------------------------------------------- /bok2ki.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/python3 2 | # -*- coding: UTF-8 -*- 3 | """ 4 | Script to import .bok files 5 | Copyright © 2008-2010, Muayyad Alsadi 6 | 7 | Released under terms of Waqf Public License. 8 | This program is free software; you can redistribute it and/or modify 9 | it under the terms of the latest version Waqf Public License as 10 | published by Ojuba.org. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 15 | 16 | The Latest version of the license can be found on 17 | "http://waqf.ojuba.org/license" 18 | 19 | """ 20 | 21 | import sys, os, os.path, glob, shutil, re 22 | import sqlite3 23 | from getopt import getopt, GetoptError 24 | 25 | # TODO: take shamela prefix 26 | # import Files/special.mdb Files/main.mdb first 27 | # then take bokids eg. -f /opt/emu/apps/shamela-r1/ 100 15001 ..etc. 28 | # if first arg of ShamelaSqlite is a directory, 29 | # getTables should generate tb:fn 30 | # 31 | 32 | def usage(): 33 | print('''\ 34 | Usage: %s [-i] [-m DIR] FILES ... 35 | Where: 36 | \t-i\t\t- in-memory 37 | \t-m DIR\t\t- move successfully imported BOK files into DIR 38 | \t--ft-prefix=FOOTER_PREFIX default is "(¬" 39 | \t--ft-suffix=FOOTER_SUFFIX default is ")" 40 | \t--ft-leading=[0|1] should footnote be match at line start only, default is 0 41 | \t--ft-sp=[0|1|2] no, single or many whitespaces, default is 0 42 | \t--bft-prefix=FOOTER_PREFIX footnote anchor in body prefix, default is "(¬" 43 | \t--bft-suffix=FOOTER_SUFFIX footnote anchor in body suffix, default is ")" 44 | \t--bft-sp=[0|1|2] no, single or many whitespaces, default is 0 45 | 46 | the generated files will be moved into db in thawab prefix (usually ~/.thawab/db/) 47 | ''' % os.path.basename(sys.argv[0])) 48 | 49 | try: 50 | opts, args = getopt(sys.argv[1:], "im:", ["help", 'ft-prefix=', 'ft-suffix=', 'bft-prefix=', 'bft-suffix=', 'ft-leading=', 'ft-sp=', 'bft-sp=']) 51 | except GetoptError as err: 52 | print(str(err)) # will print something like "option -a not recognized" 53 | usage() 54 | sys.exit(1) 55 | 56 | if not args: 57 | print("please provide at least one .bok files") 58 | usage() 59 | sys.exit(1) 60 | 61 | opts=dict(opts) 62 | 63 | def progress(msg, p, *a, **kw): print(" ** [%g%% completed] %s" % (p,msg)) 64 | 65 | from Thawab.core import ThawabMan 66 | from Thawab.shamelaUtils import ShamelaSqlite,shamelaImport 67 | th=ThawabMan() 68 | thprefix=th.prefixes[0] 69 | 70 | if '-i' not in opts: db_fn=os.path.expanduser('~/bok2sql.db') 71 | else: db_fn=None 72 | 73 | # ¬ U+00AC NOT SIGN 74 | ft_prefix=opts.get('--ft-prefix','(¬').decode('utf-8'); ft_prefix_len=len(ft_prefix) 75 | ft_suffix=opts.get('--ft-suffix',')').decode('utf-8'); ft_suffix_len=len(ft_suffix) 76 | ft_sp=['', r'\s?' , r'\s*'][int(opts.get('--ft-sp','0'))] 77 | ft_at_line_start=int(opts.get('--ft-leading','0')) 78 | footnote_re=(ft_at_line_start and '^\s*' or '') + re.escape(ft_prefix)+ft_sp+r'(\d+)'+ft_sp+re.escape(ft_suffix) 79 | 80 | bft_prefix=opts.get('--bft-prefix','(¬').decode('utf-8'); 81 | bft_suffix=opts.get('--bft-suffix',')').decode('utf-8'); 82 | bft_sp=['', r'\s?' , r'\s*'][int(opts.get('--bft-sp','0'))] 83 | body_footnote_re=re.escape(bft_prefix)+bft_sp+r'(\d+)'+bft_sp+re.escape(bft_suffix) 84 | 85 | 86 | 87 | for fn in args: 88 | if db_fn: 89 | if os.path.exists(db_fn): os.unlink(db_fn) 90 | cn=sqlite3.connect(db_fn, isolation_level=None) 91 | else: cn=None 92 | sh=ShamelaSqlite(fn, cn, 0 , 0, progress) 93 | sh.toSqlite() 94 | for bkid in sh.getBookIds(): 95 | ki=th.mktemp() 96 | c=ki.seek(-1,-1) 97 | 98 | m=shamelaImport(c, sh, bkid, footnote_re, body_footnote_re, ft_prefix_len, ft_suffix_len) 99 | c.flush() 100 | print("moving %s to %s" % (ki.uri, os.path.join(thprefix,'db', m['kitab']+"-"+m['version']+".ki"))) 101 | shutil.move(ki.uri, os.path.join(thprefix,'db', m['kitab']+"-"+m['version']+".ki")) 102 | if '-m' in opts: 103 | dd=opts['-m'] 104 | if not os.path.isdir(dd): 105 | try: os.makedirs(dd) 106 | except OSError: pass 107 | if os.path.isdir(dd): 108 | dst=os.path.join(dd,os.path.basename(fn)) 109 | print("moving %s to %s" % (fn,dst)) 110 | shutil.move(fn, dst) 111 | else: print("could not move .bok files, target directory does not exists") 112 | 113 | -------------------------------------------------------------------------------- /thawab-data/themes/neo/static/th-main.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * copyright © 2010 ojuba.org, Muayyad Saleh Alsadi 4 | * 5 | **/ 6 | var resultsPerPage=50; 7 | var async_tips_div, mouse_x, mouse_y; 8 | 9 | function main_search_row_factory(u, bu, r) { 10 | return "

"+ html_escape(r.t)+"

"+html_escape(r.k).replace(new RegExp('_', 'g'), ' ')+" - "+html_escape(r.a)+" ("+(r.y || "-")+")
\n" 11 | 12 | 13 | } 14 | var search_row_factory=main_search_row_factory; 15 | var search_done=function() {}; 16 | 17 | function showSearchPage(hash, pg){ 18 | var j,i=(pg-1)*resultsPerPage,o,h,l; 19 | var u=script+'/ajax/searchExcerpt/'+hash+'/',bu=script+'/view/'; 20 | /*l=document.getElementById("loading"); 21 | l.style.display="block";*/ 22 | window.scroll(100,0); 23 | getJson("/json/searchResults", {h:hash,i:i,c:resultsPerPage}, 24 | function (d) { 25 | var c=d.c,a=d.a; 26 | o=document.getElementById("SearchResults"); 27 | h="" 28 | o.innerHTML=h; 29 | for (j=0;j 10) { pages = 10; } 68 | document.getElementById("SearchPagesCount").innerHTML=pages; 69 | o=document.getElementById("SearchPages"); 70 | h=''; 71 | o.innerHTML=h; 72 | if (d.c>0) { 73 | for (i=1;i<=pages;++i) h+=''+(i)+''; 74 | o.innerHTML=h; 75 | showSearchPage(d.h,1); 76 | 77 | if (main) {se_t.style.display="block";} 78 | } 79 | 80 | search_done(); 81 | document.location="#searchResults"; 82 | }, 83 | function () { 84 | 85 | search_done(); 86 | } 87 | ); 88 | return false; 89 | 90 | } 91 | 92 | function kutubFilter(q) { 93 | var o=document.getElementById("kutubList"); 94 | var old=o.innerHTML; 95 | var l=document.getElementById("loading"); 96 | /*l.style.display="block";*/ 97 | getAjax(script+"/ajax/kutub", {q:q}, 98 | function (d) { 99 | o.innerHTML=d; 100 | }, 101 | function () { 102 | o.innerHTML=old; 103 | } 104 | ); 105 | 106 | return false; 107 | } 108 | 109 | function moveMouse(E) { 110 | var e=window.event || E; 111 | mouse_x=window.pageXOffset+e.clientX; 112 | mouse_y=window.pageYOffset+e.clientY; 113 | } 114 | 115 | function asynctip(e) { 116 | 117 | async_tips_div.style.top=(mouse_y+e.offsetHeight+5)+"px"; 118 | async_tips_div.innerHTML="..."; 119 | async_tips_div.style.display="block"; 120 | u=e.getAttribute('rel'); 121 | getAjax(u, { }, 122 | function (d) { 123 | async_tips_div.innerHTML=d; 124 | 125 | }, 126 | function () { 127 | async_tips_div.style.display="none"; 128 | 129 | } 130 | ); 131 | 132 | } 133 | 134 | function asynctip_hide(e) { 135 | async_tips_div.style.display="none"; 136 | } 137 | 138 | function async_tips_init() { 139 | var d=document.createElement("div"); 140 | d.id="async_tips_div"; 141 | d.style.width="60%"; 142 | d.style.display="none"; 143 | document.body.appendChild(d); 144 | async_tips_div=d; 145 | 146 | if (document.addEventListener) { 147 | document.addEventListener('mousemove',moveMouse,false); 148 | } else { 149 | document.attachEvent('onmousemove',moveMouse); 150 | } 151 | } 152 | init_ls.push(async_tips_init); 153 | 154 | -------------------------------------------------------------------------------- /thawab-data/themes/default/static/th-view.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * copyright © 2010 ojuba.org, Muayyad Saleh Alsadi 4 | * 5 | **/ 6 | var last_highlighted=""; 7 | var th_hash; 8 | function mini_search_row_factory(u, bu, r) { 9 | return ""+ html_escape(r.t)+""+html_escape(r.r)+"\n"; 10 | } 11 | function mini_search_row_factory_st(u, bu, r) { 12 | return ""+ html_escape(r.t)+""+html_escape(r.r)+"\n"; 13 | } 14 | resultsPerPage=10; // defined in main.js 15 | search_row_factory=(is_static)?mini_search_row_factory_st:mini_search_row_factory; 16 | function doMiniSearch(q) { 17 | doSearch(q+" كتاب:"+kitabId, false); 18 | } 19 | 20 | function view_cb(h) { 21 | var l,n; 22 | window.scroll(0,0); 23 | l=document.getElementById("loading"); 24 | l.style.display="block"; 25 | if (! h) h="_i0"; 26 | th_hash=h; 27 | getJson(script+"/json/view/"+kitabUid+"/"+h, {}, 28 | function (d) { 29 | document.getElementById("maincontent").innerHTML=d.content; 30 | document.getElementById("subtoc").innerHTML=d.childrenLinks; 31 | document.getElementById("breadcrumbs").innerHTML=d.breadcrumbs; 32 | n=document.getElementById("prevLink"); 33 | n.setAttribute('title', d.prevTitle); 34 | n.setAttribute('href', d.prevUrl); 35 | n=document.getElementById("upLink"); 36 | n.setAttribute('title', d.upTitle); 37 | n.setAttribute('href', d.upUrl); 38 | n=document.getElementById("nextLink"); 39 | n.setAttribute('title', d.nextTitle); 40 | n.setAttribute('href', d.nextUrl); 41 | l.style.display="none"; /* should be faded */ 42 | highlight_words(document.getElementById("maincontent"), highlighted, true); 43 | }, 44 | function () { 45 | l.style.display="none"; /* should show error */ 46 | } 47 | ); 48 | 49 | return false; 50 | } 51 | 52 | function ajax_check_hash() { 53 | var h=window.location.hash; 54 | if (h==("#"+th_hash)) return true; 55 | view_cb(h.slice(1)); 56 | return true; 57 | } 58 | 59 | var harakat="ًٌٍَُِّْـ"; 60 | 61 | function highlight_word(o, w, i) { 62 | w=w.trim(); 63 | if (w=="") return; 64 | w=re_escape(w).replace(/(\\?.)/g, "$1[\-_"+harakat+"]*"); 65 | w="("+w+")"; 66 | var re = new RegExp( w, "gi"); 67 | a=o.innerHTML.split(/(<\/?[^>]*>)/); 68 | for (j in a) { 69 | s=a[j]; 70 | if (s && s[0]!="<") { 71 | a[j]=s.replace(re, "$1"); 72 | } 73 | } 74 | o.innerHTML=a.join(""); 75 | } 76 | 77 | function highlight_words(o, w, scroll) { 78 | var i,a=w.split(" "); 79 | highlight_words_off(o); 80 | for (i in a) { 81 | highlight_word(o,a[i],i); 82 | } 83 | if (scroll) scroll_to_first_highlighted(); 84 | } 85 | 86 | function scroll_to_first_highlighted() { 87 | a=document.getElementsByClassName("highlight"); 88 | for (j=0;j([^<>]*)<\/span>/gi, "$1"); 96 | } 97 | 98 | var highlighting=false; 99 | 100 | function highlight_cb() { 101 | if (highlighting) return true; 102 | var q=document.getElementById('q').value; 103 | if (q=="نص البحث") return true; 104 | highlighting=true; 105 | highlighted=q; 106 | if (last_highlighted!=highlighted) { 107 | last_highlighted=highlighted; 108 | highlight_words(document.getElementById("maincontent"), highlighted, false); 109 | } 110 | highlighting=false; 111 | return true; 112 | } 113 | 114 | 115 | function th_view_init() { 116 | var l; 117 | if (!is_static) { 118 | l=document.location.toString(); 119 | loc=window.location.hash.slice(1); 120 | if (loc=="") document.location=l+"#_i0"; 121 | else view_cb(loc); 122 | } 123 | /* hide mini-search if not indexed */ 124 | if (!is_indexed) { 125 | document.getElementById("minisearch").style.display="none"; 126 | document.getElementById("nominisearch").style.display="block"; 127 | } 128 | highlighted=get_url_vars()["highlight"] || ""; 129 | highlight_words(document.getElementById("maincontent"), highlighted, true); 130 | last_highlighted=highlighted; 131 | } 132 | search_done=scroll_to_first_highlighted; 133 | animations["_highlight"]=[highlight_cb]; 134 | if (!is_static) animations["_ajax_check_hash"]=[ajax_check_hash]; 135 | init_ls.push(th_view_init); 136 | -------------------------------------------------------------------------------- /thawab-data/themes/default/static/th-main.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * copyright © 2010 ojuba.org, Muayyad Saleh Alsadi 4 | * 5 | **/ 6 | var resultsPerPage=50; 7 | var async_tips_div, mouse_x, mouse_y; 8 | 9 | function main_search_row_factory(u, bu, r) { 10 | return ""+html_escape(r.k).replace(new RegExp('_', 'g'), ' ')+""+html_escape(r.a)+""+(r.y || "-")+""+ html_escape(r.t)+""+html_escape(r.r)+"\n"; 11 | } 12 | var search_row_factory=main_search_row_factory; 13 | var search_done=function() {}; 14 | 15 | function showSearchPage(hash, pg){ 16 | var j,i=(pg-1)*resultsPerPage,o,h,l; 17 | var u=script+'/ajax/searchExcerpt/'+hash+'/',bu=script+'/view/'; 18 | l=document.getElementById("loading"); 19 | l.style.display="block"; 20 | window.scroll(100,0); 21 | getJson("/json/searchResults", {h:hash,i:i,c:resultsPerPage}, 22 | function (d) { 23 | var c=d.c,a=d.a; 24 | o=document.getElementById("SearchResults"); 25 | h="" 26 | o.innerHTML=h; 27 | for (j=0;j 10) { pages = 10; } 66 | document.getElementById("SearchPagesCount").innerHTML=pages; 67 | o=document.getElementById("SearchPages"); 68 | h=''; 69 | o.innerHTML=h; 70 | if (d.c>0) { 71 | for (i=1;i<=pages;++i) h+=''+(i)+''; 72 | o.innerHTML=h; 73 | showSearchPage(d.h,1); 74 | if (main) {se_t.style.display="block";} 75 | } 76 | l.style.display="none"; /* should be faded */ 77 | search_done(); 78 | }, 79 | function () { 80 | l.style.display="none"; /* should show error */ 81 | search_done(); 82 | } 83 | ); 84 | return false; 85 | 86 | } 87 | 88 | function kutubFilter(q) { 89 | var o=document.getElementById("kutubListing"); 90 | var old=o.innerHTML; 91 | var l=document.getElementById("loading"); 92 | l.style.display="block"; 93 | getAjax(script+"/ajax/kutub", {q:q}, 94 | function (d) { 95 | o.innerHTML=d; 96 | l.style.display="none"; 97 | }, 98 | function () { 99 | o.innerHTML=old; 100 | l.style.display="none"; 101 | } 102 | ); 103 | 104 | return false; 105 | } 106 | 107 | function moveMouse(E) { 108 | var e=window.event || E; 109 | mouse_x=window.pageXOffset+e.clientX; 110 | mouse_y=window.pageYOffset+e.clientY; 111 | } 112 | 113 | function asynctip(e) { 114 | var l=document.getElementById("loading"); 115 | l.style.display="block"; 116 | async_tips_div.style.top=(mouse_y+e.offsetHeight+5)+"px"; 117 | async_tips_div.innerHTML="..."; 118 | async_tips_div.style.display="block"; 119 | u=e.getAttribute('rel'); 120 | getAjax(u, { }, 121 | function (d) { 122 | async_tips_div.innerHTML=d; 123 | l.style.display="none"; /* should be faded */ 124 | }, 125 | function () { 126 | async_tips_div.style.display="none"; 127 | l.style.display="none"; /* should show error */ 128 | } 129 | ); 130 | 131 | } 132 | 133 | function asynctip_hide(e) { 134 | async_tips_div.style.display="none"; 135 | } 136 | 137 | function async_tips_init() { 138 | var d=document.createElement("div"); 139 | d.id="async_tips_div"; 140 | d.style.width="60%"; 141 | d.style.display="none"; 142 | document.body.appendChild(d); 143 | async_tips_div=d; 144 | 145 | if (document.addEventListener) { 146 | document.addEventListener('mousemove',moveMouse,false); 147 | } else { 148 | document.attachEvent('onmousemove',moveMouse); 149 | } 150 | } 151 | init_ls.push(async_tips_init); 152 | 153 | -------------------------------------------------------------------------------- /thawab-data/themes/neo/static/th-view.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * copyright © 2010 ojuba.org, Muayyad Saleh Alsadi 4 | * 5 | **/ 6 | var last_highlighted=""; 7 | var th_hash; 8 | function mini_search_row_factory(u, bu, r) { 9 | return ""+ html_escape(r.t)+""+html_escape(r.r)+"\n"; 10 | } 11 | function mini_search_row_factory_st(u, bu, r) { 12 | return ""+ html_escape(r.t)+""+html_escape(r.r)+"\n"; 13 | } 14 | resultsPerPage=10; // defined in main.js 15 | search_row_factory=(is_static)?mini_search_row_factory_st:mini_search_row_factory; 16 | function doMiniSearch(q) { 17 | doSearch(q+" كتاب:"+kitabId, false); 18 | } 19 | 20 | function view_cb(h) { 21 | var n,b; 22 | var spacer = ""; 23 | window.scroll(0,0); 24 | if (! h) h="_i0"; 25 | th_hash=h; 26 | getJson(script+"/json/view/"+kitabUid+"/"+h, {}, 27 | function (d) { 28 | document.getElementById("maincontent").innerHTML=arabic_numbers(d.content); 29 | document.getElementById("subtoc").innerHTML=arabic_numbers(d.childrenLinks); 30 | 31 | b=spacer+ d.breadcrumbs.replace(/>/g,spacer); 32 | if (d.breadcrumbs){ 33 | document.getElementById("breadcrumbs").innerHTML=arabic_numbers(b);} 34 | else {document.getElementById("breadcrumbs").innerHTML="";} 35 | 36 | n=document.getElementById("prevLink"); 37 | n.setAttribute('title', d.prevTitle); 38 | if (d.prevTitle) {n.setAttribute('class', 'button'); 39 | n.setAttribute('href', d.prevUrl);} 40 | else { n.setAttribute('class', 'button inactive'); 41 | n.setAttribute('href','javascript:void(0)'); } 42 | 43 | n=document.getElementById("upLink"); 44 | n.setAttribute('title', d.upTitle); 45 | if (d.upUrl != window.location.hash) {n.setAttribute('class', 'button'); 46 | n.setAttribute('href', d.upUrl);} 47 | else { n.setAttribute('class', 'button inactive'); 48 | n.setAttribute('href','javascript:void(0)'); } 49 | 50 | 51 | n=document.getElementById("nextLink"); 52 | n.setAttribute('title', d.nextTitle); 53 | if (d.nextTitle) {n.setAttribute('class', 'button'); 54 | n.setAttribute('href', d.nextUrl);} 55 | else { n.setAttribute('class', 'button inactive'); 56 | n.setAttribute('href','javascript:void(0)'); } 57 | highlight_words(document.getElementById("maincontent"), highlighted, true); 58 | }, 59 | function () { 60 | /* should show error */ 61 | } 62 | ); 63 | 64 | return false; 65 | } 66 | function arabic_numbers(string) { 67 | /* FIX this code 68 | string = string.replace(/0/g,"٠"); 69 | string = string.replace(/1/g,"١"); 70 | string = string.replace(/2/g,"٢"); 71 | string = string.replace(/3/g,"٣"); 72 | string = string.replace(/4/g,"٤"); 73 | string = string.replace(/5/g,"٥"); 74 | string = string.replace(/6/g,"٦"); 75 | string = string.replace(/7/g,"٧"); 76 | string = string.replace(/8/g,"٨"); 77 | string = string.replace(/9/g,"٩"); 78 | */ 79 | return string; 80 | } 81 | function ajax_check_hash() { 82 | var h=window.location.hash; 83 | if (h==("#"+th_hash)) return true; 84 | view_cb(h.slice(1)); 85 | return true; 86 | } 87 | 88 | var harakat="ًٌٍَُِّْـ"; 89 | 90 | function highlight_word(o, w, i) { 91 | w=w.trim(); 92 | if (w=="") return; 93 | w=re_escape(w).replace(/(\\?.)/g, "$1[\-_"+harakat+"]*"); 94 | w="("+w+")"; 95 | var re = new RegExp( w, "gi"); 96 | a=o.innerHTML.split(/(<\/?[^>]*>)/); 97 | for (j in a) { 98 | s=a[j]; 99 | if (s && s[0]!="<") { 100 | a[j]=s.replace(re, "$1"); 101 | } 102 | } 103 | o.innerHTML=a.join(""); 104 | } 105 | 106 | function highlight_words(o, w, scroll) { 107 | var i,a=w.split(" "); 108 | highlight_words_off(o); 109 | for (i in a) { 110 | highlight_word(o,a[i],i); 111 | } 112 | if (scroll) scroll_to_first_highlighted(); 113 | } 114 | 115 | function scroll_to_first_highlighted() { 116 | a=document.getElementsByClassName("highlight"); 117 | for (j=0;j([^<>]*)<\/span>/gi, "$1"); 125 | } 126 | 127 | var highlighting=false; 128 | 129 | function highlight_cb() { 130 | if (highlighting) return true; 131 | var q=document.getElementById('q').value; 132 | if (q=="نص البحث") return true; 133 | highlighting=true; 134 | highlighted=q; 135 | if (last_highlighted!=highlighted) { 136 | last_highlighted=highlighted; 137 | highlight_words(document.getElementById("maincontent"), highlighted, false); 138 | } 139 | highlighting=false; 140 | return true; 141 | } 142 | 143 | 144 | function th_view_init() { 145 | var l; 146 | if (!is_static) { 147 | l=document.location.toString(); 148 | loc=window.location.hash.slice(1); 149 | if (loc=="") document.location=l+"#_i0"; 150 | else view_cb(loc); 151 | } 152 | /* hide mini-search if not indexed */ 153 | if (!is_indexed) { 154 | document.getElementById("minisearch").style.display="none"; 155 | document.getElementById("nominisearch").style.display="block"; 156 | } 157 | highlighted=get_url_vars()["highlight"] || ""; 158 | highlight_words(document.getElementById("maincontent"), highlighted, true); 159 | last_highlighted=highlighted; 160 | } 161 | search_done=scroll_to_first_highlighted; 162 | animations["_highlight"]=[highlight_cb]; 163 | if (!is_static) animations["_ajax_check_hash"]=[ajax_check_hash]; 164 | init_ls.push(th_view_init); 165 | -------------------------------------------------------------------------------- /thawab.spec: -------------------------------------------------------------------------------- 1 | %global owner ojuba-org 2 | 3 | Name: thawab 4 | Summary: Arabic/Islamic encyclopedia system 5 | Summary(ar): نظام موسوعي عربي/إسلامي 6 | URL: http://ojuba.org/ 7 | Version: 4.1 8 | Release: 2%{?dist} 9 | Source0: https://github.com/%{owner}/%{name}/archive/%{version}/%{name}-%{version}.tar.gz 10 | License: WAQFv2 11 | BuildArch: noarch 12 | Requires: python-whoosh >= 1.7.2 13 | Requires: python-okasha >= 0.2.3 14 | Requires: pygobject3 >= 3.0.2 15 | Requires: python 16 | Requires: mdbtools 17 | Requires: python-paste 18 | Requires: islamic-menus 19 | Requires: python-othman 20 | Requires: webkit2gtk3 21 | BuildRequires: gettext 22 | BuildRequires: python2-devel 23 | BuildRequires: perl 24 | BuildRequires: ImageMagick 25 | BuildRequires: intltool 26 | 27 | %description 28 | Thawab Arabic/Islamic encyclopedia system 29 | 30 | %description -l ar 31 | نظام موسوعي عربي/إسلامي 32 | 33 | %prep 34 | %autosetup -n %{name}-%{version} 35 | 36 | %build 37 | bash update-manual-from-site.sh 38 | make %{?_smp_mflags} 39 | 40 | %install 41 | %make_install 42 | 43 | 44 | 45 | 46 | # Register as an application to be visible in the software center 47 | # 48 | # NOTE: It would be *awesome* if this file was maintained by the upstream 49 | # project, translated and installed into the right place during `make install`. 50 | # 51 | # See http://www.freedesktop.org/software/appstream/docs/ for more details. 52 | # 53 | mkdir -p $RPM_BUILD_ROOT%{_datadir}/appdata 54 | cat > $RPM_BUILD_ROOT%{_datadir}/appdata/%{name}.appdata.xml < 56 | 57 | 61 | 62 | %{name}.desktop 63 | CC0-1.0 64 | Arabic/Islamic encyclopedia system 65 | نظام موسوعي عربي/إسلامي 66 | 67 |

68 | Arabic/Islamic encyclopedia system. 69 |

70 |
71 | 72 |

73 | نظام موسوعي عربي/إسلامي. 74 |

75 |
76 | https://github.com/ojuba-org/%{name} 77 | 78 | http://ojuba.org/screenshots/%{name}.png 79 | 80 | moceap@hotmail.com 81 |
82 | EOF 83 | 84 | 85 | 86 | %post 87 | touch --no-create %{_datadir}/icons/hicolor || : 88 | if [ -x %{_bindir}/gtk-update-icon-cache ] ; then 89 | %{_bindir}/gtk-update-icon-cache --quiet %{_datadir}/icons/hicolor || : 90 | fi 91 | 92 | %postun 93 | touch --no-create %{_datadir}/icons/hicolor || : 94 | if [ -x %{_bindir}/gtk-update-icon-cache ] ; then 95 | %{_bindir}/gtk-update-icon-cache --quiet %{_datadir}/icons/hicolor || : 96 | fi 97 | 98 | %files 99 | %license waqf2-ar.pdf 100 | %doc waqf2-ar.pdf readme 101 | %{_bindir}/thawab-gtk 102 | %{_bindir}/thawab-server 103 | %{python2_sitelib}/Thawab/* 104 | %{python2_sitelib}/*.egg-info 105 | %{_datadir}/thawab/ 106 | %{_datadir}/icons/hicolor/*/apps/*.png 107 | %{_datadir}/icons/hicolor/*/apps/*.svg 108 | %{_datadir}/applications/*.desktop 109 | %{_datadir}/locale/*/*/*.mo 110 | %{_datadir}/appdata/%{name}.appdata.xml 111 | 112 | %changelog 113 | * Mon Feb 20 2017 Yucuf Sourani - 4.1-2 114 | - Release 2 115 | - remove webkitgtk3 dependancy 116 | - add webkit2gtk3 dependancy 117 | 118 | * Mon Feb 20 2017 Mosaab Alzoubi - 4.1-1 119 | - Update to 4.1 120 | - Fixes for warnings 121 | 122 | * Sun Feb 19 2017 Mosaab Alzoubi - 4.0-1 123 | - Update to 4.0 124 | - New generation of Thqwab Server 125 | - New enhanced look 126 | - New way to Github 127 | - Add Appdata 128 | 129 | * Sun Nov 13 2016 Ehab El-Gedawy - 3.2.1-1 130 | - add webkitgtk3 dependancy 131 | 132 | * Tue Jul 14 2015 Mosaab Alzoubi - 3.2.0-3 133 | - Enhance summary 134 | - Remove Group tag 135 | - Add Arabic summary and description 136 | - Improve %%install section 137 | - Remove %%clean section 138 | - Remove old attr way 139 | - Use %%license 140 | 141 | * Tue Jul 14 2015 Mosaab Alzoubi - 3.2.0-2 142 | - Add some BRs 143 | 144 | * Sat Feb 14 2015 Mosaab Alzoubi - 3.2.0-1 145 | - Add Thawab Server. 146 | 147 | * Sat Feb 15 2014 Mosaab Alzoubi - 3.1.1-2 148 | - Genera Revision. 149 | 150 | * Mon Jun 1 2012 Muayyad Saleh AlSadi - 3.1.1-1 151 | - port to gtk 3 152 | 153 | * Mon Nov 1 2010 Muayyad Saleh AlSadi - 3.0.10-1 154 | - update to whoosh 1.x.y 155 | 156 | * Mon Jul 26 2010 Muayyad Saleh AlSadi - 3.0.8-1 157 | - activate cancel button in import window 158 | - only reload index after new import 159 | - css: hide overflow in minisearch 160 | 161 | * Sun Jul 4 2010 Muayyad Saleh AlSadi - 3.0.7-1 162 | - update to latest stable release 163 | - activate footnotes links 164 | - opens external links with default browser 165 | - print button 166 | - add zoom buttons 167 | - auto reload after import 168 | - change search query syntax 169 | - add manual 170 | - add filter to book listing 171 | 172 | * Sun Jul 4 2010 Muayyad Saleh AlSadi - 3.0.5-1 173 | - highlight minisearch text 174 | - reload meta after import 175 | - fix some importing bugs 176 | - use connection-per-thread in core.py 177 | - static-like pages 178 | 179 | * Fri Jun 18 2010 Muayyad Saleh AlSadi - 3.0.4-1 180 | - load books from /usr/share/thawab/db/ 181 | - limit search results to 500 182 | - notfy user for non-indexed books 183 | 184 | * Thu Jun 17 2010 Muayyad Saleh AlSadi - 3.0.3-1 185 | - add missing Requires 186 | - hide mini search if not indexed 187 | 188 | * Sat Jun 12 2010 Muayyad Saleh AlSadi - 3.0.2-1 189 | - initial packing 190 | 191 | -------------------------------------------------------------------------------- /thawab-data/themes/neo/static/main.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * copyright © 2010 ojuba.org, Muayyad Saleh Alsadi 4 | * 5 | **/ 6 | var klass="class"; 7 | var animations={}, ani_c=0, init_ls=[]; 8 | var autoscroll_dir=0, autoscroll_px=5; 9 | var overlay_d; 10 | 11 | function text_keyup(e, evt, func, bool) { 12 | var charCode = (evt.which) ? evt.which : event.keyCode 13 | if (charCode == 27) { // catch ESC key and clear input 14 | e.value = ""; 15 | if (bool == true) { 16 | func("", true); 17 | }else{ 18 | func(""); 19 | } 20 | return false; 21 | } 22 | } 23 | 24 | // fake trim for IE 25 | if (!Boolean(String.prototype.trim)) { 26 | String.prototype.trim = function() { 27 | return this.replace(/^\s+/, '').replace(/\s+$/, ''); 28 | }; 29 | } 30 | 31 | function get_url_vars() { 32 | var vars = {}, i,j, e; 33 | var s= document.location.search; 34 | if (!s || s.length[0]==0 || s[0]!="?" ) return vars; 35 | var a = s.slice(1).split('&'); 36 | for(i = 0; i < a.length; ++i) { 37 | e = a[i].split("=",2); 38 | if (e && e.length==2) vars[decodeURI(e[0])] = decodeURI(e[1]); 39 | } 40 | return vars; 41 | } 42 | 43 | function animation_loop() { 44 | var i,a,fn,r; 45 | for (i in animations) { 46 | a=animations[i]; 47 | fn=a[0]; 48 | r=fn(a.slice(1)); 49 | if (r==false) delete animations[i]; 50 | } 51 | setTimeout(animation_loop, 100); 52 | } 53 | 54 | function slide_down_cb(args) { 55 | var o=args[0], h=args[1], px=args[2], cb=args[3]; 56 | var t=o.offsetHeight+px; 57 | if (t>=h) t=h; 58 | o.style.height=t+"px"; 59 | if (t==h) { 60 | --ani_c; 61 | if (cb) cb(); 62 | return false; 63 | } 64 | return true 65 | } 66 | 67 | function slide_down(o, h, px, cb) { 68 | var d; 69 | if (!h || h<0) h=o.offsetHeight; 70 | if (!px || px<0) px=h/5.0; 71 | if (px<1.0) px=1; 72 | d=o.style.display; 73 | o.style.display="none"; 74 | o.style.height=px+"px"; 75 | o.style.display=d; 76 | animations["_"+(++ani_c)]=[slide_down_cb, o, h, px, cb]; 77 | } 78 | 79 | function get_scroll_width() { 80 | var w = window.pageXOffset || document.body.scrollLeft || document.documentElement.scrollLeft; 81 | return w ? w : 0; 82 | } 83 | 84 | function get_scroll_height() { 85 | var h = window.pageYOffset || document.body.scrollTop || document.documentElement.scrollTop; 86 | return h ? h : 0; 87 | } 88 | 89 | function autoscroll_up_cb() { 90 | 91 | if (autoscroll_dir!=-1) return true; 92 | var h=get_scroll_height(),t=h-autoscroll_px; 93 | if (t<=0) {t=0; autoscroll_dir=0;} 94 | window.scroll(0,t); 95 | return true 96 | } 97 | function autoscroll_down_cb() { 98 | if (autoscroll_dir!=1) return true; 99 | var h=get_scroll_height(),t=h+autoscroll_px, hm=document.body.scrollHeight; 100 | if (t>=hm) {t=hm; autoscroll_dir=0;} 101 | window.scroll(0,t); 102 | return true 103 | } 104 | function import_script(url){ 105 | var t = document.createElement("script"); 106 | t.type="text/javascript"; 107 | t.src = url; 108 | document.body.appendChild(t); 109 | } 110 | function overlay_init() { 111 | var d = document.createElement("div"); 112 | d.id="overlay"; 113 | d.style.width=document.documentElement.scrollWidth+"px"; 114 | d.style.height=document.documentElement.scrollHeight+"px"; 115 | document.body.appendChild(d); 116 | overlay_d=d; 117 | window.onresize = resize_cb; 118 | } 119 | function resize_cb() { 120 | overlay_d.style.width=document.documentElement.scrollWidth+"px";; 121 | overlay_d.style.height=document.documentElement.scrollHeight+"px"; 122 | } 123 | 124 | function getAjax(url, q, success, failure) { 125 | if (window.XMLHttpRequest){ 126 | // code for standard browsers Firefox, Chrome, Opera, Safari, and even IE7+ 127 | xmlhttp=new XMLHttpRequest(); 128 | } else { 129 | // code for IE6, IE5 130 | xmlhttp=new ActiveXObject("Microsoft.XMLHTTP"); 131 | } 132 | xmlhttp.onreadystatechange=function() { 133 | if(xmlhttp.readyState==4) { 134 | if (xmlhttp.status==200) success(xmlhttp.responseText); 135 | else if (failure) failure(); 136 | } 137 | } 138 | s=""; 139 | for (var i in q) { s+="&"+i+"="+encodeURIComponent(q[i]); } 140 | s=url+"?"+s.slice(1); 141 | xmlhttp.open("GET",s,true); 142 | xmlhttp.send(null); 143 | } 144 | var needs_external_json=false; 145 | 146 | var fromJson = function(t) { 147 | return eval("("+t+")"); 148 | } 149 | 150 | function getJson(url, q, success, failure) { 151 | s=function(t){return success(fromJson(t));}; 152 | getAjax(url, q, s, failure); 153 | } 154 | 155 | function html_escape(s) { 156 | return s.replace("&","&").replace("<","<").replace(">",">"); 157 | } 158 | 159 | function re_escape(s) { return s.replace(/([.*+?^${}()|[\]\/\\])/g, '\\$1') } 160 | 161 | function search_entry_focus(e) { 162 | e.setAttribute(klass, "search_active"+( e.getAttribute(klass) || "" ).replace("search_active","").replace("search_inactive","")); 163 | if (e.value == "نص البحث") e.value = ""; 164 | } 165 | function search_entry_blur(e) { 166 | e.setAttribute(klass, "search_inactive"+( e.getAttribute(klass) || "" ).replace("search_active","").replace("search_inactive","")); 167 | if (e.value == "") e.value = "نص البحث"; 168 | } 169 | 170 | function rm_class(e,c) { 171 | e.setAttribute(klass, ( e.getAttribute(klass) || "" ).replace(c,"")); 172 | return false; 173 | } 174 | 175 | function init_get_by_class() { 176 | if (document.getElementsByClassName == undefined) { 177 | document.getElementsByClassName = function(className) 178 | { 179 | var hasClassName = new RegExp("(?:^|\\s)" + className + "(?:$|\\s)"); 180 | var allElements = document.getElementsByTagName("*"); 181 | var results = []; 182 | 183 | var element; 184 | for (var i = 0; (element = allElements[i]) != null; i++) { 185 | var elementClass = element.className; 186 | if (elementClass && elementClass.indexOf(className) != -1 && hasClassName.test(elementClass)) 187 | results.push(element); 188 | } 189 | 190 | return results; 191 | } 192 | } 193 | } 194 | 195 | function init() { 196 | try { 197 | if (JSON) { 198 | var t=JSON.parse('"t"'); 199 | fromJson = function(t) { 200 | return JSON.parse(t); 201 | } 202 | } else needs_external_json=true; 203 | } catch(e) { 204 | needs_external_json=true; 205 | } 206 | init_get_by_class(); 207 | if (document.body.getAttribute(klass)!='body') { 208 | klass="className"; /* hack for ie */ 209 | } 210 | setTimeout(animation_loop, 100); 211 | animations["_s_up"]=[autoscroll_up_cb]; 212 | animations["_s_dn"]=[autoscroll_down_cb]; 213 | overlay_init(); 214 | var i; 215 | for (i in init_ls) init_ls[i](); 216 | } 217 | 218 | window.onload = init; 219 | 220 | -------------------------------------------------------------------------------- /thawab-data/themes/default/static/main.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * copyright © 2010 ojuba.org, Muayyad Saleh Alsadi 4 | * 5 | **/ 6 | var klass="class"; 7 | var animations={}, ani_c=0, init_ls=[]; 8 | var autoscroll_dir=0, autoscroll_px=5; 9 | var overlay_d; 10 | 11 | function text_keyup(e, evt, func, bool) { 12 | var charCode = (evt.which) ? evt.which : event.keyCode 13 | if (charCode == 27) { // catch ESC key and clear input 14 | e.value = ""; 15 | if (bool == true) { 16 | func("", true); 17 | }else{ 18 | func(""); 19 | } 20 | return false; 21 | } 22 | } 23 | 24 | // fake trim for IE 25 | if (!Boolean(String.prototype.trim)) { 26 | String.prototype.trim = function() { 27 | return this.replace(/^\s+/, '').replace(/\s+$/, ''); 28 | }; 29 | } 30 | 31 | function get_url_vars() { 32 | var vars = {}, i,j, e; 33 | var s= document.location.search; 34 | if (!s || s.length[0]==0 || s[0]!="?" ) return vars; 35 | var a = s.slice(1).split('&'); 36 | for(i = 0; i < a.length; ++i) { 37 | e = a[i].split("=",2); 38 | if (e && e.length==2) vars[decodeURI(e[0])] = decodeURI(e[1]); 39 | } 40 | return vars; 41 | } 42 | 43 | function animation_loop() { 44 | var i,a,fn,r; 45 | for (i in animations) { 46 | a=animations[i]; 47 | fn=a[0]; 48 | r=fn(a.slice(1)); 49 | if (r==false) delete animations[i]; 50 | } 51 | setTimeout(animation_loop, 100); 52 | } 53 | 54 | function slide_down_cb(args) { 55 | var o=args[0], h=args[1], px=args[2], cb=args[3]; 56 | var t=o.offsetHeight+px; 57 | if (t>=h) t=h; 58 | o.style.height=t+"px"; 59 | if (t==h) { 60 | --ani_c; 61 | if (cb) cb(); 62 | return false; 63 | } 64 | return true 65 | } 66 | 67 | function slide_down(o, h, px, cb) { 68 | var d; 69 | if (!h || h<0) h=o.offsetHeight; 70 | if (!px || px<0) px=h/5.0; 71 | if (px<1.0) px=1; 72 | d=o.style.display; 73 | o.style.display="none"; 74 | o.style.height=px+"px"; 75 | o.style.display=d; 76 | animations["_"+(++ani_c)]=[slide_down_cb, o, h, px, cb]; 77 | } 78 | 79 | function get_scroll_width() { 80 | var w = window.pageXOffset || document.body.scrollLeft || document.documentElement.scrollLeft; 81 | return w ? w : 0; 82 | } 83 | 84 | function get_scroll_height() { 85 | var h = window.pageYOffset || document.body.scrollTop || document.documentElement.scrollTop; 86 | return h ? h : 0; 87 | } 88 | 89 | function autoscroll_up_cb() { 90 | 91 | if (autoscroll_dir!=-1) return true; 92 | var h=get_scroll_height(),t=h-autoscroll_px; 93 | if (t<=0) {t=0; autoscroll_dir=0;} 94 | window.scroll(0,t); 95 | return true 96 | } 97 | function autoscroll_down_cb() { 98 | if (autoscroll_dir!=1) return true; 99 | var h=get_scroll_height(),t=h+autoscroll_px, hm=document.body.scrollHeight; 100 | if (t>=hm) {t=hm; autoscroll_dir=0;} 101 | window.scroll(0,t); 102 | return true 103 | } 104 | function import_script(url){ 105 | var t = document.createElement("script"); 106 | t.type="text/javascript"; 107 | t.src = url; 108 | document.body.appendChild(t); 109 | } 110 | function overlay_init() { 111 | var d = document.createElement("div"); 112 | d.id="overlay"; 113 | d.style.width=document.documentElement.scrollWidth+"px"; 114 | d.style.height=document.documentElement.scrollHeight+"px"; 115 | document.body.appendChild(d); 116 | overlay_d=d; 117 | window.onresize = resize_cb; 118 | } 119 | function resize_cb() { 120 | overlay_d.style.width=document.documentElement.scrollWidth+"px";; 121 | overlay_d.style.height=document.documentElement.scrollHeight+"px"; 122 | } 123 | 124 | function getAjax(url, q, success, failure) { 125 | if (window.XMLHttpRequest){ 126 | // code for standard browsers Firefox, Chrome, Opera, Safari, and even IE7+ 127 | xmlhttp=new XMLHttpRequest(); 128 | } else { 129 | // code for IE6, IE5 130 | xmlhttp=new ActiveXObject("Microsoft.XMLHTTP"); 131 | } 132 | xmlhttp.onreadystatechange=function() { 133 | if(xmlhttp.readyState==4) { 134 | if (xmlhttp.status==200) success(xmlhttp.responseText); 135 | else if (failure) failure(); 136 | } 137 | } 138 | s=""; 139 | for (var i in q) { s+="&"+i+"="+encodeURIComponent(q[i]); } 140 | s=url+"?"+s.slice(1); 141 | xmlhttp.open("GET",s,true); 142 | xmlhttp.send(null); 143 | } 144 | var needs_external_json=false; 145 | 146 | var fromJson = function(t) { 147 | return eval("("+t+")"); 148 | } 149 | 150 | function getJson(url, q, success, failure) { 151 | s=function(t){return success(fromJson(t));}; 152 | getAjax(url, q, s, failure); 153 | } 154 | 155 | function html_escape(s) { 156 | return s.replace("&","&").replace("<","<").replace(">",">"); 157 | } 158 | 159 | function re_escape(s) { return s.replace(/([.*+?^${}()|[\]\/\\])/g, '\\$1') } 160 | 161 | function search_entry_focus(e) { 162 | e.setAttribute(klass, "search_active"+( e.getAttribute(klass) || "" ).replace("search_active","").replace("search_inactive","")); 163 | if (e.value == "نص البحث") e.value = ""; 164 | } 165 | function search_entry_blur(e) { 166 | e.setAttribute(klass, "search_inactive"+( e.getAttribute(klass) || "" ).replace("search_active","").replace("search_inactive","")); 167 | if (e.value == "") e.value = "نص البحث"; 168 | } 169 | 170 | function rm_class(e,c) { 171 | e.setAttribute(klass, ( e.getAttribute(klass) || "" ).replace(c,"")); 172 | return false; 173 | } 174 | 175 | function init_get_by_class() { 176 | if (document.getElementsByClassName == undefined) { 177 | document.getElementsByClassName = function(className) 178 | { 179 | var hasClassName = new RegExp("(?:^|\\s)" + className + "(?:$|\\s)"); 180 | var allElements = document.getElementsByTagName("*"); 181 | var results = []; 182 | 183 | var element; 184 | for (var i = 0; (element = allElements[i]) != null; i++) { 185 | var elementClass = element.className; 186 | if (elementClass && elementClass.indexOf(className) != -1 && hasClassName.test(elementClass)) 187 | results.push(element); 188 | } 189 | 190 | return results; 191 | } 192 | } 193 | } 194 | 195 | function init() { 196 | try { 197 | if (JSON) { 198 | var t=JSON.parse('"t"'); 199 | fromJson = function(t) { 200 | return JSON.parse(t); 201 | } 202 | } else needs_external_json=true; 203 | } catch(e) { 204 | needs_external_json=true; 205 | } 206 | init_get_by_class(); 207 | if (document.body.getAttribute(klass)!='body') { 208 | klass="className"; /* hack for ie */ 209 | } 210 | setTimeout(animation_loop, 100); 211 | animations["_s_up"]=[autoscroll_up_cb]; 212 | animations["_s_dn"]=[autoscroll_down_cb]; 213 | overlay_init(); 214 | var i; 215 | for (i in init_ls) init_ls[i](); 216 | } 217 | 218 | window.onload = init; 219 | 220 | -------------------------------------------------------------------------------- /Thawab/userDb.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | """ 3 | The meta handling classes of thawab 4 | Copyright © 2008-2010, Muayyad Alsadi 5 | 6 | Released under terms of Waqf Public License. 7 | This program is free software; you can redistribute it and/or modify 8 | it under the terms of the latest version Waqf Public License as 9 | published by Ojuba.org. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 14 | 15 | The Latest version of the license can be found on 16 | "http://waqf.ojuba.org/license" 17 | 18 | """ 19 | import sys, os, os.path, re, sqlite3, time, threading 20 | 21 | ################################################# 22 | 23 | USER_DB_SCHEMA = """\ 24 | CREATE TABLE "starred" ( 25 | "kitab" TEXT PRIMARY KEY, 26 | "time" FLOAT 27 | ); 28 | 29 | CREATE INDEX StarredTimeIndex on starred(time); 30 | 31 | CREATE TABLE "bookmarks" ( 32 | "kitab" TEXT, 33 | "version" TEXT, 34 | "globalOrder" INTEGER, 35 | "nodeIdNum" INTEGER, 36 | "nodeId" TEXT, 37 | "title" TEXT, 38 | "time" FLOAT, 39 | PRIMARY KEY ("kitab", "version", nodeId) 40 | ); 41 | 42 | CREATE INDEX BookmarksKitabIndex on bookmarks(kitab); 43 | CREATE INDEX BookmarksNodeIdNumIndex on bookmarks(nodeIdNum); 44 | CREATE INDEX BookmarksGlobalOrderIndex on bookmarks(globalOrder); 45 | CREATE INDEX BookmarksTimeIndex on bookmarks(time); 46 | 47 | CREATE TABLE "comments" ( 48 | "kitab" TEXT, 49 | "version" TEXT, 50 | "globalOrder" INTEGER, 51 | "nodeIdNum" INTEGER, 52 | "nodeId" TEXT, 53 | "title" TEXT, 54 | "comment" TEXT, 55 | "time" FLOAT, 56 | PRIMARY KEY ("kitab", "version", nodeId) 57 | ); 58 | 59 | CREATE INDEX CommentsKitabIndex on comments(kitab); 60 | CREATE INDEX CommentsNodeIdNumIndex on comments(nodeIdNum); 61 | CREATE INDEX CommentsGlobalOrderIndex on comments(globalOrder); 62 | CREATE INDEX CommentsTimeIndex on comments(time); 63 | 64 | """ 65 | SQL_GET_ALL_STARRED = """SELECT kitab FROM starred ORDER BY time""" 66 | SQL_GET_STARRED_TIME = """SELECT time FROM starred WHERE kitab=?""" 67 | SQL_SET_STARRED = 'INSERT OR REPLACE INTO starred (kitab, time) VALUES (?, ?)' 68 | SQL_UNSET_STARRED = 'DELETE OR IGNORE FROM starred WHERE kitab=?' 69 | 70 | # NOTE: globalOrder is used to get the right book order 71 | # NOTE: nodeIdNum is used for consistancy checking and optimization 72 | 73 | SQL_GET_ALL_BOOKMARKS = """SELECT * FROM bookmarks ORDER BY kitab""" 74 | SQL_GET_BOOKMARKED_KUTUB = """SELECT DISTINCT kitab FROM bookmarks ORDER BY kitab""" 75 | SQL_GET_KITAB_BOOKMARKS = """SELECT * FROM bookmarks WHERE kitab=? ORDER BY time""" 76 | SQL_ADD_BOOKMARK = 'INSERT OR REPLACE INTO bookmarks (kitab, version, globalOrder, nodeIdNum, nodeId, title, time) VALUES (?,?,?,?,?,?,?)' 77 | 78 | SQL_GET_ALL_COMMENTS = """SELECT * FROM comments ORDER BY kitab""" 79 | SQL_GET_COMMENTED_KUTUB = """SELECT DISTINCT kitab FROM comments ORDER BY kitab""" 80 | SQL_GET_KITAB_COMMENTS = """SELECT * FROM comments WHERE kitab=? ORDER BY time""" 81 | SQL_ADD_COMMENT = 'INSERT OR REPLACE INTO comments (kitab, version, globalOrder, nodeIdNum, nodeId, title, comment, time) VALUES (?,?,?,?,?,?,?,?)' 82 | 83 | ################################# 84 | 85 | class UserDb(object): 86 | """a class holding metadata cache""" 87 | def __init__(self, th, user_db): 88 | self.th = th 89 | self.db_fn = user_db 90 | if not os.path.exists(self.db_fn): 91 | create_new = True 92 | else: 93 | create_new = False 94 | self._cn = {} 95 | cn = self._getConnection() 96 | if create_new: 97 | cn.executescript(USER_DB_SCHEMA) 98 | cn.commit() 99 | 100 | def _getConnection(self): 101 | n = threading.current_thread().name 102 | if n in self._cn: 103 | r = self._cn[n] 104 | else: 105 | r = sqlite3.connect(self.db_fn) 106 | r.row_factory = sqlite3.Row 107 | self._cn[n] = r 108 | return r 109 | 110 | def getStarredTime(self, kitab): 111 | """ 112 | return None if not starred, can be used to check if starred 113 | """ 114 | r = self._getConnection().execute(SQL_GET_STARRED_TIME, (kitab,)).fetchone() 115 | if not r: 116 | return None 117 | return r['time'] 118 | 119 | def getStarredList(self): 120 | r = self._getConnection().execute(SQL_GET_ALL_STARRED).fetchall() 121 | return [i['kitab'] for i in r] 122 | 123 | def starKitab(self, kitab): 124 | self._getConnection().execute(SQL_SET_STARRED , (kitab, float(time.time()))) 125 | 126 | def unstarKitab(self, kitab): 127 | self._getConnection().execute(SQL_UNSET_STARRED, (kitab,)) 128 | 129 | def starKitab(self, kitab): 130 | self._getConnection().execute(SQL_SET_STARRED , (kitab, float(time.time()))) 131 | 132 | def getAllBookmarks(self): 133 | r = self._getConnection().execute(SQL_GET_ALL_BOOKMARKS).fetchall() 134 | return [dict(i) for i in r] 135 | 136 | def getBookmarkedKutub(self): 137 | r = self._getConnection().execute(SQL_GET_BOOKMARKED_KUTUB).fetchall() 138 | return [i['kitab'] for i in r] 139 | 140 | def getKitabBookmarks(self, kitab): 141 | r = self._getConnection().execute(SQL_GET_KITAB_BOOKMARKS, (kitab, )).fetchall() 142 | return [dict(i) for i in r] 143 | 144 | def addBookmark(self, kitab, version, globalOrder, nodeIdNum, nodeId, title): 145 | self._getConnection().execute(SQL_ADD_BOOKMARKS, 146 | (kitab, 147 | version, 148 | globalOrder, 149 | nodeIdNum, 150 | nodeId, 151 | title, 152 | float(time.time()) )) 153 | 154 | def getAllComments(self): 155 | r = self._getConnection().execute(SQL_GET_ALL_COMMENTS).fetchall() 156 | return [dict(i) for i in r] 157 | 158 | def getCommentedKutub(self): 159 | r = self._getConnection().execute(SQL_GET_COMMENTED_KUTUB).fetchall() 160 | return [i['kitab'] for i in r] 161 | 162 | def getKitabComments(self, kitab): 163 | r = self._getConnection().execute(SQL_GET_KITAB_COMMENTS, (kitab, )).fetchall() 164 | return [dict(i) for i in r] 165 | 166 | def addComment(self, kitab, version, globalOrder, nodeIdNum, nodeId, title, comment): 167 | self._getConnection().execute(SQL_ADD_COMMENT, 168 | (kitab, 169 | version, 170 | globalOrder, 171 | nodeIdNum, 172 | nodeId, 173 | title, 174 | comment, 175 | float(time.time()) )) 176 | 177 | 178 | -------------------------------------------------------------------------------- /po/ar.po: -------------------------------------------------------------------------------- 1 | # Translation of thawab templates to Arabic 2 | # Copyright (C) 2008-2010, ojuba.org 3 | # This file is distributed under the same license as the thawab package. 4 | # Muayyad Saleh Alsadi , 2010 5 | # 6 | msgid "" 7 | msgstr "" 8 | "Project-Id-Version: PACKAGE VERSION\n" 9 | "Report-Msgid-Bugs-To: \n" 10 | "POT-Creation-Date: 2014-02-16 04:06+0200\n" 11 | "PO-Revision-Date: 2010-06-12 19:35+0300\n" 12 | "Last-Translator: Muayyad Saleh Alsadi \n" 13 | "Language-Team: thawab@ojuba.org\n" 14 | "Language: \n" 15 | "MIME-Version: 1.0\n" 16 | "Content-Type: text/plain; charset=UTF-8\n" 17 | "Content-Transfer-Encoding: 8bit\n" 18 | 19 | #: ../thawab.desktop.in.h:1 ../Thawab/gtkUi.py:837 ../Thawab/gtkUi.py:849 20 | msgid "Thawab" 21 | msgstr "ثواب" 22 | 23 | #: ../thawab.desktop.in.h:2 24 | msgid "Electronic Arabic/Islamic Encyclopedia" 25 | msgstr "موسوعة ثواب العربية الإسلامية" 26 | 27 | #: ../Thawab/gtkUi.py:154 28 | msgid "Import Shamela .bok files" 29 | msgstr "استيراد ملفات bok من الشاملة" 30 | 31 | #: ../Thawab/gtkUi.py:216 32 | msgid "Advanced options" 33 | msgstr "خيارات متقدمة" 34 | 35 | #: ../Thawab/gtkUi.py:221 36 | msgid "Performance tuning:" 37 | msgstr "معايرة الأداء" 38 | 39 | #: ../Thawab/gtkUi.py:228 40 | msgid "in memory" 41 | msgstr "في الذاكرة" 42 | 43 | #: ../Thawab/gtkUi.py:229 44 | msgid "faster but consumes more memory and harder to debug." 45 | msgstr "أسرع لكنها تستهلك الكثير من الذاكرة وصعبة التمحيص" 46 | 47 | #: ../Thawab/gtkUi.py:238 48 | msgid "Release Major:" 49 | msgstr "الإصدار الأكبر:" 50 | 51 | #: ../Thawab/gtkUi.py:243 52 | msgid "Release Minor:" 53 | msgstr "الإصدار الأصغر:" 54 | 55 | #: ../Thawab/gtkUi.py:252 ../Thawab/gtkUi.py:278 56 | msgid "Prefix:" 57 | msgstr "السابقة:" 58 | 59 | #: ../Thawab/gtkUi.py:258 ../Thawab/gtkUi.py:284 60 | msgid "Suffix:" 61 | msgstr "اللاحقة:" 62 | 63 | #: ../Thawab/gtkUi.py:264 64 | msgid "only at line start" 65 | msgstr "فقط على بداية الكلمة" 66 | 67 | #: ../Thawab/gtkUi.py:268 ../Thawab/gtkUi.py:291 68 | msgid "in between spaces:" 69 | msgstr "المسافات البينية:" 70 | 71 | #: ../Thawab/gtkUi.py:269 ../Thawab/gtkUi.py:292 72 | msgid "no spaces" 73 | msgstr "دون مسافات" 74 | 75 | #: ../Thawab/gtkUi.py:270 ../Thawab/gtkUi.py:293 76 | msgid "optional white-space" 77 | msgstr "مسافة واحدة اختيارية" 78 | 79 | #: ../Thawab/gtkUi.py:271 ../Thawab/gtkUi.py:294 80 | msgid "optional white-spaces" 81 | msgstr "مسافات اختيارية" 82 | 83 | #: ../Thawab/gtkUi.py:344 ../Thawab/gtkUi.py:357 84 | msgid "working ..." 85 | msgstr "يجري العمل ..." 86 | 87 | #. canceled 88 | #: ../Thawab/gtkUi.py:413 ../Thawab/gtkUi.py:414 ../Thawab/gtkUi.py:432 89 | #: ../Thawab/gtkUi.py:433 90 | msgid "Canceled" 91 | msgstr "ملغي" 92 | 93 | #. windows can't move an opened file 94 | #. FIXME: close ki in a clean way so the above code works in windows 95 | #: ../Thawab/gtkUi.py:448 ../Thawab/gtkUi.py:461 ../Thawab/gtkUi.py:817 96 | #: ../Thawab/gtkUi.py:828 97 | msgid "Done" 98 | msgstr "تم" 99 | 100 | #: ../Thawab/gtkUi.py:462 101 | msgid "Convert Book, Done" 102 | msgstr "" 103 | 104 | #: ../Thawab/gtkUi.py:482 105 | msgid "Select files to import" 106 | msgstr "اختر الملفات كي تستورد" 107 | 108 | #: ../Thawab/gtkUi.py:489 109 | msgid "Shamela BOK files" 110 | msgstr "ملفات BOK الخاصة بالشاملة" 111 | 112 | #: ../Thawab/gtkUi.py:667 113 | msgid "Open Link in New Tab" 114 | msgstr "فتح الرابط في لسان جديد" 115 | 116 | #: ../Thawab/gtkUi.py:716 117 | msgid "Manage search index" 118 | msgstr "إدارة فهارس البحث" 119 | 120 | #: ../Thawab/gtkUi.py:727 121 | msgid "Queue new books" 122 | msgstr "دفع الكتب الجديدة" 123 | 124 | #: ../Thawab/gtkUi.py:744 125 | msgid "Indexing jobs canceled" 126 | msgstr "تم إلغاء مهمات الفهرسة" 127 | 128 | #: ../Thawab/gtkUi.py:761 129 | #, python-format 130 | msgid "Indexing ... (%d left)" 131 | msgstr "يجري الفهرسة ... (بقي %d)" 132 | 133 | #. Gtk.main_iteration_do(True) 134 | #: ../Thawab/gtkUi.py:766 135 | msgid "No indexing jobs left" 136 | msgstr "لم يتبق أي مهمات فهرسة" 137 | 138 | #: ../Thawab/gtkUi.py:769 139 | #, python-format 140 | msgid "Indexing %d jobs, Done" 141 | msgstr "إنتهى %d مهمات فهرسة" 142 | 143 | #: ../Thawab/gtkUi.py:776 144 | msgid "Misc. Fixes" 145 | msgstr "إصلاحات متنوعة" 146 | 147 | #: ../Thawab/gtkUi.py:788 148 | msgid "" 149 | "Those procedures are to be used in case of " 150 | "emergency only,\n" 151 | "for example to recover power failure." 152 | msgstr "" 153 | "تستخدم هذه الإجراءات في الحالات الطارئة فقط,\n" 154 | "مثل عطل ناتج عن انقطاع الطاقة الكهربائية." 155 | 156 | #: ../Thawab/gtkUi.py:793 157 | msgid "remove search index" 158 | msgstr "حذف فهرس البحث" 159 | 160 | #: ../Thawab/gtkUi.py:794 161 | msgid "you will need to re-index all books" 162 | msgstr "ستحتاج للقيام بإعادة فعرسة كل الكتب" 163 | 164 | #: ../Thawab/gtkUi.py:799 165 | msgid "remove meta data cache to generate a fresh one" 166 | msgstr "إزالة نسخة الميتا الخبيئة وتوليد واحدة جديدة" 167 | 168 | #: ../Thawab/gtkUi.py:800 169 | msgid "instead of incremental meta data gathering" 170 | msgstr "عوضا عن جمعها تزايديا" 171 | 172 | #: ../Thawab/gtkUi.py:810 173 | msgid "" 174 | "You will need to recreate search index in-order to search again.\n" 175 | "Are you sure you want to remove search index?" 176 | msgstr "" 177 | "يتوجب عليك إعادة توليد فهارس البحث حتى يعمل البحث مجددا.\n" 178 | "هل أنت متأكد من رغبتك في حذف فهارس البحث؟" 179 | 180 | #: ../Thawab/gtkUi.py:815 181 | #, python-format 182 | msgid "unable to remove folder [%s]" 183 | msgstr "غير قادر على حذف المجلد [%s]" 184 | 185 | #: ../Thawab/gtkUi.py:820 186 | msgid "Are you sure you want to remove search meta data cache?" 187 | msgstr "هل أنت متاكد من رغبتك في إزالة خبيئة الميتا؟" 188 | 189 | #: ../Thawab/gtkUi.py:825 190 | #, python-format 191 | msgid "unable to remove file [%s]" 192 | msgstr "غير قادر على إزالة الملف [%s]" 193 | 194 | #: ../Thawab/gtkUi.py:860 195 | msgid "Open a new tab" 196 | msgstr "فتح لسان جديد" 197 | 198 | #: ../Thawab/gtkUi.py:868 199 | msgid "Import" 200 | msgstr "استيراد" 201 | 202 | #: ../Thawab/gtkUi.py:869 203 | msgid "Import .bok files" 204 | msgstr "استيراد ملف bok" 205 | 206 | #: ../Thawab/gtkUi.py:875 207 | msgid "Index" 208 | msgstr "فهرسة" 209 | 210 | #: ../Thawab/gtkUi.py:877 211 | msgid "Create search index" 212 | msgstr "إنشاء فهرس البحث" 213 | 214 | #: ../Thawab/gtkUi.py:885 215 | msgid "Zoom in" 216 | msgstr "تكبير" 217 | 218 | #: ../Thawab/gtkUi.py:890 219 | msgid "Makes things appear bigger" 220 | msgstr "تجعل الأشياء تبدو أكبر" 221 | 222 | #: ../Thawab/gtkUi.py:896 223 | msgid "Zoom out" 224 | msgstr "تصغيير" 225 | 226 | #: ../Thawab/gtkUi.py:899 227 | msgid "Makes things appear smaller" 228 | msgstr "تجعل الأشياء تبدو أصغر" 229 | 230 | #: ../Thawab/gtkUi.py:905 231 | msgid "1:1 Zoom" 232 | msgstr "تكبير 1:1" 233 | 234 | #: ../Thawab/gtkUi.py:908 235 | msgid "Restore original zoom factor" 236 | msgstr "تعيد التكبير الأصلي" 237 | 238 | #: ../Thawab/gtkUi.py:916 239 | msgid "Fixes" 240 | msgstr "إصلاحات" 241 | 242 | #: ../Thawab/gtkUi.py:918 243 | msgid "Misc Fixes" 244 | msgstr "إصلاحات متنوعة" 245 | 246 | #: ../Thawab/gtkUi.py:926 247 | msgid "Help" 248 | msgstr "مساعدة" 249 | 250 | #: ../Thawab/gtkUi.py:927 251 | msgid "Show user manual" 252 | msgstr "إظهار دليل المستخدم" 253 | -------------------------------------------------------------------------------- /po/de.po: -------------------------------------------------------------------------------- 1 | # Translation of thawab templates to Arabic 2 | # Copyright (C) 2008-2010, ojuba.org 3 | # This file is distributed under the same license as the thawab package. 4 | # cegerxwin , 2010 5 | # 6 | msgid "" 7 | msgstr "" 8 | "Project-Id-Version: PACKAGE VERSION\n" 9 | "Report-Msgid-Bugs-To: \n" 10 | "POT-Creation-Date: 2014-02-16 04:06+0200\n" 11 | "PO-Revision-Date: 2010-08-27 23:53+0100\n" 12 | "Last-Translator: cegerxwin \n" 13 | "Language-Team: LANGUAGE \n" 14 | "Language: \n" 15 | "MIME-Version: 1.0\n" 16 | "Content-Type: text/plain; charset=utf-8\n" 17 | "Content-Transfer-Encoding: 8bit\n" 18 | 19 | #: ../thawab.desktop.in.h:1 ../Thawab/gtkUi.py:837 ../Thawab/gtkUi.py:849 20 | msgid "Thawab" 21 | msgstr "Thawab" 22 | 23 | #: ../thawab.desktop.in.h:2 24 | msgid "Electronic Arabic/Islamic Encyclopedia" 25 | msgstr "Elektronische Arabisch/Islamische Ezyklopädie" 26 | 27 | #: ../Thawab/gtkUi.py:154 28 | msgid "Import Shamela .bok files" 29 | msgstr "Importiere Shamela .bok Dateien" 30 | 31 | #: ../Thawab/gtkUi.py:216 32 | msgid "Advanced options" 33 | msgstr "Erweiterte Optionen:" 34 | 35 | #: ../Thawab/gtkUi.py:221 36 | msgid "Performance tuning:" 37 | msgstr "Geschwindigkeits Feineinstellungen" 38 | 39 | #: ../Thawab/gtkUi.py:228 40 | msgid "in memory" 41 | msgstr "im Speicher" 42 | 43 | #: ../Thawab/gtkUi.py:229 44 | msgid "faster but consumes more memory and harder to debug." 45 | msgstr "" 46 | "schneller aber verbraucht mehr Speicherplatz und die Fehlersuche ist " 47 | "aufwändiger" 48 | 49 | #: ../Thawab/gtkUi.py:238 50 | msgid "Release Major:" 51 | msgstr "Hauptveröffentlichung:" 52 | 53 | #: ../Thawab/gtkUi.py:243 54 | msgid "Release Minor:" 55 | msgstr "Kleinere Veröffentlichungen:" 56 | 57 | #: ../Thawab/gtkUi.py:252 ../Thawab/gtkUi.py:278 58 | msgid "Prefix:" 59 | msgstr "Präfix:" 60 | 61 | #: ../Thawab/gtkUi.py:258 ../Thawab/gtkUi.py:284 62 | msgid "Suffix:" 63 | msgstr "Suffix:" 64 | 65 | #: ../Thawab/gtkUi.py:264 66 | msgid "only at line start" 67 | msgstr "nur am Zeilenanfang" 68 | 69 | #: ../Thawab/gtkUi.py:268 ../Thawab/gtkUi.py:291 70 | msgid "in between spaces:" 71 | msgstr "in den Zwischenräumen:" 72 | 73 | #: ../Thawab/gtkUi.py:269 ../Thawab/gtkUi.py:292 74 | msgid "no spaces" 75 | msgstr "keine Zwischenräume" 76 | 77 | #: ../Thawab/gtkUi.py:270 ../Thawab/gtkUi.py:293 78 | msgid "optional white-space" 79 | msgstr "optional Leerzeile" 80 | 81 | #: ../Thawab/gtkUi.py:271 ../Thawab/gtkUi.py:294 82 | msgid "optional white-spaces" 83 | msgstr "optional Leerzeilen" 84 | 85 | #: ../Thawab/gtkUi.py:344 ../Thawab/gtkUi.py:357 86 | msgid "working ..." 87 | msgstr "läuft..." 88 | 89 | #. canceled 90 | #: ../Thawab/gtkUi.py:413 ../Thawab/gtkUi.py:414 ../Thawab/gtkUi.py:432 91 | #: ../Thawab/gtkUi.py:433 92 | msgid "Canceled" 93 | msgstr "Abgebrochen" 94 | 95 | #. windows can't move an opened file 96 | #. FIXME: close ki in a clean way so the above code works in windows 97 | #: ../Thawab/gtkUi.py:448 ../Thawab/gtkUi.py:461 ../Thawab/gtkUi.py:817 98 | #: ../Thawab/gtkUi.py:828 99 | msgid "Done" 100 | msgstr "Erledigt" 101 | 102 | #: ../Thawab/gtkUi.py:462 103 | msgid "Convert Book, Done" 104 | msgstr "" 105 | 106 | #: ../Thawab/gtkUi.py:482 107 | msgid "Select files to import" 108 | msgstr "Wähle zu importierende Dateien aus" 109 | 110 | #: ../Thawab/gtkUi.py:489 111 | msgid "Shamela BOK files" 112 | msgstr "Shamela BOK Dateien" 113 | 114 | #: ../Thawab/gtkUi.py:667 115 | msgid "Open Link in New Tab" 116 | msgstr "Öffne Link im neuen Tab" 117 | 118 | #: ../Thawab/gtkUi.py:716 119 | msgid "Manage search index" 120 | msgstr "Verwalte Suchindex" 121 | 122 | #: ../Thawab/gtkUi.py:727 123 | msgid "Queue new books" 124 | msgstr "Neue Bücher in der Warteschlange aufstellen" 125 | 126 | #: ../Thawab/gtkUi.py:744 127 | #, fuzzy 128 | msgid "Indexing jobs canceled" 129 | msgstr "Keine Indizierungsaufgaben mehr vorhanden" 130 | 131 | #: ../Thawab/gtkUi.py:761 132 | #, python-format 133 | msgid "Indexing ... (%d left)" 134 | msgstr "Indizierung...(%d left)" 135 | 136 | #. Gtk.main_iteration_do(True) 137 | #: ../Thawab/gtkUi.py:766 138 | #, fuzzy 139 | msgid "No indexing jobs left" 140 | msgstr "Keine Indizierungsaufgaben mehr vorhanden" 141 | 142 | #: ../Thawab/gtkUi.py:769 143 | #, fuzzy, python-format 144 | msgid "Indexing %d jobs, Done" 145 | msgstr "Keine Indizierungsaufgaben mehr vorhanden" 146 | 147 | #: ../Thawab/gtkUi.py:776 148 | msgid "Misc. Fixes" 149 | msgstr "Misc. Fixes" 150 | 151 | #: ../Thawab/gtkUi.py:788 152 | #, fuzzy 153 | msgid "" 154 | "Those procedures are to be used in case of " 155 | "emergency only,\n" 156 | "for example to recover power failure." 157 | msgstr "" 158 | "Diese Prozedur sollte nur in Notfällen benutzt " 159 | "werden,\n" 160 | " z.B. um nach einem Stromausfall die Daten wiederherzustellen." 161 | 162 | #: ../Thawab/gtkUi.py:793 163 | msgid "remove search index" 164 | msgstr "entferne Suchindex" 165 | 166 | #: ../Thawab/gtkUi.py:794 167 | msgid "you will need to re-index all books" 168 | msgstr "Neu-Indizierung aller Bücher notwendig" 169 | 170 | #: ../Thawab/gtkUi.py:799 171 | msgid "remove meta data cache to generate a fresh one" 172 | msgstr "entferne meta Daten Speicher um eine neue zu generieren" 173 | 174 | #: ../Thawab/gtkUi.py:800 175 | msgid "instead of incremental meta data gathering" 176 | msgstr "anstatt der inkrementellen meta Daten ansammlung" 177 | 178 | #: ../Thawab/gtkUi.py:810 179 | msgid "" 180 | "You will need to recreate search index in-order to search again.\n" 181 | "Are you sure you want to remove search index?" 182 | msgstr "" 183 | "Du musst den Suchindex noch einmal um danach eine Suche durchführen zu " 184 | "können.\n" 185 | "Bist du sicher, das du den Suchindex entfernen möchtest?" 186 | 187 | #: ../Thawab/gtkUi.py:815 188 | #, python-format 189 | msgid "unable to remove folder [%s]" 190 | msgstr "Fehler beim entfernen des Ordners [%s]" 191 | 192 | #: ../Thawab/gtkUi.py:820 193 | msgid "Are you sure you want to remove search meta data cache?" 194 | msgstr "Sind sie sicher, das sie die Suchmetadaten Speicher entfernen möchten?" 195 | 196 | #: ../Thawab/gtkUi.py:825 197 | #, python-format 198 | msgid "unable to remove file [%s]" 199 | msgstr "Fehler beim entfernen der Datei [%s]" 200 | 201 | #: ../Thawab/gtkUi.py:860 202 | msgid "Open a new tab" 203 | msgstr "Öffne im neuen Tab" 204 | 205 | #: ../Thawab/gtkUi.py:868 206 | msgid "Import" 207 | msgstr "Importieren" 208 | 209 | #: ../Thawab/gtkUi.py:869 210 | msgid "Import .bok files" 211 | msgstr "Importiere .bok Dateien" 212 | 213 | #: ../Thawab/gtkUi.py:875 214 | msgid "Index" 215 | msgstr "Index" 216 | 217 | #: ../Thawab/gtkUi.py:877 218 | msgid "Create search index" 219 | msgstr "Erzeuge Suchindex" 220 | 221 | #: ../Thawab/gtkUi.py:885 222 | msgid "Zoom in" 223 | msgstr "Einzoomen" 224 | 225 | #: ../Thawab/gtkUi.py:890 226 | msgid "Makes things appear bigger" 227 | msgstr "Macht das Dinge größer wirken" 228 | 229 | #: ../Thawab/gtkUi.py:896 230 | msgid "Zoom out" 231 | msgstr "Auszoomen" 232 | 233 | #: ../Thawab/gtkUi.py:899 234 | msgid "Makes things appear smaller" 235 | msgstr "Macht das Dinge kleiner wirken" 236 | 237 | #: ../Thawab/gtkUi.py:905 238 | msgid "1:1 Zoom" 239 | msgstr "1:1 Zoom" 240 | 241 | #: ../Thawab/gtkUi.py:908 242 | #, fuzzy 243 | msgid "Restore original zoom factor" 244 | msgstr "Original Zoomgröße wiederherstellen" 245 | 246 | #: ../Thawab/gtkUi.py:916 247 | msgid "Fixes" 248 | msgstr "Fixes" 249 | 250 | #: ../Thawab/gtkUi.py:918 251 | msgid "Misc Fixes" 252 | msgstr "Misc Fixes" 253 | 254 | #: ../Thawab/gtkUi.py:926 255 | msgid "Help" 256 | msgstr "Hilfe" 257 | 258 | #: ../Thawab/gtkUi.py:927 259 | msgid "Show user manual" 260 | msgstr "Zeige Benutzerhandbuch" 261 | -------------------------------------------------------------------------------- /thawab-server: -------------------------------------------------------------------------------- 1 | #! /usr/bin/python3 2 | # -*- coding: UTF-8 -*- 3 | import sys, os, time, atexit, signal, shutil, tempfile, sqlite3 4 | from Thawab.gtkUi import launchServer, onlyterminal 5 | from Thawab.shamelaUtils import ShamelaSqlite, shamelaImport 6 | class ThawabServer: 7 | def __init__(self, pidfile): 8 | self.pidfile = pidfile 9 | def tprint(self, message, noend=False): 10 | if not noend: 11 | sys.stderr.write(message+"\n") 12 | else: 13 | sys.stderr.write(message+"\r") 14 | 15 | def daemonize(self): 16 | try: 17 | pid = os.fork() 18 | if pid > 0: 19 | # exit first parent 20 | sys.exit(0) 21 | except OSError as err: 22 | self.tprint('fork #1 failed: {0}'.format(err)) 23 | sys.exit(1) 24 | 25 | # decouple from parent environment 26 | #os.chdir('/') 27 | os.setsid() 28 | os.umask(0) 29 | 30 | # do second fork 31 | try: 32 | pid = os.fork() 33 | if pid > 0: 34 | 35 | # exit from second parent 36 | sys.exit(0) 37 | except OSError as err: 38 | self.tprint('fork #2 failed: {0}'.format(err)) 39 | sys.exit(1) 40 | 41 | # redirect standard file descriptors 42 | #sys.stdout.flush() 43 | #sys.stderr.flush() 44 | #si = open(os.devnull, 'r') 45 | #so = open(os.devnull, 'a+') 46 | #se = open(os.devnull, 'a+') 47 | 48 | #os.dup2(si.fileno(), sys.stdin.fileno()) 49 | #os.dup2(so.fileno(), sys.stdout.fileno()) 50 | #os.dup2(se.fileno(), sys.stderr.fileno()) 51 | 52 | # write pidfile 53 | atexit.register(self.delpid) 54 | 55 | pid = str(os.getpid()) 56 | 57 | with open(self.pidfile,'w+') as f: 58 | f.write(pid + '\n') 59 | 60 | 61 | def delpid(self): 62 | os.remove(self.pidfile) 63 | 64 | def check_running(self): 65 | # Check for a pidfile to see if the daemon already runs 66 | try: 67 | with open(self.pidfile,'r') as pf: 68 | r = int(pf.read().strip()) 69 | c = os.system("ps o cmd= {} > /dev/null".format(r)) 70 | #print ("ps o cmd= {}".format(r),c) 71 | if not c: 72 | return r 73 | return None 74 | except IOError: 75 | return None 76 | 77 | 78 | 79 | def start(self): 80 | """Start the daemon.""" 81 | 82 | if self.check_running(): 83 | message = "pidfile {0} already exist. " + \ 84 | "Server is already running?\n" 85 | self.tprint(message.format(self.pidfile)) 86 | sys.exit(1) 87 | # Start the daemon 88 | self.daemonize() 89 | print("** Thawab server is running on: 127.0.0.1:18080") 90 | self.run() 91 | 92 | def stop(self): 93 | """Stop the daemon.""" 94 | 95 | # Get the pid from the pidfile 96 | pid = self.check_running() 97 | 98 | if not pid: 99 | message = "pidfile {0} does not exist. " + \ 100 | "Server is not running?\n" 101 | self.tprint(message.format(self.pidfile)) 102 | return # not an error in a restart 103 | 104 | # Try killing the daemon process 105 | try: 106 | while 1: 107 | os.kill(pid, signal.SIGTERM) 108 | time.sleep(0.1) 109 | except OSError as err: 110 | e = str(err.args) 111 | if e.find("No such process") > 0: 112 | if os.path.exists(self.pidfile): 113 | os.remove(self.pidfile) 114 | else: 115 | print (str(err.args)) 116 | sys.exit(1) 117 | print("** Thawab server stopped") 118 | 119 | def restart(self): 120 | """Restart the daemon.""" 121 | if self.check_running(): 122 | self.stop() 123 | self.start() 124 | 125 | def run(self, silent=False): 126 | self.th, self.port, self.server = onlyterminal() 127 | if not silent: 128 | self.server.serve_forever() 129 | 130 | def clean_run(self): 131 | if self.check_running(): 132 | self.tprint("Stopping the running server") 133 | self.stop() 134 | self.run(True) 135 | 136 | def test(self): 137 | self.tprint("server started successfully") 138 | 139 | def reindex(self): 140 | self.clean_run() 141 | self.th.asyncIndexer.queueIndexNew() 142 | if not self.th.asyncIndexer.started: 143 | self.th.asyncIndexer.start() 144 | jj = j = self.th.asyncIndexer.jobs() 145 | while (j > 0 ): 146 | self.tprint("Indexing ... (%d left)" % j,True) 147 | j = self.th.asyncIndexer.jobs() 148 | self.tprint("No indexing jobs left") 149 | if j <= 0 and jj > 0: 150 | self.tprint("Indexing %d jobs, Done" % jj) 151 | self.server.server_close() 152 | 153 | def remove_index(self): 154 | self.clean_run() 155 | self.tprint("You will need to recreate search index in-order to search again.") 156 | p = os.path.join(self.th.prefixes[0], 'index') 157 | try: 158 | shutil.rmtree(p) 159 | except OSError: 160 | self.tprint("unable to remove folder [%s]" % p) 161 | else: 162 | self.tprint("Done") 163 | self.server.server_close() 164 | 165 | def remove_meta(self): 166 | self.clean_run() 167 | p = os.path.join(self.th.prefixes[0], 'cache', 'meta.db') 168 | try: 169 | os.unlink(p) 170 | except OSError: 171 | self.tprint("unable to remove file [%s]" % p) 172 | else: 173 | self.th.reconstructMetaIndexedFlags() 174 | self.tprint("Done") 175 | 176 | def progress_cb(self, msg, p, *d, **kw): 177 | self.tprint(" ** progress: [%g%% completed] %s" % (p, msg)) 178 | 179 | def importbok(self, bok): 180 | self.clean_run() 181 | fh, db_fn = tempfile.mkstemp(suffix = '.sqlite', prefix = 'th_shamela_tmp') 182 | f = open(db_fn, "w") 183 | f.truncate(0) 184 | f.close() 185 | cn = sqlite3.connect(db_fn, isolation_level = None) 186 | try: 187 | sh = ShamelaSqlite(bok,cn,0,0, self.progress_cb) 188 | except TypeError: 189 | self.tprint("not a shamela file") 190 | self.server.server_close() 191 | return 192 | except OSError: 193 | self.tprint("mdbtools is not installed") 194 | self.server.server_close() 195 | return 196 | 197 | if not sh.toSqlite(): 198 | self.server.server_close() 199 | return 200 | 201 | ids = sh.getBookIds() 202 | 203 | for j, bkid in enumerate(ids): 204 | ki = self.th.mktemp() 205 | c = ki.seek(-1,-1) 206 | 207 | m = shamelaImport(c, 208 | sh, 209 | bkid) 210 | c.flush() 211 | t_fn = os.path.join(self.th.prefixes[0], 212 | 'db', 213 | u"".join((m['kitab'] + \ 214 | u"-" + \ 215 | m['version'] + \ 216 | u'.ki',))) 217 | try: 218 | shutil.move(ki.uri, t_fn) 219 | except OSError: 220 | self.tprint("unable to move converted file.") # windows can't move an opened file 221 | if db_fn and os.path.exists(db_fn): 222 | try: 223 | os.unlink(db_fn) 224 | except OSError: 225 | pass 226 | self.th.loadMeta() 227 | self.tprint("Done") 228 | self.server.server_close() 229 | if __name__ == "__main__": 230 | 231 | daemon = ThawabServer('/tmp/thawab-server.pid') 232 | if len(sys.argv) >= 2: 233 | if 'start' == sys.argv[1]: 234 | daemon.start() 235 | elif 'stop' == sys.argv[1]: 236 | daemon.stop() 237 | elif 'restart' == sys.argv[1]: 238 | daemon.restart() 239 | elif 'reindex' == sys.argv[1]: 240 | daemon.reindex() 241 | elif 'check' == sys.argv[1]: 242 | r = daemon.check_running() 243 | if r: 244 | print("** Thawab server is running on: 127.0.0.1:18080, pid: {}.".format(r)) 245 | else: 246 | print("** Thawab server is not running.") 247 | elif 'fix' == sys.argv[1]: 248 | if sys.argv[2] == 'index': 249 | daemon.remove_index() 250 | elif sys.argv[2] == 'meta': 251 | daemon.remove_meta() 252 | elif 'importbok' == sys.argv[1] and len(sys.argv) >= 3: 253 | daemon.importbok(sys.argv[2]) 254 | else: 255 | print ("Unknown command") 256 | sys.exit(2) 257 | sys.exit(0) 258 | else: 259 | print ('''Thawab Server\nusage: thawab-server [command] [file(s)] \nCommands: 260 | start starts the server 261 | stop stops the server 262 | restart restarts the server 263 | check check server status 264 | reindex queues new books 265 | fix index removes search index 266 | fix meta removes meta data cache to generate a fresh one 267 | importbok [file path] imports Shamela .bok file''') 268 | 269 | sys.exit(2) 270 | -------------------------------------------------------------------------------- /thawab-data/themes/default/static/main.css: -------------------------------------------------------------------------------- 1 | .clear {clear:both; padding-bottom:2px;} 2 | a img { border: none; } 3 | a[href] { text-decoration: none; color:#150;} 4 | a[href]:hover { text-decoration: underline; } 5 | a[target] { background: transparent url('img/external.gif') top left no-repeat; padding-left:12px;} 6 | 7 | body { 8 | font-family: "amiri", "Liberation Sans", "KacstOne", "Simplified Naskh", "KFGQPC Uthman Taha Naskh", "ArabeyesQr", "Times New Roman", "sans", "Sans"; 9 | background:#000; 10 | margin:0;padding:0;border:0; 11 | } 12 | h1 13 | { 14 | margin-top:50px; 15 | padding-top:5px; 16 | padding-bottom:5px; 17 | padding-left:4px; 18 | color:#303030; 19 | border:1px solid #d4d4d4; 20 | background-color:#eee; 21 | background:#fff url('img/bg-l.png') top left repeat-x; 22 | background-repeat:repeat-x; 23 | background-position:0px -50px; 24 | clear:both; 25 | } 26 | h2 27 | { 28 | margin-top:20px; 29 | padding-top:5px; 30 | padding-bottom:5px; 31 | padding-left:4px; 32 | color:#303030; 33 | border:1px solid #d4d4d4; 34 | background-color:#eee; 35 | background:#fff url('img/bg-l.png') top left repeat-x; 36 | background-repeat:repeat-x; 37 | background-position:0px -50px; 38 | clear:both; 39 | } 40 | #async_tips_div { 41 | display:none; 42 | position: absolute; 43 | background-color:#ffe; 44 | left:20%; 45 | margin:0; padding:3px 10px; 46 | border:2px solid #aa7; 47 | border:2px solid rgba(170, 170, 119, 0.6); 48 | } 49 | .match { background-color:#acf; } 50 | .term0 { background-color:#acf; } 51 | .term1 { background-color:#fca; } 52 | .term2 { background-color:#cfa; } 53 | .term3 { background-color:#fac; } 54 | .term4 { background-color:#afc; } 55 | .term5 { background-color:#caf; } 56 | 57 | 58 | .quran { 59 | font-family: "amiri-quran", "amiri", "Simplified Naskh", "me_quran", "KFGQPC Uthman Taha Naskh", "ArabeyesQr", "Times New Roman", "Serif"; 60 | font-size: 150%; 61 | color: #240; 62 | background-color:#ffe; 63 | } 64 | 65 | blockquote { 66 | border:3px dotted #aaa; 67 | background:#ddd url('img/quote.gif') top left no-repeat; 68 | background-color:rgba(190, 230, 190, 0.4); 69 | padding:5px 50px; 70 | } 71 | 72 | input { 73 | font-family: "amiri", "Simplified Naskh", "me_quran", "KFGQPC Uthman Taha Naskh", "ArabeyesQr", "Times New Roman", "Serif"; 74 | padding: 2px 30px; 75 | border-radius: 17px; 76 | font-size: 12pt; 77 | color: rgb(0, 0, 0); 78 | border: 2px solid rgba(245,245,245,0.2); 79 | background:#ddd url('img/search.gif') center right no-repeat; 80 | background-gradient-start: rgba(5,5,6,0.1); 81 | background-gradient-end: rgba(254,254,254,0.1); 82 | background-gradient-direction: vertical; 83 | selected-color: black; 84 | caret-color: rgb(128, 128, 128); 85 | caret-size: 1px; 86 | width: 30%; 87 | transition-duration: 300; 88 | box-shadow: inset 0px 2px 4px rgba(0,0,0,0.6); 89 | outline: none; 90 | } 91 | input:focus, 92 | input:hover { 93 | border: 2px solid rgb(136,138,133); 94 | background-gradient-start: rgb(200,200,200); 95 | background-gradient-end: white; 96 | background-gradient-direction: vertical; 97 | outline: none; 98 | } 99 | input:hover { 100 | transition-duration: 300; 101 | } 102 | #loading { 103 | display:none; 104 | background:transparent url('img/loading.gif') center center no-repeat; 105 | position:absolute; 106 | top:16px; 107 | left:8px; 108 | margin:20px auto; 109 | width:32px; 110 | height:32px; 111 | } 112 | #logo { 113 | background:transparent; 114 | position:absolute; 115 | top:8px; 116 | right:8px; 117 | height:48px; 118 | font-size:24px; 119 | line-height: 100%; 120 | text-decoration: none; 121 | vertical-align:middle; 122 | margin:0;padding:0;border:0; 123 | } 124 | #logo img {border:0; vertical-align: middle;} 125 | #absnav { 126 | display:none; 127 | background:#000 url('img/bg-d.png') top left repeat-x; 128 | color:#fff; 129 | position:absolute; 130 | top:46px; 131 | left:64px; 132 | width:200px; 133 | height:40px; 134 | margin:0; 135 | padding:0; 136 | border:2px solid #aaa; 137 | } 138 | #absnav2 { 139 | background:#000 url('img/bg-d.png') top left repeat-x; 140 | color:#fff; 141 | position:absolute; 142 | top:46px; 143 | right:64px; 144 | width:160px; 145 | height:40px; 146 | margin:0; 147 | padding:0; 148 | border:2px solid #aaa; 149 | } 150 | #absnav3 { 151 | background:#000 url('img/bg-d.png') top left repeat-x; 152 | color:#fff; 153 | position:absolute; 154 | top:128px; 155 | left:10px; 156 | width:32px; 157 | height:110px; 158 | margin:0; 159 | padding:0; 160 | border:2px solid #aaa; 161 | } 162 | #absnav2 img, #absnav3 img { 163 | padding:8px 5px; 164 | border:0; 165 | margin:8px auto 8px auto; 166 | } 167 | 168 | #results { 169 | background:#838383; 170 | color:#fff; 171 | overflow:hidden; 172 | position:absolute; 173 | top:4px; 174 | left:64px; 175 | width:500px; 176 | height:22px; 177 | margin:0;padding:0; 178 | border:2px solid #aaa; 179 | border-top:0; 180 | } 181 | #nominisearch { 182 | display:none; 183 | text-align:right; 184 | width:200px; 185 | margin:0;padding:0; 186 | } 187 | 188 | #minisearch { 189 | background:#000 url('img/bg-d.png') top left repeat-x; 190 | color:#fff; 191 | overflow:hidden; 192 | position:absolute; 193 | top:0px; 194 | left:92px; 195 | width:220px; 196 | height:32px; 197 | margin:0;padding:0; 198 | border:2px solid #777; 199 | } 200 | #minisearch form { 201 | margin:0;border:0;padding:0; 202 | } 203 | #minisearch input { 204 | #background:#ddd url('img/search.gif') center right no-repeat; 205 | width:70%; 206 | height:24px; 207 | border:2px solid #aaa; 208 | } 209 | 210 | #searchHelpDiv { display:none;} 211 | 212 | #searchHelp { 213 | background:#ffffa0; 214 | padding:16px; 215 | -webkit-border-radius:8px; 216 | -moz-border-radius:8px; 217 | border-radius:8px; 218 | } 219 | #searchHelpArrow { 220 | background:transparent url('img/up.gif') right top no-repeat; 221 | margin:0 24px; 222 | height:16px; 223 | width:32px; 224 | } 225 | 226 | 227 | #wrapper { 228 | background:#fff url('img/bg-l.png') top left repeat-x; 229 | margin:0;padding:64px 32px 32px;border:0; 230 | } 231 | 232 | #container { 233 | background:#fff url('img/bg-l.png') 0 -10px repeat-x; 234 | margin:0;padding:0;border:0; 235 | border:2px solid #eee; 236 | 237 | } 238 | 239 | #footer { 240 | background:#000 url('img/bg-d.png') top left repeat-x; 241 | color:#aaa; 242 | width:100%; 243 | margin:0;padding:32px 0 0;border:0; 244 | text-align:center; 245 | } 246 | #footer img {border:0;} 247 | 248 | #contentheader { 249 | margin:0;padding:0 16px;border:0; 250 | } 251 | #contentbody { 252 | margin:0;padding:0 16px;border:0; 253 | } 254 | 255 | /* nav */ 256 | .navtoolbar { 257 | overflow:hidden; 258 | width:100%;height:32px; 259 | background:#acf; 260 | background:rgba(240,240,255,0.4); 261 | z-index:10; 262 | } 263 | 264 | .navtoolbar span, #searchbar span, #tailnav span{ 265 | float:right; 266 | } 267 | .navtoolbar span.otherside, #searchbar span.otherside, #tailnav span.otherside{ 268 | float:left; 269 | } 270 | 271 | .navtoolbar a, #tailnav a { 272 | padding-right:24px;padding-left:16px; 273 | } 274 | .navtoolbar a.prevLink, #tailnav a.prevLink { 275 | background:url('img/go-prev.gif') center right no-repeat; 276 | } 277 | .navtoolbar a.upLink, #tailnav a.upLink{ 278 | background:url('img/go-up.gif') center right no-repeat; 279 | } 280 | .navtoolbar a.homeLink, #tailnav a.homeLink{ 281 | background:url('img/go-home.gif') center right no-repeat; 282 | } 283 | .navtoolbar a.nextLink, #tailnav a.nextLink{ 284 | background:url('img/go-next.gif') center left no-repeat; 285 | padding-left:24px;padding-right:16px; 286 | } 287 | #overlay { 288 | display: none; 289 | overflow:hidden; 290 | position: absolute; 291 | left:0; top:0; 292 | z-index:1000; 293 | background:#fff; 294 | } 295 | 296 | 297 | 298 | #kutubListing { 299 | border: 2px solid #aba; 300 | margin:20px; 301 | background-color:#eef7ee; 302 | } 303 | #kutubListing ul { 304 | display:inline; 305 | } 306 | #kutubListing ul li { 307 | display:inline; 308 | font-size:18px; 309 | display:block; 310 | float:right; 311 | margin:18px 16px; 312 | padding-top:64px; 313 | width:130px; 314 | height:50px; 315 | text-align:center; 316 | background:transparent url('img/kutub.gif') no-repeat center 8px; 317 | } 318 | 319 | table,th,td { 320 | border:none; 321 | border-collapse:collapse; 322 | padding:6px; 323 | } 324 | 325 | table { 326 | width:90%; 327 | background:#666; 328 | -webkit-border-radius:6px; 329 | -moz-border-radius:6px; 330 | border-radius:6px; 331 | /* -webkit-box-shadow:1px 1px 10px rgba(0,0,0,0.3); */ 332 | -moz-box-shadow:1px 1px 10px rgba(0,0,0,0.3) 333 | } 334 | thead, tfoot {color:white; } 335 | 336 | tbody td, tbody th{ background-color:#ddd; } 337 | 338 | tbody th { color:#222;} 339 | tbody td{border:1px solid #000;} 340 | tbody td:first-child{border-right:none;} 341 | tbody td:last-child{border-left:none;} 342 | 343 | tbody tr:nth-child(odd) td { background-color:#eee; color:#222} 344 | 345 | tfoot td, tfoot th {border:none; font-size:130%} 346 | 347 | #SearchPages span { 348 | display:inline; 349 | float:right; 350 | margin:0px; 351 | text-align:center; 352 | background: #def; 353 | color:#777; 354 | border:2px solid #acd; 355 | width:42px; 356 | cursor:pointer; 357 | } 358 | #SearchPages a{ 359 | padding:0 0.5em; 360 | } 361 | 362 | #SearchPages a.current{ 363 | background: #acf; 364 | color:#000; 365 | } 366 | 367 | .mini table, .mini th, .mini td { padding:0; } 368 | .mini tbody a{ 369 | display:block; 370 | padding:0 5px; 371 | white-space:nowrap; 372 | width:380px; overflow:hidden; 373 | } 374 | .mini #SearchPages span { font-size:80%; } 375 | 376 | .mini #SearchPages a{ padding:0 0.2em; } 377 | 378 | #SearchContainer.mini { 379 | padding:36px 0 0 0; 380 | overflow:hidden; 381 | } 382 | #rollup { 383 | background: transparent url('img/close.gif') 0 0 no-repeat; 384 | position:absolute; 385 | width:22px; 386 | height:22px; 387 | left:0; 388 | bottom:0; 389 | } 390 | -------------------------------------------------------------------------------- /Thawab/meta.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | """ 3 | The meta handling classes of thawab 4 | Copyright © 2008, Muayyad Alsadi 5 | 6 | Released under terms of Waqf Public License. 7 | This program is free software; you can redistribute it and/or modify 8 | it under the terms of the latest version Waqf Public License as 9 | published by Ojuba.org. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 14 | 15 | The Latest version of the license can be found on 16 | "http://waqf.ojuba.org/license" 17 | 18 | """ 19 | import os 20 | import os.path 21 | import sqlite3 22 | import threading 23 | import time 24 | import hashlib, base64 25 | from itertools import groupby 26 | from .dataModel import * 27 | from okasha.utils import fromFs, toFs, strverscmp 28 | import re 29 | 30 | def prettyId(i, empty_for_special = True): 31 | """convert the id into a more human form""" 32 | if empty_for_special and i.startswith('_'): 33 | return '' 34 | return i.replace('_',' ') 35 | 36 | def makeId(i): 37 | """convert the id into a canonical form""" 38 | return i.strip().replace(' ','_').replace('/','_') 39 | 40 | def metaVr(m): 41 | return m["version"] + "-" + str(m["releaseMajor"]) 42 | 43 | def metaVrr(m): 44 | return "-".join((m["version"], 45 | str(m["releaseMajor"]), 46 | str(m["releaseMinor"]))) 47 | 48 | def metaDict2Hash(meta, suffix = None): 49 | k = [i for i in list(meta.keys()) if i != 'cache_hash'] 50 | k.sort() 51 | l = [] 52 | for i in k: 53 | l.append("%s:%s" % (i,meta[i])) 54 | l.append("timestamp:%d" % int(time.time())) 55 | if suffix: 56 | l.append(suffix) 57 | #rr = hashlib.sha256(("-".join(l)).encode('utf-8')).digest().encode('base64').strip()[:-1] 58 | rr = base64.b64encode(hashlib.sha256(("-".join(l)).encode('utf-8')).digest()).decode().strip()[:-1] 59 | return rr 60 | 61 | class MCache(object): 62 | """a class holding metadata cache""" 63 | def __init__(self, mcache_db, uri_list, smart = -1): 64 | self.db_fn = mcache_db 65 | if not os.path.exists(mcache_db): 66 | create_new = True 67 | else: 68 | create_new = False 69 | self._cn = {} 70 | cn = self._getConnection() 71 | if create_new: 72 | cn.executescript(SQL_MCACHE_DATA_MODEL) 73 | cn.commit() 74 | self.__reload() 75 | if self.__create_cache(uri_list, smart) > 0: 76 | self.__reload() 77 | 78 | def _getConnection(self): 79 | n = threading.current_thread().name 80 | if n in self._cn: 81 | r = self._cn[n] 82 | else: 83 | r = sqlite3.connect(self.db_fn) 84 | r.row_factory = sqlite3.Row 85 | self._cn[n] = r 86 | return r 87 | 88 | 89 | def __reload(self): 90 | self.__meta = [dict(i) for i in self._getConnection().execute(SQL_MCACHE_GET_BY_KITAB)] 91 | self.__meta_by_uri = (dict([(a[1]['uri'], a[0]) for a in enumerate(self.__meta)])) 92 | self.__meta_uri_list = list(self.__meta_by_uri.keys()) 93 | self.__meta_by_kitab = {} 94 | for k,G in groupby(enumerate(self.__meta), lambda a: a[1]['kitab']): 95 | g = list(G) 96 | self.__meta_by_kitab[k] = [i[0] for i in g] 97 | 98 | def load_from_uri(self, uri): 99 | """extract meta object from kitab's uri and return it""" 100 | cn = sqlite3.connect(uri) 101 | cn.row_factory=sqlite3.Row 102 | c = cn.cursor() 103 | try: 104 | r = c.execute(SQL_MCACHE_GET).fetchone() 105 | except sqlite3.OperationalError: 106 | return None 107 | if not r: 108 | return None 109 | return dict(r) 110 | 111 | def __cache(self, c, uri, meta = None): 112 | if not meta: 113 | meta = self.load_from_uri(uri) 114 | if not meta: 115 | return 0 116 | #if drop_old_needed: 117 | meta['uri'] = uri 118 | meta['mtime'] = os.path.getmtime(toFs(uri)) 119 | meta['flags'] = 0 120 | c.execute(SQL_MCACHE_ADD, meta) 121 | return 1 122 | 123 | def __create_cache(self, uri_list, smart = -1): 124 | """ 125 | create cache and return the number of newly created meta caches 126 | 127 | smart is how fast you want to do that: 128 | * 0 force regeneration of entire meta cache 129 | * 1 regenerate cache when hash differs (it would need to open every kitab) 130 | * 2 regenerate when mtime differs 131 | * -1 do not update cache for exiting meta (even if the file is changed) 132 | """ 133 | cn = self._getConnection() 134 | c = cn.cursor() 135 | r = 0 136 | uri_set = set(uri_list) 137 | #c.execute('BEGIN TRANSACTION') 138 | # remove meta for kitab that no longer exists 139 | deleted = [i for i in self.__meta_uri_list if i not in uri_set] 140 | for uri in deleted: 141 | c.execute(SQL_MCACHE_DROP, (uri,)) 142 | r += 1 143 | # update meta for the rest (in a smart way) 144 | for uri in uri_list: 145 | if not os.access(toFs(uri), os.R_OK): continue 146 | if smart == 0: 147 | # force recreation of cache, drop all, then create all 148 | r+=self.__cache(c, uri, uri in self.__meta_uri_list) 149 | continue 150 | meta = None 151 | drop_old_needed = False 152 | cache_needed = False 153 | if uri not in self.__meta_uri_list: 154 | cache_needed = True 155 | else: 156 | drop_old_needed = True 157 | cache_needed = True 158 | if smart == -1: continue # don't replace existing cache 159 | elif smart == 2: # rely of mtime 160 | if abs(os.path.getmtime(toFs(uri)) - self.getByUri(uri)['mtime']) < 1e-5: 161 | continue 162 | elif smart == 1: # rely on a hash saved inside the database 163 | old_meta = self.getByUri(uri) 164 | meta = self.load_from_uri(uri) 165 | if not meta or old_meta['hash'] == meta['hash']: 166 | continue 167 | if cache_needed: 168 | r += self.__cache(c, uri, meta) 169 | #c.execute('END TRANSACTION') 170 | cn.commit() 171 | return r 172 | 173 | def getKitabList(self): 174 | return list(self.__meta_by_kitab.keys()) 175 | 176 | def getUriList(self): 177 | return list(self.__meta_by_uri.keys()) 178 | 179 | def getByUri(self, uri): 180 | """return meta object for uri""" 181 | i = self.__meta_by_uri.get(uri,None) 182 | if i == None: return None 183 | return self.__meta[i] 184 | 185 | def getByKitab(self, kitab): 186 | """return a list of meta objects for a kitab""" 187 | a = self.__meta_by_kitab.get(kitab,None) 188 | if not a: 189 | return None 190 | return [self.__meta[i] for i in a] 191 | 192 | def _latest(self, a): 193 | lm = a[0] 194 | l = metaVrr(lm) 195 | for m in a[1:]: 196 | v = metaVrr(m) 197 | if strverscmp(v, l) > 0: 198 | lm = m 199 | l = v 200 | return lm 201 | 202 | def getLatestKitab(self, kitab): 203 | """return a meta object for latest kitab (based on version)""" 204 | a = self.__meta_by_kitab.get(kitab, None) 205 | if not a: 206 | return None 207 | return self._latest([self.__meta[i] for i in a]) 208 | 209 | def getLatestKitabV(self, kitab, v): 210 | """ 211 | given kitab name and version 212 | return a meta object for latest kitab (based on version) 213 | """ 214 | a = self.__meta_by_kitab.get(kitab, None) 215 | if not a: 216 | return None 217 | ma = [m for m in [self.__meta[i] for i in a] if m['version'] == v] 218 | if not ma: 219 | return None 220 | return self._latest(ma) 221 | 222 | def getLatestKitabVr(self, kitab, v, r): 223 | """ 224 | given kitab name and version and major release 225 | return a meta object for latest kitab (based on version) 226 | """ 227 | if type(r) != int: 228 | r = int(r) 229 | a = self.__meta_by_kitab.get(kitab, None) 230 | ma = [m for m in [self.__meta[i] for i in a] if m['version'] == v and m['releaseMajor'] == r] 231 | if not ma: 232 | return None 233 | return self._latest(ma) 234 | 235 | def setIndexedFlags(self, uri, flags=2): 236 | cn = self._getConnection() 237 | cn.execute(SQL_MCACHE_SET_INDEXED, (flags, uri,)) 238 | cn.commit() 239 | 240 | def setAllIndexedFlags(self, flags=0): 241 | cn = self._getConnection() 242 | cn.execute(SQL_MCACHE_SET_ALL_INDEXED, (flags,)) 243 | cn.commit() 244 | 245 | def getUnindexedList(self): 246 | """ 247 | return a list of meta dicts for Kutub that are likely to be unindexed 248 | """ 249 | 250 | return [dict(i) for i in self._getConnection().execute(SQL_MCACHE_GET_UNINDEXED)] 251 | 252 | def getDirtyIndexList(self): 253 | """ 254 | return a list of meta dicts for Kutub that are likely to have broken index 255 | """ 256 | return [dict(i) for i in self._getConnection().execute(SQL_MCACHE_GET_DIRTY_INDEX)] 257 | 258 | def getIndexedList(self): 259 | """ 260 | return a list of meta dicts for Kutub that are already in index. 261 | """ 262 | return [dict(i) for i in self._getConnection().execute(SQL_MCACHE_GET_INDEXED)] 263 | 264 | 265 | -------------------------------------------------------------------------------- /Thawab/baseSearchEngine.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | """ 3 | 4 | Copyright © 2009, Muayyad Alsadi 5 | 6 | Released under terms of Waqf Public License. 7 | This program is free software; you can redistribute it and/or modify 8 | it under the terms of the latest version Waqf Public License as 9 | published by Ojuba.org. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 14 | 15 | The Latest version of the license can be found on 16 | "http://waqf.ojuba.org/license" 17 | 18 | """ 19 | from .meta import metaVrr 20 | from okasha.utils import strverscmp 21 | from .tags import * 22 | 23 | # TODO: use flags in meta cache object to indicate if indexing was started for some kitab so that if something wrong happend while indexing we can drop index of that kitab 24 | 25 | class BaseSearchEngine: 26 | def __init__(self, th, multithreading = False): 27 | self.th = th 28 | self.multithreading = multithreading 29 | 30 | def getIndexedVersion(self, name): 31 | """ 32 | return a Version-Release string if in index, otherwise return None 33 | """ 34 | raise NotImplementedError 35 | 36 | def queryIndex(self, queryString): 37 | """ 38 | return an interatable of fields dict 39 | this method must be overridden in implementation specific way 40 | """ 41 | raise NotImplementedError 42 | 43 | def indexingStart(self): 44 | """ 45 | should be called before any sequence of indexing Ops, reindexAll() calls this method automatically 46 | """ 47 | pass 48 | 49 | def indexingEnd(self): 50 | """ 51 | should be called after a sequence of indexing Ops, reindexAll() calls this method automatically 52 | """ 53 | pass 54 | 55 | def reload(self): 56 | """ 57 | called after commiting changes to index (eg. adding or dropping from index) 58 | """ 59 | pass 60 | 61 | def dropKitabIndex(self, name): 62 | """ 63 | drop search index for a given Kitab name 64 | you need to call indexingStart() before this and indexingEnd() after it 65 | this method must be overridden in implementation specific way 66 | """ 67 | raise NotImplementedError 68 | 69 | def addDocumentToIndex(self, name, vrr, nodeIdNum, title, content, tags): 70 | """ 71 | this method must be overridden in implementation specific way 72 | """ 73 | raise NotImplementedError 74 | 75 | def dropAll(self): 76 | raise NotImplementedError 77 | # NOTE: the following implementation is buggy, since there could be documents index but no longer exists 78 | #t = [] 79 | #self.indexingStart() 80 | #for i in self.th.getManagedUriList(): self.dropKitabIndex(i) 81 | #self.indexingEnd() 82 | 83 | def dropChanged(self): 84 | """ 85 | drop index for all indexed kutub that got changed (updated or downgraded) 86 | this is useful if followed by indexNew 87 | 88 | no need you need to call indexingStart() indexingEnd() around this 89 | """ 90 | self.indexingStart() 91 | m = self.th.getMeta() 92 | for n in self.th.getKitabList(): 93 | vr = self.getIndexedVersion(n) 94 | if vr and vr != metaVrr(m.getLatestKitab(n)): 95 | self.dropKitabIndex(n) 96 | self.indexingEnd() 97 | 98 | def dropOld(self): 99 | """ 100 | drop index for all indexed kutub that got updated 101 | this is useful if followed by indexNew 102 | 103 | no need you need to call indexingStart() indexingEnd() around this 104 | """ 105 | self.indexingStart() 106 | m = self.th.getMeta() 107 | for n in self.th.getKitabList(): 108 | vr = self.getIndexedVersion(n) 109 | if vr and strverscmp(vr,metaVrr(m.getLatestKitab(n))) > 0: 110 | self.dropKitabIndex(n) 111 | self.indexingEnd() 112 | 113 | def indexNew(self): 114 | """ 115 | index all non-indexed 116 | 117 | no need to call indexingStart() indexingEnd() around this 118 | """ 119 | self.indexingStart() 120 | for n in self.th.getKitabList(): 121 | vr = self.getIndexedVersion(n) 122 | if not vr: 123 | self.indexKitab(n) 124 | self.indexingEnd() 125 | 126 | def refresh(self): 127 | """ 128 | drop changed then index them along with new unindexed. 129 | 130 | no need to call indexingStart() indexingEnd() around this 131 | """ 132 | self.dropChanged() 133 | self.indexNew() 134 | 135 | def reindexAll(self): 136 | """ 137 | no need to call indexingStart() indexingEnd() around this 138 | """ 139 | self.dropAll() 140 | # FIXME: should be dropAll() then usual index not reindex 141 | t = [] 142 | self.indexingStart() 143 | for n in self.th.getKitabList(): 144 | self.indexKitab(n) 145 | # if threading is supported by indexer it would look like 146 | #if self.multithreading: 147 | # for i in self.getManagedUriList(): 148 | # t.append(threading.Thread(target=self.indexKitab,args=(i,))) 149 | # t[-1].start() 150 | # for i in t: i.join() 151 | self.indexingEnd() 152 | 153 | def reindexKitab(self, name): 154 | """ 155 | you need to call indexingStart() before this and indexingEnd() after it 156 | """ 157 | self.dropKitabIndex(name) 158 | self.indexKitab(name) 159 | 160 | def __ix_nodeStart(self, node, name, vrr, iix): 161 | # NOTE: benchmarks says append then join is faster than s += "foo" 162 | tags = node.getTags() 163 | tag_flags = node.getTagFlags() 164 | # create new consuming main indexing fields [ie. headers] 165 | # TODO: let loadToc use TAG_FLAGS_HEADER instead of hard-coding 'header' 166 | #if node.getTagsByFlagsMask(TAG_FLAGS_HEADER): 167 | # NOTE: for consistency, header is the only currentely allowed tag having TAG_FLAGS_HEADER 168 | if tag_flags & TAG_FLAGS_HEADER: 169 | iix.main_f_node_idnums.append(node.idNum) 170 | iix.main_f_content_index.append(len(iix.contents)) 171 | iix.main_f_tags_index.append(len(iix.tags)) 172 | # create new sub non-consuming indexing fields 173 | if tag_flags & TAG_FLAGS_IX_FIELD: 174 | iix.sub_f_node_idnums.append(node.idNum) 175 | iix.sub_f_content_index.append(len(iix.contents)) 176 | iix.sub_f_tags_index.append(len(iix.tags)) 177 | # TODO: check for nodes that are not supposed to be indexed TAG_FLAGS_IX_SKIP 178 | # append ix contents 179 | iix.contents.append(node.getContent()) # TODO: append extra padding space if TAG_FLAGS_PAD_CONTENT 180 | # append ix tags 181 | iix.tags.extend([tags[t] == None and t or '.'.join((t,tags[t])) for t in node.getTagsByFlagsMask(TAG_FLAGS_IX_TAG)]) 182 | 183 | def __ix_nodeEnd(self, node, name, vrr, iix): 184 | # index extra sub fields if any 185 | if iix.sub_f_node_idnums and iix.sub_f_node_idnums[-1] == node.idNum: 186 | n = iix.sub_f_node_idnums.pop() 187 | i = iix.sub_f_content_index.pop() 188 | j = iix.sub_f_tags_index.pop() 189 | c = "".join(iix.contents[i:]) 190 | T = " ".join(iix.tags[j:]) 191 | del iix.tags[j:] 192 | k = iix.main_f_content_index[-1] # the nearest header title index 193 | N = iix.main_f_node_idnums[-1] # the nearest header node.idNum 194 | # NOTE: the above two lines means that a sub ix fields should be children of some main field (header) 195 | t = iix.contents[k] 196 | self.addDocumentToIndex(str(name), vrr, N, t, c, T) 197 | # index consuming main indexing fields if any 198 | if iix.main_f_node_idnums and iix.main_f_node_idnums[-1] == node.idNum: 199 | n = iix.main_f_node_idnums.pop() 200 | i = iix.main_f_content_index.pop() 201 | j = iix.main_f_tags_index.pop() 202 | t = iix.contents[i] 203 | c = ("".join(iix.contents[i:])).strip() 204 | del iix.contents[i:] 205 | T = " ".join(iix.tags[j:]) 206 | del iix.tags[j:] 207 | self.addDocumentToIndex(str(name), vrr, n, t.strip(), c, T) 208 | 209 | class __IIX(object): 210 | "internal indexing object" 211 | def __init__(self): 212 | # independent arrays 213 | self.contents = [] # array of contents to be indexed 214 | self.tags = [] # array of ix tags 215 | # main_f* parallel arrays 216 | self.main_f_node_idnums = [] # array of node.idNum of consuming ix fields (ie. header) 217 | self.main_f_content_index = [] # array of the starting index in self.contents for each main ix field (ie. header) 218 | self.main_f_tags_index = [] # array of the starting index in self.contents for each main ix field (ie. header) 219 | # sub_f* parallel arrays 220 | self.sub_f_node_idnums = [] # array of node.idNum for each sub ix field 221 | self.sub_f_content_index = [] # array of the starting index in self.contents for each sub ix field 222 | self.sub_f_tags_index = [] # array of the starting index in self.tags for each sub ix field 223 | # TODO: benchmark which is faster parallel arrays or small tubles sub_field = (idNum,content_i,tag_i) 224 | 225 | def indexKitab(self, name): 226 | """ 227 | create search index for a given Kitab name 228 | NOTE: you need to call indexingStart() before this and indexingEnd() after it 229 | """ 230 | #print "creating index for kitab with name:", name 231 | ki = self.th.getKitab(name) 232 | self.th.getMeta().setIndexedFlags(ki.uri, 1) 233 | vrr = metaVrr(ki.meta) 234 | iix = self.__IIX() 235 | ki.root.traverser(3, 236 | self.__ix_nodeStart, 237 | self.__ix_nodeEnd, 238 | name, 239 | vrr, 240 | iix) 241 | self.th.getMeta().setIndexedFlags(ki.uri, 2) 242 | 243 | -------------------------------------------------------------------------------- /thawab-data/themes/neo/static/main.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'Cairo'; 3 | font-style: normal; 4 | font-weight: 400; 5 | src: local('Cairo'), local('Cairo-Regular'), url(fonts/cairo.woff2) format('woff2'); 6 | unicode-range: U+0600-06FF, U+200C-200E, U+2010-2011, U+FB50-FDFF, U+FE80-FEFC; 7 | } 8 | .clear {clear:both; padding-bottom:2px;} 9 | 10 | a img { border: none; } 11 | a[href] { text-decoration: none; color:#150;} 12 | a[href]:hover { text-decoration: underline; } 13 | a[target] { background: transparent url('img/external.gif') top left no-repeat; padding-left:12px;} 14 | 15 | body { 16 | font-family: "amiri", "Liberation Sans", "KacstOne", "Simplified Naskh", "KFGQPC Uthman Taha Naskh", "ArabeyesQr", "Times New Roman", "sans", "Sans"; 17 | background:#ccc; 18 | margin:0;padding:0;border:0; 19 | color:#273c42; 20 | font-size:18px; 21 | } 22 | h1, h2{ 23 | margin:0; 24 | padding:0; 25 | color:#303030; 26 | font-family: "Cairo"; 27 | font-weight: normal; 28 | font-size: 22px; 29 | } 30 | .clearer { 31 | display: block;height: 12px; 32 | } 33 | #async_tips_div { 34 | display:none; 35 | position: absolute; 36 | background-color:#ffe; 37 | left:20%; 38 | margin:0; padding:3px 10px; 39 | border:2px solid #aa7; 40 | border:2px solid rgba(170, 170, 119, 0.6); 41 | } 42 | .match { background-color:#acf; } 43 | .term0 { background-color:#acf; } 44 | .term1 { background-color:#fca; } 45 | .term2 { background-color:#cfa; } 46 | .term3 { background-color:#fac; } 47 | .term4 { background-color:#afc; } 48 | .term5 { background-color:#caf; } 49 | 50 | 51 | .quran { 52 | font-family: "amiri-quran", "amiri", "Simplified Naskh", "me_quran", "KFGQPC Uthman Taha Naskh", "ArabeyesQr", "Times New Roman", "Serif"; 53 | font-size: 22px; 54 | color: #240; 55 | padding: 4px; 56 | border-radius: 4px; 57 | box-shadow: 0 1px 2px #e5d693; 58 | background: linear-gradient(#FFFCF0,#FFF5C4); 59 | border: 1px solid #e7d172; 60 | } 61 | 62 | blockquote { 63 | border:3px dotted #aaa; 64 | background:#ddd url('img/quote.gif') top left no-repeat; 65 | background-color:rgba(190, 230, 190, 0.4); 66 | padding:5px 50px; 67 | } 68 | 69 | input.search_input { 70 | font-family: "amiri", "Simplified Naskh", "me_quran", "KFGQPC Uthman Taha Naskh", "ArabeyesQr", "Times New Roman", "Serif"; 71 | padding: 3px 30px 3px 3px; 72 | border-radius: 0px 2px 3px 0px; 73 | font-size: 12pt; 74 | color: #000; 75 | border: 1px solid #4D8726; 76 | width: 576px; 77 | transition: all 400ms ease-in-out; 78 | float: right; 79 | margin-top: 2px; 80 | background: url("img/search.png") right 5px no-repeat,linear-gradient(#fff,#ddd); 81 | box-shadow: 0px 0px 4px 0px #bfbfbf; 82 | } 83 | input:focus.search_input, 84 | input:hover.search_input { 85 | border: 1px solid #56C016; 86 | } 87 | 88 | input.submit_button { 89 | float: left; 90 | height: 36px; 91 | border: 1px solid #4D8726; 92 | background: linear-gradient(#CDE38F,#4D8726); 93 | margin: 2px -1px 0px 0px; 94 | width: 56px; 95 | border-radius: 2px 0px 0px 3px; 96 | color: #fff; 97 | text-shadow: 0px 0px 3px #255D04; 98 | font-size: 14px; 99 | cursor: pointer; 100 | } 101 | #loading { 102 | display:none; 103 | background:transparent url('img/loading.gif') center center no-repeat; 104 | position:absolute; 105 | top:16px; 106 | left:8px; 107 | margin:20px auto; 108 | width:32px; 109 | height:32px; 110 | } 111 | #logo { 112 | background:transparent; 113 | position:absolute; 114 | top:8px; 115 | right:8px; 116 | height:48px; 117 | font-size:24px; 118 | line-height: 100%; 119 | text-decoration: none; 120 | vertical-align:middle; 121 | margin:0;padding:0;border:0; 122 | } 123 | #logo img {border:0; vertical-align: middle;} 124 | #results { 125 | background: #E7E7E7; 126 | color: #fff; 127 | overflow: hidden; 128 | position: fixed; 129 | display: none; 130 | top: 45px; 131 | left: 35px; 132 | width: 500px; 133 | height: 300px; 134 | margin: 0; 135 | padding: 0; 136 | border: 1px solid #A7A7A7; 137 | border-top: 0; 138 | z-index: 12; 139 | } 140 | #nominisearch { 141 | display:none; 142 | text-align:right; 143 | width:200px; 144 | margin:0;padding:0; 145 | } 146 | 147 | #minisearch { 148 | color: #fff; 149 | overflow: hidden; 150 | position: fixed; 151 | top: 7px; 152 | left: 48px; 153 | width: 250px; 154 | height: 32px; 155 | margin: 0; 156 | padding: 0; 157 | z-index: 11; 158 | } 159 | #minisearch form { 160 | margin:0;border:0;padding:0; 161 | } 162 | #minisearch input { 163 | background: #fff; 164 | width: 236px; 165 | height: 26px; 166 | border: 1px solid #A7A7A7; 167 | padding: 1px 4px; 168 | color: #A7A7A7; 169 | border-radius: 2.5px; 170 | } 171 | 172 | #searchHelpDiv { display:none;} 173 | 174 | #searchHelp { 175 | background:#ffffa0; 176 | padding:16px; 177 | -webkit-border-radius:8px; 178 | -moz-border-radius:8px; 179 | border-radius:8px; 180 | } 181 | #searchHelpArrow { 182 | background:transparent url('img/up.gif') right top no-repeat; 183 | margin:0 24px; 184 | height:16px; 185 | width:32px; 186 | } 187 | 188 | 189 | 190 | 191 | #container { 192 | background-image: radial-gradient(circle 1000px at top,#fff 0%,#E7E7E7 40%); 193 | min-height: 100vh; 194 | 195 | } 196 | 197 | #footer { 198 | background: linear-gradient(#eee,#ddd); 199 | color: #111; 200 | margin: 36px; 201 | text-align: left; 202 | height: 35px; 203 | text-shadow: 1px 1px 0px #ccc; 204 | padding: 6px; 205 | border: 1px solid #555; 206 | border-radius: 4px; 207 | box-shadow: 0px 0px 6px -1px #666; 208 | } 209 | #footer img {border:0;} 210 | #contentheaderHome { 211 | text-align: center; 212 | padding: 32px; 213 | } 214 | #contentheader { 215 | text-align: right; 216 | position: absolute; 217 | top: 49px; 218 | left: 45px; 219 | right: 45px; 220 | border-bottom: 1px dashed #a7a7a7; 221 | padding: 4px 12px; 222 | font-size: 14px; 223 | max-height: 30px; 224 | overflow-x: auto; 225 | overflow-y: hidden; 226 | } 227 | #toolbar a.button { 228 | color: #fff; 229 | text-shadow: 0px 0px 3px #aaa; 230 | background: linear-gradient(#f8f8f8,#cfcfcf); 231 | border: 1px solid #aaa; 232 | border-radius: 5px; 233 | padding: 0 10px; 234 | box-shadow: 0px 0px 5px -3px #aaa; 235 | display: inline-block; 236 | white-space: nowrap; 237 | vertical-align: top; 238 | } 239 | #contentheader a:hover, #toolbar a.button:hover{ 240 | text-decoration: none; 241 | background-color: #e9e9e9; 242 | 243 | } 244 | #contentheader a:active, #toolbar a.button:active{ 245 | background-color: #ccc; 246 | } 247 | #contentheader a.home{ 248 | border:none; 249 | background:none; 250 | box-shadow:none; 251 | } 252 | 253 | #contentheader a{ 254 | font-size: 14px; 255 | font-family: cairo; 256 | vertical-align: top; 257 | border: 1px solid #ccc; 258 | background: #efefef; 259 | border-radius: 3px; 260 | padding: 1px 4px; 261 | box-shadow: 0px 0px 2px 1px #efefef; 262 | } 263 | #contentheaderHome .logo { 264 | width: 256px; 265 | height: 256px; 266 | cursor: pointer; 267 | } 268 | #toolbar { 269 | position: fixed; 270 | top: 0px; 271 | right: 36px; 272 | left: 36px; 273 | background: linear-gradient(#E7E7E7,#d5d5d5); 274 | border-bottom: 1px solid #A7A7A7; 275 | padding: 6px 10px; 276 | z-index: 10; 277 | } 278 | a.inactive { 279 | filter: grayscale(100%); 280 | opacity: 0.5; 281 | cursor: not-allowed; 282 | } 283 | #toolbar a.button { 284 | margin-left: 10px; 285 | border-radius: 2.5px; 286 | padding: 2px 10px; 287 | } 288 | #contentbodyHome { 289 | margin:0;padding:0 16px;border:0; 290 | } 291 | #contentbody { 292 | background: #fff; 293 | margin: 0px 35px; 294 | border: 1px solid #a7a7a7; 295 | border-radius: 0 0 4px 4px; 296 | padding: 77px 10px 10px 10px; 297 | min-height: 100vh; 298 | box-shadow: 0 1px 2px #aeaeac; 299 | } 300 | /* nav */ 301 | .navtoolbar { 302 | overflow:hidden; 303 | width:100%;height:32px; 304 | background:#acf; 305 | background:rgba(240,240,255,0.4); 306 | z-index:10; 307 | } 308 | 309 | .navtoolbar span, #searchbar span, #tailnav span{ 310 | float:right; 311 | } 312 | .navtoolbar span.otherside, #searchbar span.otherside, #tailnav span.otherside{ 313 | float:left; 314 | } 315 | 316 | .navtoolbar a, #tailnav a { 317 | padding-right:24px;padding-left:16px; 318 | } 319 | .navtoolbar a.prevLink, #tailnav a.prevLink { 320 | background:url('img/go-prev.gif') center right no-repeat; 321 | } 322 | .navtoolbar a.upLink, #tailnav a.upLink{ 323 | background:url('img/go-up.gif') center right no-repeat; 324 | } 325 | .navtoolbar a.homeLink, #tailnav a.homeLink{ 326 | background:url('img/go-home.gif') center right no-repeat; 327 | } 328 | .navtoolbar a.nextLink, #tailnav a.nextLink{ 329 | background:url('img/go-next.gif') center left no-repeat; 330 | padding-left:24px;padding-right:16px; 331 | } 332 | #overlay { 333 | display: none; 334 | overflow:hidden; 335 | position: absolute; 336 | left:0; top:0; 337 | z-index:1000; 338 | background:#fff; 339 | } 340 | 341 | 342 | 343 | .Box { 344 | border-radius: 5px; 345 | margin: 20px; 346 | background-color: #fff; 347 | position: relative; 348 | box-shadow: 0px 0px 5px 0px #999; 349 | } 350 | 351 | .Box h2 { 352 | margin: 0; 353 | background: linear-gradient(#d7d7d7,#FFF); 354 | color: #5D606F; 355 | text-shadow: 0px 0px 3px #fff; 356 | padding: 0 12px; 357 | border-radius: 2px 2px 0px 0px; 358 | } 359 | .Box div.info { 360 | position: absolute; 361 | top: 10px; 362 | left: 10px; 363 | color: #150; 364 | opacity: 0.5; 365 | } 366 | .Box div.info:hover { 367 | opacity:1; 368 | } 369 | #kutubListing input { 370 | top: 10px; 371 | left: 10px; 372 | position: absolute; 373 | background: #fff; 374 | border: 1px solid #ccc; 375 | padding: 3px; 376 | width: 250px; 377 | } 378 | #kutubListing ul { 379 | display:inline; 380 | } 381 | #kutubListing ul li { 382 | list-style: none; 383 | } 384 | #kutubListing ul li a { 385 | font-size: 18px; 386 | display: block; 387 | margin: 5px 10px; 388 | padding: 3px 40px 3px 4px; 389 | height: 32px; 390 | text-align: right; 391 | background: #efefef url('img/book.png') no-repeat right center; 392 | transition: all 400ms ease-in-out; 393 | clear: both; 394 | color: #273c42; 395 | border: 1px solid #d4d4d4; 396 | border-radius: 2px; 397 | } 398 | 399 | #kutubListing ul li a:hover{ 400 | background: #ccc url('img/book.png') no-repeat right center; 401 | text-decoration: none; 402 | } 403 | 404 | table,th,td { 405 | border:none; 406 | border-collapse:collapse; 407 | padding:6px; 408 | } 409 | 410 | table { 411 | width: 96%; 412 | background: #666; 413 | border-radius: 6px; 414 | box-shadow: 1px 1px 10px rgba(0,0,0,0.3); 415 | } 416 | thead, tfoot {color:white; } 417 | 418 | tbody td, tbody th{ background-color:#ddd; } 419 | 420 | tbody th { color:#222;} 421 | tbody td{border:1px solid #000;} 422 | tbody td:first-child{border-right:none;} 423 | tbody td:last-child{border-left:none;} 424 | 425 | tbody tr:nth-child(odd) td { background-color:#eee; color:#222} 426 | 427 | tfoot td, tfoot th {border:none; font-size:130%} 428 | 429 | #SearchContainer_tab{ 430 | overflow-y: scroll; 431 | height: 79vh; 432 | direction: rtl; 433 | } 434 | .searchItem { 435 | display: block; 436 | width: auto; 437 | border-radius: 3px; 438 | background: url("img/text.png") no-repeat scroll right 10px, #fff linear-gradient(#eef7ee,#e3f9cd) !important; 439 | border: 1px solid #e3f9cd; 440 | margin: 14px 10px 12px 8px; 441 | box-shadow: 0px 0px 5px -2px #657556; 442 | padding-right: 48px; 443 | transition: all 400ms ease-in-out; 444 | } 445 | .searchItem h2{ 446 | background: transparent none repeat scroll 0% 0%; 447 | border: none; 448 | color: #150; 449 | text-shadow: 2px 1px 1px #ccc; 450 | font-weight: normal; 451 | padding-top: 1px; 452 | } 453 | .searchItem span{ 454 | color: #950000; 455 | } 456 | 457 | .searchItem:hover { 458 | background: url("img/text.png") no-repeat scroll right 10px, #fff linear-gradient(#eef7ee,#bcdb9d) !important; 459 | border: 1px solid #bcdb9d; 460 | text-decoration: none !important; 461 | } 462 | .searchItem:active { 463 | background: url("img/text.png") no-repeat scroll right 10px, #fff linear-gradient(#bcdb9d,#eef7ee) !important; 464 | border: 1px solid #bcdb9d; 465 | text-decoration: none !important; 466 | } 467 | #SearchPages span { 468 | display:inline; 469 | float:right; 470 | margin:0px; 471 | text-align:center; 472 | background: #def; 473 | color:#777; 474 | border:2px solid #acd; 475 | width:42px; 476 | cursor:pointer; 477 | } 478 | #SearchPages a{ 479 | padding:0 0.5em; 480 | } 481 | 482 | #SearchPages a.current{ 483 | background: #acf; 484 | color:#000; 485 | } 486 | 487 | .mini table, .mini th, .mini td { padding:0; } 488 | .mini tbody a{ 489 | display:block; 490 | padding:0 5px; 491 | white-space:nowrap; 492 | width:380px; overflow:hidden; 493 | } 494 | .mini #SearchPages span { font-size:80%; } 495 | 496 | .mini #SearchPages a{ padding:0 0.2em; } 497 | 498 | #SearchContainer.mini { 499 | padding:36px 0 0 0; 500 | overflow:hidden; 501 | } 502 | #rollup { 503 | background: transparent url('img/close.png') 0 0 no-repeat; 504 | position: absolute; 505 | width: 22px; 506 | height: 22px; 507 | left: 0; 508 | bottom: 0; 509 | cursor: pointer; 510 | } 511 | -------------------------------------------------------------------------------- /Thawab/whooshSearchEngine.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | """ 3 | 4 | Copyright © 2008, Muayyad Alsadi 5 | 6 | Released under terms of Waqf Public License. 7 | This program is free software; you can redistribute it and/or modify 8 | it under the terms of the latest version Waqf Public License as 9 | published by Ojuba.org. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 14 | 15 | The Latest version of the license can be found on 16 | "http://waqf.ojuba.org/license" 17 | 18 | """ 19 | import sys, os, os.path, re 20 | import shutil 21 | from .tags import * 22 | from .meta import prettyId,makeId 23 | 24 | from whoosh import query 25 | from whoosh.index import EmptyIndexError, create_in, open_dir, IndexVersionError 26 | from whoosh.highlight import highlight, SentenceFragmenter, BasicFragmentScorer, FIRST, HtmlFormatter 27 | from whoosh.filedb.filestore import FileStorage 28 | from whoosh.fields import Schema, ID, IDLIST, TEXT 29 | from whoosh.formats import Frequency 30 | from whoosh.qparser import QueryParserError 31 | from whoosh.lang.porter import stem 32 | from whoosh.analysis import StandardAnalyzer, StemFilter 33 | 34 | try: 35 | from whoosh.index import _CURRENT_TOC_VERSION as whoosh_ix_ver 36 | except ImportError: 37 | from whoosh.filedb.fileindex import _INDEX_VERSION as whoosh_ix_ver 38 | 39 | from .stemming import stemArabic 40 | 41 | def stemfn(word): return stemArabic(stem(word)) 42 | # word_re = ur"[\w\u064e\u064b\u064f\u064c\u0650\u064d\u0652\u0651\u0640]" 43 | analyzer = StandardAnalyzer(expression = r"[\w\u064e\u064b\u064f\u064c\u0650\u064d\u0652\u0651\u0640]+(?:\.?[\w\u064e\u064b\u064f\u064c\u0650\u064d\u0652\u0651\u0640]+)*") | StemFilter(stemfn) 44 | 45 | from whoosh.qparser import FieldAliasPlugin 46 | from .whooshSymbolicQParser import MultifieldSQParser 47 | 48 | class ExcerptFormatter(object): 49 | def __init__(self, between = "..."): 50 | self.between = between 51 | 52 | def _format_fragment(self, text, fragment): 53 | output = [] 54 | index = fragment.startchar 55 | 56 | for t in fragment.matches: 57 | if t.startchar > index: 58 | output.append(text[index:t.startchar]) 59 | 60 | ttxt = text[t.startchar:t.endchar] 61 | if t.matched: 62 | ttxt = "\0" + ttxt.upper() + "\010" 63 | output.append(ttxt) 64 | index = t.endchar 65 | 66 | output.append(text[index:fragment.endchar]) 67 | return "".join(output) 68 | 69 | def __call__(self, text, fragments): 70 | return self.between.join((self._format_fragment(text, fragment) 71 | for fragment in fragments)) 72 | 73 | 74 | from .baseSearchEngine import BaseSearchEngine 75 | class SearchEngine(BaseSearchEngine): 76 | def __init__(self, th): 77 | BaseSearchEngine.__init__(self, th, False) 78 | self.__ix_writer = None 79 | ix_dir = os.path.join(th.prefixes[0],'index', "ix_" + str(whoosh_ix_ver)) 80 | if not os.path.isdir(ix_dir): 81 | os.makedirs(ix_dir) 82 | # try to load a pre-existing index 83 | try: 84 | self.indexer = open_dir(ix_dir) 85 | except (EmptyIndexError, IndexVersionError): 86 | # create a new one 87 | try: 88 | shutil.rmtree(ix_dir, True) 89 | os.makedirs(ix_dir) 90 | except OSError: 91 | pass 92 | schema = Schema( 93 | kitab = ID(stored = True), 94 | vrr = ID(stored = True, unique = False), # version release 95 | nodeIdNum = ID(stored = True, unique = False), 96 | title = TEXT(stored = True, field_boost = 1.5, analyzer = analyzer), 97 | content = TEXT(stored = False,analyzer = analyzer), 98 | #content = TEXT(stored = False,analyzer = analyzer, 99 | #vector = Frequency(analyzer = analyzer)), # with term vector 100 | tags=IDLIST(stored = False) 101 | ) 102 | self.indexer = create_in(ix_dir, schema) 103 | #self.__ix_qparser = ThMultifieldParser(self.th, ("title","content",), schema=self.indexer.schema) 104 | self.__ix_qparser = MultifieldSQParser(("title","content",), self.indexer.schema) 105 | self.__ix_qparser.add_plugin(FieldAliasPlugin({ 106 | "kitab":("كتاب",), 107 | "title":("عنوان",), 108 | "tags":("وسوم",)}) 109 | ) 110 | #self.__ix_pre = whoosh.query.Prefix 111 | self.__ix_searcher = self.indexer.searcher() 112 | 113 | def __del__(self): 114 | if self.__ix_writer: self.__ix_writer.commit() 115 | 116 | def getIndexedVersion(self, name): 117 | """ 118 | return a Version-Release string if in index, otherwise return None 119 | """ 120 | try: 121 | d = self.__ix_searcher.document(kitab = str(makeId(name))) 122 | except TypeError: 123 | return None 124 | except KeyError: 125 | return None 126 | if d: 127 | return d['vrr'] 128 | return None 129 | 130 | def queryIndex(self, queryString): 131 | """return an interatable of fields dict""" 132 | # FIXME: the return should not be implementation specific 133 | try: 134 | r = self.__ix_searcher.search(self.__ix_qparser.parse(queryString), limit = 500) 135 | except QueryParserError: 136 | return None 137 | return r 138 | 139 | def resultExcerpt(self, results, i, ki = None): 140 | # FIXME: this should not be implementation specific 141 | if not ki: 142 | r = results[i] 143 | name = r['kitab'] 144 | v = r['vrr'].split('-')[0] 145 | m = self.th.getMeta().getLatestKitabV(name,v) 146 | ki = self.th.getCachedKitab(m['uri']) 147 | num = int(results[i]['nodeIdNum']) 148 | node = ki.getNodeByIdNum(num) 149 | n = ki.toc.next(node) 150 | 151 | if n: 152 | ub = n.globalOrder 153 | else: 154 | ub = -1 155 | txt = node.toText(ub) 156 | 157 | s = set() 158 | #results.query.all_terms(s) # return (field,term) pairs 159 | # return (field,term) pairs # self.self.__ix_searcher.reader() 160 | s = results.q.existing_terms(self.indexer.reader(), phrases = True) 161 | #s = set([i.decode('utf_8') for i in s]) 162 | terms = list(dict( 163 | [(i[1],i[0]) for i in [j for j in s if j[0] == 'content' or j[0] == 'title']]).keys()) 164 | #print "txt = [%s]" % len(txt) 165 | terms = [i.decode('utf_8') for i in terms] 166 | snippet_dummy = txt[:min(len(txt),512)] # dummy summary 167 | snippet = highlight(txt, 168 | terms, 169 | analyzer, 170 | SentenceFragmenter(sentencechars = ".!?؟\n"), 171 | HtmlFormatter(between = "\u2026\n"), 172 | top = 3, 173 | scorer = BasicFragmentScorer, 174 | minscore = 1, 175 | order = FIRST) 176 | #snippet = highlight(txt, terms, analyzer, 177 | # SentenceFragmenter(sentencechars = ".!?"), ExcerptFormatter(between = u"\u2026\n"), top = 3, 178 | # scorer = BasicFragmentScorer, minscore = 1, 179 | # order = FIRST) 180 | #print snippet 181 | if len(snippet) > 1: return snippet 182 | else: return snippet_dummy 183 | 184 | def indexingStart(self): 185 | """ 186 | should be called before any sequence of indexing Ops, reindexAll() calls this method automatically 187 | """ 188 | if not self.__ix_writer: 189 | try: 190 | self.__ix_writer = self.indexer.writer() 191 | except OSError as e: 192 | print('*** whooshSearchEnfine.indexingStart: %s', e) 193 | 194 | 195 | def indexingEnd(self): 196 | """ 197 | should be called after a sequence of indexing Ops, reindexAll() calls this method automatically 198 | """ 199 | self.__ix_writer.commit(optimize = True) 200 | # self.indexer.optimize() # no need for this with optimize in previous line 201 | self.reload() 202 | 203 | def reload(self): 204 | """ 205 | called after commiting changes to index (eg. adding or dropping from index) 206 | """ 207 | self.__ix_searcher = self.__ix_searcher.refresh() # no need to obtain new one with self.indexer.searcher() 208 | self.__ix_writer = None 209 | 210 | def dropKitabIndex(self, name): 211 | """ 212 | drop search index for a given Kitab by its uri 213 | if you call indexingStart() before this 214 | then you must call indexingEnd() after it 215 | """ 216 | # FIXME: it seems that this used not work correctly without commit() just after drop, this mean that reindex needs a commit in-between 217 | ki = self.th.getKitab(name) 218 | if ki: 219 | self.th.getMeta().setIndexedFlags(ki.uri, 1) 220 | print("dropping index for kitab name:", name, end=' ') 221 | w, c = self.__ix_writer, False 222 | if not w: 223 | w, c = self.indexer.writer(), True # creates a writer internally if one is not defined 224 | # NOTE: because the searcher could be limited do a loop that keeps deleting till the query is empty 225 | while(w.delete_by_term('kitab', name)): 226 | print("*", end=' ') 227 | print() 228 | if c: 229 | w.commit() 230 | if ki: 231 | self.th.getMeta().setIndexedFlags(ki.uri, 0) 232 | 233 | def dropAll(self): 234 | # FIXME: it would be more effeciant to delete the directory 235 | # NOTE: see http://groups.google.com/group/whoosh/browse_thread/thread/35b1700b4e4a3d5d 236 | self.th.getMeta().setAllIndexedFlags(1) 237 | self.indexingStart() 238 | reader = self.indexer.reader() # also self.__ix_searcher.reader() 239 | for docnum in reader.all_stored_fields(): 240 | self.__ix_writer.delete_document(docnum) 241 | self.indexingEnd() 242 | self.th.getMeta().setAllIndexedFlags(0) 243 | 244 | def reindexKitab(self,name): 245 | """ 246 | you need to call indexingStart() before this and indexingEnd() after it 247 | """ 248 | # NOTE: this method is overridden here because we need to commit 249 | # between dropping and creating a new index. 250 | # NOTE: can't use updateDocument because each Kitab contains many documents 251 | self.dropKitabIndex(name) 252 | self.__ix_writer.commit() 253 | self.indexKitab(name) 254 | 255 | def addDocumentToIndex(self, name, vrr, nodeIdNum, title, content, tags): 256 | """ 257 | this method must be overridden in implementation specific way 258 | """ 259 | if not tags: tags = [' '] 260 | if content: 261 | self.__ix_writer.add_document(kitab = name, 262 | vrr = vrr, 263 | nodeIdNum = str(nodeIdNum), 264 | title = title, 265 | content = content, 266 | tags = tags) 267 | 268 | def keyterms(self, kitab, vrr, nodeIdNum): 269 | s = self.indexer.searcher() 270 | dn = s.document_number(kitab = kitab, vrr = vrr, nodeIdNum = str(nodeIdNum)) 271 | if dn == None: 272 | return None, [] 273 | print(" ## ", dn) 274 | r = s.key_terms([dn], "content", numterms = 5) 275 | return dn, r 276 | 277 | def related(self, kitab, vrr, nodeIdNum): 278 | dn, kt = self.keyterms(kitab, vrr, nodeIdNum) 279 | if not dn: 280 | return None 281 | for t, r in kt: 282 | print("term = ", t, " @ rank = ",r) 283 | q = query.Or([query.Term("content", t) for (t, r) in kt]) 284 | results = self.indexer.searcher().search(q, limit = 10) 285 | for i, fields in enumerate(results): 286 | if results.docnum(i) != dn: 287 | print(fields['kitab'],"\t\t",str(fields['nodeIdNum']),"\t\t",fields['title']) 288 | 289 | -------------------------------------------------------------------------------- /thawab-data/themes/neo/static/manual/manual.html: -------------------------------------------------------------------------------- 1 | 3 | 5 | 6 | 7 | دليل استخدام ثواب 8 | 9 | 10 | 11 | 12 | 13 |
14 | 15 | 41 | 42 | 43 |

دليل استخدام مكتبة ثواب

44 |
45 | 46 |
47 | 48 |

البحث

49 |
50 | 51 |
52 | 53 |

لمحة

54 |
55 | 56 |

57 | تستخدم مكتبة ثواب فهرسًا لتسريع عملية البحث. الكتب غير المضافة للفهرس لا تظهر في نتائج البحث. 58 |

59 | 60 |

61 | عندما تفتح كتابًا ما فإن صندوق البحث الصغير في صفحة الكتاب يبحث داخل ذاك الكتاب فقط، أما الصفحة الأولى فتبحث في كل الكتب ما لم تحدّد لها مجال البحث. 62 |

63 | 64 |

65 | في الغالب لا تحتاج تحديد مجال البحث حيث يتم البحث في كل الكتب خلال ثانية أو أقل ولا يزيد الوقت اللازم للبحث كلما زادت حجم المكتبة كماً، والنتائج تكون مرتبة بحسب ارتباط النتيجة بالشيء الذي تبحث عنه (مثلا يعطى ورود الكلمة في العنوان وزنا أعلى من ورودها في المتن) 66 |

67 | 68 |

69 | يتأخر البحث عند البحث عن أمور عامة تُخرِج الكثير من النتائج (في الزمن اللازم لتقييمها وترتيبها). حاليا المكتبة تظهر أوّل 500 نتيجة فقط (يمكن إعادة ضبط هذا الرقم إن احتجنا لذلك). 70 |

71 | 72 |

73 |

74 | لتسريع البحث ابحث عن شيء مميز. مثلا: لا تبحث عن كلمات مثل حرف “في” بل ابحث عن كلمة مميزة. 75 | 76 |

77 |

78 | 79 |

80 | البحث الموجود على الصفحة الرئيسة يقوم بالبحث في كل الكتب المفهرسة ما لم تقم بحصر مجال البحث بشكل صريح. 81 |

82 | 83 |

84 |

لا يتم البحث في الكتب غير المفهرسة. 85 |

86 |

87 | 88 |

89 | عندما تفتح كتابًا فإنك تجد صندوق بحث مصغر في رأس الصفحة وبعكس صندوق البحث الرئيس لا يبحث هذا الصندوق إلا في الكتاب الحالي. الكلمات التي تكتبها فيه يتم تظليلها في الصفحة الحالية فور كتابتها ويتم النزول إليها لإظهارها عند النقر على زر الإدخال أي أن الإدخال له فائدتان: البحث في الصفحة الحالية والبحث في كل الكتاب الحالي. 90 |

91 | 92 |
93 | 94 |

الملخص

95 |
96 |
97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 |
الرمز اسم العملية شرح مثال معنى المثال
& و تشترط تحقق ما قبلها وما بعدها معا كتاب & قلم أن يحتوي النص كلمتي “كتاب” و “قلم”
&~ وربما تشترط تحقق ما قبلها ويفضل أن يتحقق ما بعدها دون اشتراط ذلك كتاب &~ قلم أن يحتوي النص كلمة “كتاب” ويفضل أن يحتوي كلمة “قلم”
| أو تطابق سواء تحقق ما قبلها أو تحقق ما بعدها عيسى | المسيح أن يحتوي النص كلمة “عيسى” أو كلمة “المسيح”
! النفي تطابق إذا لم يتحقق ما بعدها عيسى | المسيح ! الدجال أن يحتوي النص كلمة “عيسى” أو كلمة “المسيح” لكن بشرط أن لا يحتوي كلمة “الدجال”
* أي شيء صفر أو أكثر من الحروف استع* يمكن أن تطابق ما يبدأ ب استع مثل استعان أو استعمل …إلخ
? حرف واحد حرف واحد تماما ش؟ب يمكن أن تطابق شرب أو شنب …إلخ
^ قوة زيادة وزن الكلمة عند ترتيب النتائج أحمد ابن تيمية^3 إعطاء كلمة تيمية 3 أضعاف وزن الأخرى عند الترتيب
كتاب: تحديد المجال البحث في الكتاب المذكور تاليا التيمم كتاب:(“صحيح البخاري” | “صحيح مسلم”) البحث عن التيمم في صحيح البخاري أو صحيح مسلم
عنوان: البحث في العناوين حصر البحث في عناوين الأبواب عنوان:(بدء الوحي) البحث عن كلمة بدء وكلمة وحي في عناوين الأبواب
128 | 129 |

130 | 131 |

يمكنك طباعة رمز ~ الذي يأتي بعد علامة & في استعلام “وربما” عبر الضغط على زر SHIFT+Z في لوحة المفاتيح العربية QWERTY 132 |

133 |

134 | 135 |
136 | 137 |

الاستعلامات المتقدمة

138 |
139 | 140 |

141 | عند كتابة أكثر من كلمة فإنه يتم ربطها معا ضمنيا عبر علاقة “و” (ورمزها “&”) أي أنه سيعطيك الوثائق التي تحتوي كل تلك الكلمات معا (دون اشتراط تتابع معين) مثلا عند كتابة كلمة خمر ثم مسافة ثم تحريم فإنه لن يعطيك الوثائق التي تحتوي واحدة من الكلمتين بل يجب أن ترد الكلمتين معا (أي كأنك كتبت خمر & تحريم) لكنه قد يعطيك وثائق تحتوي على عبارة “تحريم الخمر” أي بترتيب معكوس. 142 |

143 | 144 |

145 | يمكنك استعمال العلاقة “أو” (ورمزها “|”) حتى تشترط ورود واحدة من تلك الكلمات مثلا لو كتبت كلمة عيسى ثم | ثم المسيح فإنه لن يشترط ورود الكلمتين معا فواحدة منهما تكفي. 146 |

147 | 148 |

149 | يمكن نفي العمليات عبر علامة التعجب “!” وذلك لاستثناء شيء من النتائج مثلا المسيح ! الدجال تحضر الصفحات التي تحتوي كلمة المسيح لكنها في نفس الوقت لا تحتوي كلمة الدجال. 150 |

151 | 152 |

153 | يمكن استعمال الأقواس كما في العمليات الحسابية مثلا (المسيح | عيسى) ! الدجال. 154 |

155 | 156 |

157 | لاشتراط الترتيب عليك وضع علامة اقتباس مزدوجة مثلا “تحريم الخمر” وهذا يسمى البحث عن عبارة. 158 |

159 | 160 |

161 | يمكنك اشتراط ورود الكلمة في العنوان عبر كتابة عنوان ثم علامة : مثلا عنوان:“بدء الوحي” للبحث عن عبارة “بدء الوحي” في العنوان أما البحث عن كلمات في العنوان دون اشتراط أن تكون عبارة يمكنك كتابة عنوان:(بدء الوحي) 162 |

163 | 164 |

165 |

166 | البحث عن عنوان:بدء الوحي دون اقتباس أو أقواس تعني البحث عن عنوان به كلمة بدء أما كلمة وحي فلا يشترط أن تكون في العنوان 167 | 168 |

169 |

170 | 171 |

172 | يمكنك تحديد مجال البحث عبر ذكر اسم الكتاب بعد كلمة كتاب ثم : مثلا 173 |

174 |
    175 |
  • بدء الوحي كتاب:صحيح_البخاري
    176 |
  • 177 |
  • بدء الوحي كتاب:“صحيح البخاري”
    178 |
  • 179 |
180 | 181 |

182 | 183 |

184 | البحث عن بدء الوحي كتاب:صحيح_البخاري دون اقتباس ولا علامة تحتية _ تعني أن اسم الكتاب هو صحيح أما البخاري فهي كلمة تريد البحث عنها 185 | 186 |

187 |

188 | 189 |

190 |

191 | يمكنك البحث في أكثر من كتاب عبر استعمال أو مثلا بدء الوحي (كتاب:صحيح_البخاري | كتاب:صحيح_مسلم) 192 | كذلك يمكنك استثناء كتاب عبر ! مثلا بدء الوحي ! كتاب:صحيح_البخاري تبحث عن بدء الوحي في أي كتاب إلا صحيح البخاري 193 | 194 |

195 |

196 | 197 |
198 | 199 |

الحصول على الكتب

200 |
201 | 202 |

203 | قد تأتي بعض الكتب مع النظام (مثلا في نظام أعجوبة لينكس هناك عينة من الكتب المميزة تأتي مع التوزيعة) لكن 204 | الكتب ليست جزء من مكتبة ثواب بل يقوم باختيارها المستخدم حيث يمكنه الحصول عليها من أكثر من مصدر. 205 |

206 | 207 |

208 | يمكن وضع الكتب في مجلد db داخل المجلدات الخاصة بمكتبة ثواب. 209 |

210 | 211 |

212 | المجلدات الخاصة بثواب هي 213 |

214 |
    215 |
  1. مجلد .thawab داخل المجلد البيت الخاص بالمستخدم.
    216 |
  2. 217 |
  3. مجلد thawab-data المجاور للملف التنفيذي الذي يشغل ثواب
    218 |
  4. 219 |
  5. مجلد thawab-data في كل من أقراص ويندوز من C إلى Z
    220 |
  6. 221 |
222 | 223 |
224 | 225 |

الاستيراد من الشاملة

226 |
227 | 228 |

229 | يمكن استيراد كتب الشاملة ذات الهيئة .bok والتي يمكن الحصول عليها بتصديرها من برنامج الشاملة أو 230 | بتنزيلها من الإنترنت 1). 231 | ويكون ذلك من خلال زر الاستيراد بالأعلى أو من خلال سحب ملفات bok وإفلاتها في برنامج ثواب، كما يمكنك إضافة الكتب عبر زر الإضافة Add وبعد الانتهاء من اختيار الكتب، اضغط الزر التحويل Convert. 232 |

233 | 234 |
235 | 236 |

من موقع أعجوبة

237 |
238 | 239 |

240 | يمكن جلب الكتب من هذا الرابط: 241 | 242 |

243 | 247 | 248 |
249 | 250 |

إنشاء فهرس البحث

251 |
252 | 253 |

254 | بعد إضافة المزيد من الكتب إلى ثواب لا تكون مفهرسة مما يعني أنه لا يمكن البحث فيها. لإضافتها إلى فهرس البحث في تطبيق ثواب انقر على زر فهرس البحث Index ثم تضغط على زر دفع الكتب الجديدة queue new books كي يتم التعرف على الكتب غير الموجودة في الفهرس وإضافتها إليه. 255 |

256 | 257 |
258 | 259 |

التخلص من فهرس لا يعمل

260 |
261 | 262 |

263 | إن توقف الفهرس عن العمل لأي سبب (مثل تلف الملف بسبب انقطاع التيار الكهربائي أو ترقية إصدار محرك البحث whoosh إلى إصدار أحدث غير متوافق مع الذي قبله) عليك حذف مجلد index ثم إعادة فهرسة الملفات. 264 |

265 | 266 |
267 |
268 |
1) 269 | يمكن تنزيلها من موقع الشاملة الجديد أو القديم أو من موقع islamport
270 |
271 |
272 | 273 | 274 | --------------------------------------------------------------------------------