├── .gitignore ├── favicon.ico ├── package.json ├── Gruntfile.js ├── Web.config ├── README.md ├── quranweb.sln ├── css └── site.css ├── js ├── quranclient.js ├── mousetrap.min.js ├── modernizr.js ├── site.js └── semantic-quran-web.min.js └── index.html /.gitignore: -------------------------------------------------------------------------------- 1 | *.suo 2 | node_modules 3 | *.log 4 | *.err 5 | -------------------------------------------------------------------------------- /favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hasankhan/semantic-quran-web/HEAD/favicon.ico -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "semantic-quran-web", 3 | "version": "1.0.0", 4 | "description": "Web UI for Semantic Quran App", 5 | "main": "index.html", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "http://github.com/hasankhan/semantic-quran-web" 12 | }, 13 | "author": "Hasan Khan", 14 | "bugs": { 15 | "url": "https://github.com/hasankhan/semantic-quran-web/issues" 16 | }, 17 | "homepage": "http://semquran.com", 18 | "devDependencies": { 19 | "grunt": "~0.4.5", 20 | "grunt-contrib-uglify": "^0.6.0" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | module.exports = function (grunt) { 2 | 3 | // Project configuration. 4 | grunt.initConfig({ 5 | pkg: grunt.file.readJSON('package.json'), 6 | uglify: { 7 | options: { 8 | banner: '/*! <%= pkg.name %> <%= grunt.template.today("yyyy-mm-dd") %> */\n' 9 | }, 10 | build: { 11 | src: ['js/*.js', '!js/<%= pkg.name %>.min.js'], 12 | dest: 'js/<%= pkg.name %>.min.js' 13 | } 14 | }, 15 | jshint: { 16 | all: ['Gruntfile.js', 'js/quranclient.js', 'js/site.js'] 17 | } 18 | }); 19 | 20 | // Load the plugin that provides the "uglify" task. 21 | grunt.loadNpmTasks('grunt-contrib-uglify'); 22 | grunt.loadNpmTasks('grunt-contrib-jshint'); 23 | 24 | // Default task(s). 25 | grunt.registerTask('default', ['jshint', 'uglify']); 26 | 27 | }; -------------------------------------------------------------------------------- /Web.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Semantic Quran 2 | ================== 3 | 4 | There are many verses in the Holy Quran that relate to certain topics but if we try to search for them based on keywords, we won't be able to find them because the search keyword may not be present in the translation of the verse itself. 5 | 6 | For example in verse [(94:6)](http://semquran.com#94/6) Allah (SWT) says, "Indeed, with hardship will be ease.". This verse is about hope and patience for believers who are in difficulty but the word 'hope' or 'patience' itself is not present in the verse and we won't be able to find this verse if we were to search for it. 7 | 8 | Tagging verses with words helps us categorize them in topics. This is useful in exploring Quran based on topics and identifying patterns and descriptions from Allah (SWT) about matters in a way not commonly used before. 9 | 10 | The website is hosted at: http://semquran.com 11 | 12 | ### Shortcuts ### 13 | 14 | * To directly visit a verse simply append hash (#) surah number and verse number to the url separated by slash e.g. http://semquran.com#3/2 would take you to verse 2 of surah 3. 15 | * To visit a range of verses replace the last number with a range e.g. http://semquran.com#3/2-5 would show you verse 2 to 5 of surah 3. 16 | * To see all verse for a given tag append search and keyword separated by slash e.g. http://semquran.com#search/heaven 17 | 18 | ### code ### 19 | This is a single page application (SPA) written using HTML5, JQuery Mobile and Backbone. The [backend](https://github.com/hasankhan/semantic-quran-api) is Azure Mobile Service based REST API. 20 | -------------------------------------------------------------------------------- /quranweb.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 2013 4 | VisualStudioVersion = 12.0.30501.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{E24C65DC-7377-472B-9ABA-BC803B73C61A}") = "quranweb", "http://localhost:21777", "{5BDB860E-A664-4C18-8BE2-05677B9BFAEA}" 7 | ProjectSection(WebsiteProperties) = preProject 8 | UseIISExpress = "true" 9 | TargetFrameworkMoniker = ".NETFramework,Version%3Dv4.0" 10 | Debug.AspNetCompiler.VirtualPath = "/localhost_21777" 11 | Debug.AspNetCompiler.PhysicalPath = "." 12 | Debug.AspNetCompiler.TargetPath = "PrecompiledWeb\localhost_21777\" 13 | Debug.AspNetCompiler.Updateable = "true" 14 | Debug.AspNetCompiler.ForceOverwrite = "true" 15 | Debug.AspNetCompiler.FixedNames = "false" 16 | Debug.AspNetCompiler.Debug = "True" 17 | Release.AspNetCompiler.VirtualPath = "/localhost_21777" 18 | Release.AspNetCompiler.PhysicalPath = "." 19 | Release.AspNetCompiler.TargetPath = "PrecompiledWeb\localhost_21777\" 20 | Release.AspNetCompiler.Updateable = "true" 21 | Release.AspNetCompiler.ForceOverwrite = "true" 22 | Release.AspNetCompiler.FixedNames = "false" 23 | Release.AspNetCompiler.Debug = "False" 24 | SlnRelativePath = "." 25 | EndProjectSection 26 | EndProject 27 | Global 28 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 29 | Debug|Any CPU = Debug|Any CPU 30 | EndGlobalSection 31 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 32 | {5BDB860E-A664-4C18-8BE2-05677B9BFAEA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 33 | {5BDB860E-A664-4C18-8BE2-05677B9BFAEA}.Debug|Any CPU.Build.0 = Debug|Any CPU 34 | EndGlobalSection 35 | GlobalSection(SolutionProperties) = preSolution 36 | HideSolutionNode = FALSE 37 | EndGlobalSection 38 | EndGlobal 39 | -------------------------------------------------------------------------------- /css/site.css: -------------------------------------------------------------------------------- 1 | .hidden { 2 | display: none !important; 3 | } 4 | 5 | h1 a, .ayahRef a, .tag a { 6 | text-decoration:none; 7 | font: inherit !important; 8 | color: inherit !important; 9 | } 10 | 11 | .result { 12 | background-color: beige; 13 | border: 1px solid black; 14 | border-top-width: 0; 15 | overflow: hidden 16 | } 17 | 18 | .result .ayahWrapper { 19 | text-align: right; 20 | padding: 10px; 21 | } 22 | 23 | .result .ayah { 24 | font-size: 35px; 25 | } 26 | 27 | .result .ayahRef { 28 | font-size: 25px; 29 | float: left; 30 | } 31 | 32 | .result .surahRef { 33 | cursor: pointer 34 | } 35 | 36 | .result .translation { 37 | font-size: 15px; 38 | padding-top: 5px; 39 | } 40 | 41 | li.tag { 42 | float: left; 43 | white-space: nowrap; 44 | display: inline-block; 45 | list-style-type: none; 46 | border: 1px solid gray; 47 | border-radius: 10px; 48 | background-color: beige; 49 | margin: 1px; 50 | font-size: 10px; 51 | text-align: center; 52 | } 53 | 54 | li.tag a, li.addTag { 55 | display: inline-block; 56 | width: 86px; 57 | padding: 5px; 58 | overflow: hidden; 59 | -ms-text-overflow: ellipsis; 60 | -o-text-overflow: ellipsis; 61 | text-overflow: ellipsis; 62 | } 63 | 64 | .loggedin-action { 65 | display: none; 66 | } 67 | 68 | .loggedin .loggedin-action { 69 | display: block; 70 | } 71 | 72 | li.addTag { 73 | background-color: lightgreen; 74 | display: none; 75 | cursor: pointer; 76 | } 77 | 78 | .loggedin li.addTag { 79 | display: inline-block; 80 | } 81 | 82 | span.delete { 83 | margin-right: 5px; 84 | color: red; 85 | cursor: pointer; 86 | } 87 | 88 | .loggedin span.delete { 89 | display: inline-block; 90 | } 91 | 92 | .results ul { 93 | float: right; 94 | } 95 | 96 | #recentlyAddedTags { 97 | display: inline-block; 98 | max-width: 300px; 99 | margin: 0 auto; 100 | padding: 10px; 101 | } 102 | 103 | #addTagDialogButton { 104 | max-width: 250px; 105 | margin: 0 auto; 106 | } 107 | 108 | #loadMore { 109 | display: none; 110 | } 111 | -------------------------------------------------------------------------------- /js/quranclient.js: -------------------------------------------------------------------------------- 1 | var QuranClient = (function () { 2 | 3 | function QuranClient(host, key) { 4 | this.host = host; 5 | 6 | var clientLoaded = typeof WindowsAzure !== 'undefined' && 7 | typeof WindowsAzure.MobileServiceClient !== 'undefined'; 8 | 9 | if (clientLoaded) { 10 | this.client = new WindowsAzure.MobileServiceClient(host, key); 11 | this.canLogin = true; 12 | } 13 | 14 | Object.defineProperty(this, 'loggedIn', { 15 | get: function () { 16 | return this.currentUser !== null; 17 | } 18 | }); 19 | 20 | Object.defineProperty(this, 'currentUser', { 21 | get: function () { 22 | return this.client ? this.client.currentUser : null; 23 | } 24 | }); 25 | } 26 | 27 | QuranClient.prototype.addTag = function (surah, verse, tag) { 28 | return this._post('tag/' + tag + '/' + surah + '/' + verse); 29 | }; 30 | 31 | QuranClient.prototype.removeTag = function (surah, verse, tag) { 32 | return this._del('tag/' + tag + '/' + surah + '/' + verse); 33 | }; 34 | 35 | QuranClient.prototype.findVersesByTag = function (tag) { 36 | return this._get('tag/' + tag); 37 | }; 38 | 39 | QuranClient.prototype.getVersesByRange = function (surah, start, end) { 40 | var path = 'verse/' + surah; 41 | 42 | if (start && end) { 43 | path += "/" + start + "-" + end; 44 | } 45 | else if (start) { 46 | path += "/" + start; 47 | } 48 | 49 | return this._get(path); 50 | }; 51 | 52 | QuranClient.prototype.listSurahs = function () { 53 | return this._get('surah'); 54 | }; 55 | 56 | QuranClient.prototype.listTags = function () { 57 | return this._get('tag'); 58 | }; 59 | 60 | QuranClient.prototype.login = function (provider, options) { 61 | if (this.client) { 62 | return this.client.loginWithOptions(provider, options); 63 | } 64 | }; 65 | 66 | QuranClient.prototype._onLoading = function (state) { 67 | if (typeof this.onLoading === 'function') { 68 | this.onLoading(state); 69 | } 70 | }; 71 | 72 | QuranClient.prototype._get = function (path) { 73 | return this._invoke(path, { 74 | method: 'get', 75 | }); 76 | }; 77 | 78 | QuranClient.prototype._post = function (path, body) { 79 | return this._invoke(path, { 80 | method: 'post', 81 | body: body 82 | }); 83 | }; 84 | 85 | QuranClient.prototype._del = function (path, body) { 86 | return this._invoke(path, { 87 | method: 'delete', 88 | body: body 89 | }); 90 | }; 91 | 92 | QuranClient.prototype._invoke = function (path, settings) { 93 | this._onLoading(true); 94 | 95 | var url = this.host + 'api/' + path, 96 | req = { 97 | type: settings.method.toUpperCase(), 98 | url: url, 99 | data: settings.body, 100 | headers: {} 101 | }; 102 | 103 | if (this.client && this.client.currentUser) { 104 | req.headers['x-zumo-auth'] = this.client.currentUser.mobileServiceAuthenticationToken; 105 | } 106 | 107 | var self = this; 108 | return $.ajax(req).always(function () { 109 | self._onLoading(false); 110 | }); 111 | }; 112 | 113 | return QuranClient; 114 | 115 | })(); -------------------------------------------------------------------------------- /js/mousetrap.min.js: -------------------------------------------------------------------------------- 1 | /* mousetrap v1.4.6 craig.is/killing/mice */ 2 | (function(J,r,f){function s(a,b,d){a.addEventListener?a.addEventListener(b,d,!1):a.attachEvent("on"+b,d)}function A(a){if("keypress"==a.type){var b=String.fromCharCode(a.which);a.shiftKey||(b=b.toLowerCase());return b}return h[a.which]?h[a.which]:B[a.which]?B[a.which]:String.fromCharCode(a.which).toLowerCase()}function t(a){a=a||{};var b=!1,d;for(d in n)a[d]?b=!0:n[d]=0;b||(u=!1)}function C(a,b,d,c,e,v){var g,k,f=[],h=d.type;if(!l[a])return[];"keyup"==h&&w(a)&&(b=[a]);for(g=0;gg||h.hasOwnProperty(g)&&(p[h[g]]=g)}e=p[d]?"keydown":"keypress"}"keypress"==e&&f.length&&(e="keydown");return{key:c,modifiers:f,action:e}}function F(a,b,d,c,e){q[a+":"+d]=b;a=a.replace(/\s+/g," ");var f=a.split(" ");1":".","?":"/","|":"\\"},G={option:"alt",command:"meta","return":"enter",escape:"esc",mod:/Mac|iPod|iPhone|iPad/.test(navigator.platform)?"meta":"ctrl"},p,l={},q={},n={},D,z=!1,I=!1,u=!1;for(f=1;20>f;++f)h[111+f]="f"+f;for(f=0;9>=f;++f)h[f+96]=f;s(r,"keypress",y);s(r,"keydown",y);s(r,"keyup",y);var m={bind:function(a,b,d){a=a instanceof Array?a:[a];for(var c=0;c 2 | 3 | Symantic Quran 4 | 5 | 6 | 17 | 18 | 19 | 20 | 21 | Menu 22 | Semantic Quran 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | بِسْمِ اللَّهِ الرَّحْمَٰنِ الرَّحِيمِ 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | Select Surah 43 | 44 | 45 | 50 | Feedback 51 | 52 | 56 | View source 57 | 58 | 59 | 60 | 61 | 62 | 63 | Login 64 | 65 | 66 | Suggested Tags 67 | 68 | 69 | marriage 70 | 71 | 72 | dua 73 | 74 | 75 | heaven 76 | 77 | 78 | hell 79 | 80 | 81 | 82 | 83 | Latest Tags 84 | 85 | 86 | 87 | 88 | 89 | 90 | Please contribute a thoughtful tag for ayah 91 | Tag 92 | 96 | 97 | 98 | Cancel 99 | Save 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 112 | 117 | 124 | 130 | 155 | 165 | 166 |