mirror of
https://gitlab.com/walljm/dynamicbible.git
synced 2025-07-23 07:19:50 -04:00
initial start at porting ionic to angular material. this just deals with the website. i hate ionics new web components. despise isn't to strong a word.
this also introduces a redux pattern into the state management, which should significantly improve the complexity of the prev code.
This commit is contained in:
parent
87cc470bfa
commit
b283898f8c
18
app/db/.browserslistrc
Normal file
18
app/db/.browserslistrc
Normal file
@ -0,0 +1,18 @@
|
||||
# This file is used by the build system to adjust CSS and JS output to support the specified browsers below.
|
||||
# For additional information regarding the format and rule options, please see:
|
||||
# https://github.com/browserslist/browserslist#queries
|
||||
|
||||
# For the full list of supported browsers by the Angular framework, please see:
|
||||
# https://angular.io/guide/browser-support
|
||||
|
||||
# You can see what browsers were selected by your queries by running:
|
||||
# npx browserslist
|
||||
|
||||
last 1 Chrome version
|
||||
last 1 Firefox version
|
||||
last 2 Edge major versions
|
||||
last 2 Safari major versions
|
||||
last 2 iOS major versions
|
||||
Firefox ESR
|
||||
not IE 9-10 # Angular support for IE 9-10 has been deprecated and will be removed as of Angular v11. To opt-in, remove the 'not' prefix on this line.
|
||||
not IE 11 # Angular supports IE 11 only as an opt-in. To opt-in, remove the 'not' prefix on this line.
|
16
app/db/.editorconfig
Normal file
16
app/db/.editorconfig
Normal file
@ -0,0 +1,16 @@
|
||||
# Editor configuration, see https://editorconfig.org
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.ts]
|
||||
quote_type = single
|
||||
|
||||
[*.md]
|
||||
max_line_length = off
|
||||
trim_trailing_whitespace = false
|
46
app/db/.gitignore
vendored
Normal file
46
app/db/.gitignore
vendored
Normal file
@ -0,0 +1,46 @@
|
||||
# See http://help.github.com/ignore-files/ for more about ignoring files.
|
||||
|
||||
# compiled output
|
||||
/dist
|
||||
/tmp
|
||||
/out-tsc
|
||||
# Only exists if Bazel was run
|
||||
/bazel-out
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
|
||||
# profiling files
|
||||
chrome-profiler-events*.json
|
||||
speed-measure-plugin*.json
|
||||
|
||||
# IDEs and editors
|
||||
/.idea
|
||||
.project
|
||||
.classpath
|
||||
.c9/
|
||||
*.launch
|
||||
.settings/
|
||||
*.sublime-workspace
|
||||
|
||||
# IDE - VSCode
|
||||
.vscode/*
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
.history/*
|
||||
|
||||
# misc
|
||||
/.sass-cache
|
||||
/connect.lock
|
||||
/coverage
|
||||
/libpeerconnection.log
|
||||
npm-debug.log
|
||||
yarn-error.log
|
||||
testem.log
|
||||
/typings
|
||||
|
||||
# System Files
|
||||
.DS_Store
|
||||
Thumbs.db
|
27
app/db/README.md
Normal file
27
app/db/README.md
Normal file
@ -0,0 +1,27 @@
|
||||
# Db
|
||||
|
||||
This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 10.0.3.
|
||||
|
||||
## Development server
|
||||
|
||||
Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files.
|
||||
|
||||
## Code scaffolding
|
||||
|
||||
Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.
|
||||
|
||||
## Build
|
||||
|
||||
Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build.
|
||||
|
||||
## Running unit tests
|
||||
|
||||
Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
|
||||
|
||||
## Running end-to-end tests
|
||||
|
||||
Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/).
|
||||
|
||||
## Further help
|
||||
|
||||
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md).
|
128
app/db/angular.json
Normal file
128
app/db/angular.json
Normal file
@ -0,0 +1,128 @@
|
||||
{
|
||||
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
|
||||
"version": 1,
|
||||
"newProjectRoot": "projects",
|
||||
"projects": {
|
||||
"db": {
|
||||
"projectType": "application",
|
||||
"schematics": {
|
||||
"@schematics/angular:component": {
|
||||
"style": "scss"
|
||||
}
|
||||
},
|
||||
"root": "",
|
||||
"sourceRoot": "src",
|
||||
"prefix": "app",
|
||||
"architect": {
|
||||
"build": {
|
||||
"builder": "@angular-devkit/build-angular:browser",
|
||||
"options": {
|
||||
"outputPath": "dist/db",
|
||||
"index": "src/index.html",
|
||||
"main": "src/main.ts",
|
||||
"polyfills": "src/polyfills.ts",
|
||||
"tsConfig": "tsconfig.app.json",
|
||||
"aot": true,
|
||||
"assets": [
|
||||
"src/favicon.ico",
|
||||
"src/assets"
|
||||
],
|
||||
"styles": [
|
||||
"src/styles.scss"
|
||||
],
|
||||
"scripts": []
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"fileReplacements": [
|
||||
{
|
||||
"replace": "src/environments/environment.ts",
|
||||
"with": "src/environments/environment.prod.ts"
|
||||
}
|
||||
],
|
||||
"optimization": true,
|
||||
"outputHashing": "all",
|
||||
"sourceMap": false,
|
||||
"extractCss": true,
|
||||
"namedChunks": false,
|
||||
"extractLicenses": true,
|
||||
"vendorChunk": false,
|
||||
"buildOptimizer": true,
|
||||
"budgets": [
|
||||
{
|
||||
"type": "initial",
|
||||
"maximumWarning": "2mb",
|
||||
"maximumError": "5mb"
|
||||
},
|
||||
{
|
||||
"type": "anyComponentStyle",
|
||||
"maximumWarning": "6kb",
|
||||
"maximumError": "10kb"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"serve": {
|
||||
"builder": "@angular-devkit/build-angular:dev-server",
|
||||
"options": {
|
||||
"browserTarget": "db:build"
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"browserTarget": "db:build:production"
|
||||
}
|
||||
}
|
||||
},
|
||||
"extract-i18n": {
|
||||
"builder": "@angular-devkit/build-angular:extract-i18n",
|
||||
"options": {
|
||||
"browserTarget": "db:build"
|
||||
}
|
||||
},
|
||||
"test": {
|
||||
"builder": "@angular-devkit/build-angular:karma",
|
||||
"options": {
|
||||
"main": "src/test.ts",
|
||||
"polyfills": "src/polyfills.ts",
|
||||
"tsConfig": "tsconfig.spec.json",
|
||||
"karmaConfig": "karma.conf.js",
|
||||
"assets": [
|
||||
"src/favicon.ico",
|
||||
"src/assets"
|
||||
],
|
||||
"styles": [
|
||||
"src/styles.scss"
|
||||
],
|
||||
"scripts": []
|
||||
}
|
||||
},
|
||||
"lint": {
|
||||
"builder": "@angular-devkit/build-angular:tslint",
|
||||
"options": {
|
||||
"tsConfig": [
|
||||
"tsconfig.app.json",
|
||||
"tsconfig.spec.json",
|
||||
"e2e/tsconfig.json"
|
||||
],
|
||||
"exclude": [
|
||||
"**/node_modules/**"
|
||||
]
|
||||
}
|
||||
},
|
||||
"e2e": {
|
||||
"builder": "@angular-devkit/build-angular:protractor",
|
||||
"options": {
|
||||
"protractorConfig": "e2e/protractor.conf.js",
|
||||
"devServerTarget": "db:serve"
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"devServerTarget": "db:serve:production"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}},
|
||||
"defaultProject": "db"
|
||||
}
|
36
app/db/e2e/protractor.conf.js
Normal file
36
app/db/e2e/protractor.conf.js
Normal file
@ -0,0 +1,36 @@
|
||||
// @ts-check
|
||||
// Protractor configuration file, see link for more information
|
||||
// https://github.com/angular/protractor/blob/master/lib/config.ts
|
||||
|
||||
const { SpecReporter, StacktraceOption } = require('jasmine-spec-reporter');
|
||||
|
||||
/**
|
||||
* @type { import("protractor").Config }
|
||||
*/
|
||||
exports.config = {
|
||||
allScriptsTimeout: 11000,
|
||||
specs: [
|
||||
'./src/**/*.e2e-spec.ts'
|
||||
],
|
||||
capabilities: {
|
||||
browserName: 'chrome'
|
||||
},
|
||||
directConnect: true,
|
||||
baseUrl: 'http://localhost:4200/',
|
||||
framework: 'jasmine',
|
||||
jasmineNodeOpts: {
|
||||
showColors: true,
|
||||
defaultTimeoutInterval: 30000,
|
||||
print: function() {}
|
||||
},
|
||||
onPrepare() {
|
||||
require('ts-node').register({
|
||||
project: require('path').join(__dirname, './tsconfig.json')
|
||||
});
|
||||
jasmine.getEnv().addReporter(new SpecReporter({
|
||||
spec: {
|
||||
displayStacktrace: StacktraceOption.PRETTY
|
||||
}
|
||||
}));
|
||||
}
|
||||
};
|
23
app/db/e2e/src/app.e2e-spec.ts
Normal file
23
app/db/e2e/src/app.e2e-spec.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import { AppPage } from './app.po';
|
||||
import { browser, logging } from 'protractor';
|
||||
|
||||
describe('workspace-project App', () => {
|
||||
let page: AppPage;
|
||||
|
||||
beforeEach(() => {
|
||||
page = new AppPage();
|
||||
});
|
||||
|
||||
it('should display welcome message', () => {
|
||||
page.navigateTo();
|
||||
expect(page.getTitleText()).toEqual('db app is running!');
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
// Assert that there are no errors emitted from the browser
|
||||
const logs = await browser.manage().logs().get(logging.Type.BROWSER);
|
||||
expect(logs).not.toContain(jasmine.objectContaining({
|
||||
level: logging.Level.SEVERE,
|
||||
} as logging.Entry));
|
||||
});
|
||||
});
|
11
app/db/e2e/src/app.po.ts
Normal file
11
app/db/e2e/src/app.po.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import { browser, by, element } from 'protractor';
|
||||
|
||||
export class AppPage {
|
||||
navigateTo(): Promise<unknown> {
|
||||
return browser.get(browser.baseUrl) as Promise<unknown>;
|
||||
}
|
||||
|
||||
getTitleText(): Promise<string> {
|
||||
return element(by.css('app-root .content span')).getText() as Promise<string>;
|
||||
}
|
||||
}
|
14
app/db/e2e/tsconfig.json
Normal file
14
app/db/e2e/tsconfig.json
Normal file
@ -0,0 +1,14 @@
|
||||
/* To learn more about this file see: https://angular.io/config/tsconfig. */
|
||||
{
|
||||
"extends": "../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../out-tsc/e2e",
|
||||
"module": "commonjs",
|
||||
"target": "es2018",
|
||||
"types": [
|
||||
"jasmine",
|
||||
"jasminewd2",
|
||||
"node"
|
||||
]
|
||||
}
|
||||
}
|
32
app/db/karma.conf.js
Normal file
32
app/db/karma.conf.js
Normal file
@ -0,0 +1,32 @@
|
||||
// Karma configuration file, see link for more information
|
||||
// https://karma-runner.github.io/1.0/config/configuration-file.html
|
||||
|
||||
module.exports = function (config) {
|
||||
config.set({
|
||||
basePath: '',
|
||||
frameworks: ['jasmine', '@angular-devkit/build-angular'],
|
||||
plugins: [
|
||||
require('karma-jasmine'),
|
||||
require('karma-chrome-launcher'),
|
||||
require('karma-jasmine-html-reporter'),
|
||||
require('karma-coverage-istanbul-reporter'),
|
||||
require('@angular-devkit/build-angular/plugins/karma')
|
||||
],
|
||||
client: {
|
||||
clearContext: false // leave Jasmine Spec Runner output visible in browser
|
||||
},
|
||||
coverageIstanbulReporter: {
|
||||
dir: require('path').join(__dirname, './coverage/db'),
|
||||
reports: ['html', 'lcovonly', 'text-summary'],
|
||||
fixWebpackSourcePaths: true
|
||||
},
|
||||
reporters: ['progress', 'kjhtml'],
|
||||
port: 9876,
|
||||
colors: true,
|
||||
logLevel: config.LOG_INFO,
|
||||
autoWatch: true,
|
||||
browsers: ['Chrome'],
|
||||
singleRun: false,
|
||||
restartOnFileChange: true
|
||||
});
|
||||
};
|
15243
app/db/package-lock.json
generated
Normal file
15243
app/db/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
53
app/db/package.json
Normal file
53
app/db/package.json
Normal file
@ -0,0 +1,53 @@
|
||||
{
|
||||
"name": "db",
|
||||
"version": "0.0.0",
|
||||
"scripts": {
|
||||
"ng": "ng",
|
||||
"start": "ng serve",
|
||||
"build": "ng build",
|
||||
"test": "ng test",
|
||||
"lint": "ng lint",
|
||||
"e2e": "ng e2e"
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@angular/animations": "~10.0.4",
|
||||
"@angular/cdk": "^10.0.2",
|
||||
"@angular/common": "~10.0.4",
|
||||
"@angular/compiler": "~10.0.4",
|
||||
"@angular/core": "~10.0.4",
|
||||
"@angular/forms": "~10.0.4",
|
||||
"@angular/material": "^10.0.2",
|
||||
"@angular/platform-browser": "~10.0.4",
|
||||
"@angular/platform-browser-dynamic": "~10.0.4",
|
||||
"@angular/router": "~10.0.4",
|
||||
"@types/mathjs": "^6.0.5",
|
||||
"component": "^1.1.0",
|
||||
"mathjs": "^7.0.2",
|
||||
"redux": "^4.0.5",
|
||||
"reselect": "^4.0.0",
|
||||
"rxjs": "~6.5.5",
|
||||
"tslib": "^2.0.0",
|
||||
"zone.js": "~0.10.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "~0.1000.3",
|
||||
"@angular/cli": "~10.0.3",
|
||||
"@angular/compiler-cli": "~10.0.4",
|
||||
"@types/node": "^12.11.1",
|
||||
"@types/jasmine": "~3.5.0",
|
||||
"@types/jasminewd2": "~2.0.3",
|
||||
"codelyzer": "^6.0.0",
|
||||
"jasmine-core": "~3.5.0",
|
||||
"jasmine-spec-reporter": "~5.0.0",
|
||||
"karma": "~5.0.0",
|
||||
"karma-chrome-launcher": "~3.1.0",
|
||||
"karma-coverage-istanbul-reporter": "~3.0.2",
|
||||
"karma-jasmine": "~3.3.0",
|
||||
"karma-jasmine-html-reporter": "^1.5.0",
|
||||
"protractor": "~7.0.0",
|
||||
"ts-node": "~8.3.0",
|
||||
"tslint": "~6.1.0",
|
||||
"typescript": "~3.9.5"
|
||||
}
|
||||
}
|
25
app/db/src/app/app-routing.module.ts
Normal file
25
app/db/src/app/app-routing.module.ts
Normal file
@ -0,0 +1,25 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { Routes, RouterModule } from '@angular/router';
|
||||
import { SearchPage } from './search/components/search-page/search.page';
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
redirectTo: 'search/',
|
||||
pathMatch: 'full',
|
||||
},
|
||||
{
|
||||
path: 'search/',
|
||||
component: SearchPage,
|
||||
},
|
||||
{
|
||||
path: 'search/:term',
|
||||
component: SearchPage,
|
||||
},
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forRoot(routes)],
|
||||
exports: [RouterModule],
|
||||
})
|
||||
export class AppRoutingModule {}
|
25
app/db/src/app/app.component.html
Normal file
25
app/db/src/app/app.component.html
Normal file
@ -0,0 +1,25 @@
|
||||
<body>
|
||||
<mat-sidenav-container class="sidenav-container">
|
||||
<mat-sidenav
|
||||
#drawer
|
||||
class="sidenav"
|
||||
fixedInViewport
|
||||
[attr.role]="(isHandset$ | async) ? 'dialog' : 'navigation'"
|
||||
[mode]="'over'"
|
||||
[opened]="false"
|
||||
>
|
||||
<mat-toolbar>Pages</mat-toolbar>
|
||||
<mat-nav-list>
|
||||
<a
|
||||
*ngFor="let p of mainPages$ | async"
|
||||
mat-list-item
|
||||
[routerLink]="['/dashboard']"
|
||||
><mat-icon color="accent">{{ p.icon }}</mat-icon> {{ p.title }}</a
|
||||
>
|
||||
</mat-nav-list>
|
||||
</mat-sidenav>
|
||||
<mat-sidenav-content>
|
||||
<router-outlet></router-outlet>
|
||||
</mat-sidenav-content>
|
||||
</mat-sidenav-container>
|
||||
</body>
|
9
app/db/src/app/app.component.scss
Normal file
9
app/db/src/app/app.component.scss
Normal file
@ -0,0 +1,9 @@
|
||||
.sidenav {
|
||||
max-width: 30rem;
|
||||
min-width: 20rem;
|
||||
|
||||
mat-icon {
|
||||
padding-right: 1rem;
|
||||
color: var(--primary-color);
|
||||
}
|
||||
}
|
35
app/db/src/app/app.component.spec.ts
Normal file
35
app/db/src/app/app.component.spec.ts
Normal file
@ -0,0 +1,35 @@
|
||||
import { TestBed, async } from '@angular/core/testing';
|
||||
import { RouterTestingModule } from '@angular/router/testing';
|
||||
import { AppComponent } from './app.component';
|
||||
|
||||
describe('AppComponent', () => {
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [
|
||||
RouterTestingModule
|
||||
],
|
||||
declarations: [
|
||||
AppComponent
|
||||
],
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
it('should create the app', () => {
|
||||
const fixture = TestBed.createComponent(AppComponent);
|
||||
const app = fixture.componentInstance;
|
||||
expect(app).toBeTruthy();
|
||||
});
|
||||
|
||||
it(`should have as title 'db'`, () => {
|
||||
const fixture = TestBed.createComponent(AppComponent);
|
||||
const app = fixture.componentInstance;
|
||||
expect(app.title).toEqual('db');
|
||||
});
|
||||
|
||||
it('should render title', () => {
|
||||
const fixture = TestBed.createComponent(AppComponent);
|
||||
fixture.detectChanges();
|
||||
const compiled = fixture.nativeElement;
|
||||
expect(compiled.querySelector('.content span').textContent).toContain('db app is running!');
|
||||
});
|
||||
});
|
38
app/db/src/app/app.component.ts
Normal file
38
app/db/src/app/app.component.ts
Normal file
@ -0,0 +1,38 @@
|
||||
import { Component, ViewChild, AfterViewInit } from '@angular/core';
|
||||
import { AppService } from './services/app.service';
|
||||
import { NavService } from './services/nav.service';
|
||||
import { Observable } from 'rxjs';
|
||||
import { Breakpoints, BreakpointObserver } from '@angular/cdk/layout';
|
||||
import { map, shareReplay } from 'rxjs/operators';
|
||||
import { MatSidenav } from '@angular/material/sidenav';
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
templateUrl: './app.component.html',
|
||||
styleUrls: ['./app.component.scss'],
|
||||
})
|
||||
export class AppComponent implements AfterViewInit {
|
||||
public savedPages$ = this.appService.select((state) => state.savedPages);
|
||||
public mainPages$ = this.appService.select((state) => state.mainPages);
|
||||
|
||||
public isHandset$: Observable<boolean> = this.breakpointObserver
|
||||
.observe(Breakpoints.Handset)
|
||||
.pipe(
|
||||
map((result) => result.matches),
|
||||
shareReplay()
|
||||
);
|
||||
|
||||
@ViewChild(MatSidenav) public sidenav: MatSidenav;
|
||||
|
||||
constructor(
|
||||
private appService: AppService,
|
||||
private navService: NavService,
|
||||
private breakpointObserver: BreakpointObserver
|
||||
) {
|
||||
this.appService.getSavedPages();
|
||||
}
|
||||
|
||||
ngAfterViewInit(): void {
|
||||
this.navService.setSidenav(this.sidenav);
|
||||
}
|
||||
}
|
100
app/db/src/app/app.module.ts
Normal file
100
app/db/src/app/app.module.ts
Normal file
@ -0,0 +1,100 @@
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
|
||||
|
||||
import { AppRoutingModule } from './app-routing.module';
|
||||
import { AppComponent } from './app.component';
|
||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
|
||||
|
||||
import { SearchPage } from './search/components/search-page/search.page';
|
||||
import { PassageComponent } from './search/components/passage/passage';
|
||||
|
||||
import { MatCheckboxModule } from '@angular/material/checkbox';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatInputModule } from '@angular/material/input';
|
||||
import { MatAutocompleteModule } from '@angular/material/autocomplete';
|
||||
import { MatDatepickerModule } from '@angular/material/datepicker';
|
||||
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||
import { MatRadioModule } from '@angular/material/radio';
|
||||
import { MatSelectModule } from '@angular/material/select';
|
||||
import { MatSliderModule } from '@angular/material/slider';
|
||||
import { MatSlideToggleModule } from '@angular/material/slide-toggle';
|
||||
import { MatMenuModule } from '@angular/material/menu';
|
||||
import { MatSidenavModule } from '@angular/material/sidenav';
|
||||
import { MatToolbarModule } from '@angular/material/toolbar';
|
||||
import { MatListModule } from '@angular/material/list';
|
||||
import { MatGridListModule } from '@angular/material/grid-list';
|
||||
import { MatCardModule } from '@angular/material/card';
|
||||
import { MatStepperModule } from '@angular/material/stepper';
|
||||
import { MatTabsModule } from '@angular/material/tabs';
|
||||
import { MatExpansionModule } from '@angular/material/expansion';
|
||||
import { MatButtonToggleModule } from '@angular/material/button-toggle';
|
||||
import { MatChipsModule } from '@angular/material/chips';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
||||
import { MatProgressBarModule } from '@angular/material/progress-bar';
|
||||
import { MatDialogModule } from '@angular/material/dialog';
|
||||
import { MatTooltipModule } from '@angular/material/tooltip';
|
||||
import { MatSnackBarModule } from '@angular/material/snack-bar';
|
||||
import { MatTableModule } from '@angular/material/table';
|
||||
import { MatSortModule } from '@angular/material/sort';
|
||||
import { MatPaginatorModule } from '@angular/material/paginator';
|
||||
import { MatBadgeModule } from '@angular/material/badge';
|
||||
import { MatBottomSheetModule } from '@angular/material/bottom-sheet';
|
||||
import { MatDividerModule } from '@angular/material/divider';
|
||||
import { MatNativeDateModule, MatRippleModule } from '@angular/material/core';
|
||||
import { MatTreeModule } from '@angular/material/tree';
|
||||
|
||||
@NgModule({
|
||||
declarations: [AppComponent, SearchPage, PassageComponent],
|
||||
imports: [
|
||||
BrowserModule.withServerTransition({ appId: 'ng-cli-universal' }),
|
||||
HttpClientModule,
|
||||
|
||||
ReactiveFormsModule,
|
||||
FormsModule,
|
||||
AppRoutingModule,
|
||||
BrowserAnimationsModule,
|
||||
|
||||
MatSidenavModule,
|
||||
MatToolbarModule,
|
||||
MatIconModule,
|
||||
MatAutocompleteModule,
|
||||
MatBadgeModule,
|
||||
MatBottomSheetModule,
|
||||
MatButtonModule,
|
||||
MatButtonToggleModule,
|
||||
MatCardModule,
|
||||
MatCheckboxModule,
|
||||
MatChipsModule,
|
||||
MatStepperModule,
|
||||
MatDatepickerModule,
|
||||
MatDialogModule,
|
||||
MatDividerModule,
|
||||
MatExpansionModule,
|
||||
MatGridListModule,
|
||||
MatInputModule,
|
||||
MatListModule,
|
||||
MatMenuModule,
|
||||
MatNativeDateModule,
|
||||
MatPaginatorModule,
|
||||
MatProgressBarModule,
|
||||
MatProgressSpinnerModule,
|
||||
MatRadioModule,
|
||||
MatRippleModule,
|
||||
MatSelectModule,
|
||||
MatSliderModule,
|
||||
MatSlideToggleModule,
|
||||
MatSnackBarModule,
|
||||
MatSortModule,
|
||||
MatTableModule,
|
||||
MatTabsModule,
|
||||
MatTooltipModule,
|
||||
MatTreeModule,
|
||||
MatFormFieldModule,
|
||||
],
|
||||
providers: [],
|
||||
bootstrap: [AppComponent],
|
||||
})
|
||||
export class AppModule {}
|
295
app/db/src/app/common/bible-reference.spec.ts
Normal file
295
app/db/src/app/common/bible-reference.spec.ts
Normal file
@ -0,0 +1,295 @@
|
||||
import { BibleReference } from './bible-reference';
|
||||
|
||||
describe('Reference', () => {
|
||||
it('Should parse the reference: Gen 1:1', () => {
|
||||
const ref = new BibleReference('Gen 1:1').toString();
|
||||
expect(ref).toBe('Genesis 1:1');
|
||||
});
|
||||
|
||||
it('Should parse the reference: Gen 1:1-11', () => {
|
||||
const ref = new BibleReference('Gen 1:1-11').toString();
|
||||
expect(ref).toBe('Genesis 1:1 - 11');
|
||||
});
|
||||
|
||||
it('Should parse the reference: Gen 1-2', () => {
|
||||
const ref = new BibleReference('Gen 1-2').toString();
|
||||
expect(ref).toBe('Genesis 1:1 - 2:*');
|
||||
});
|
||||
|
||||
it('Should parse the reference: John 3:16', () => {
|
||||
const ref = new BibleReference('John 3:16').toString();
|
||||
expect(ref).toBe('John 3:16');
|
||||
});
|
||||
|
||||
it('Should parse the reference: John 3-6', () => {
|
||||
const ref = new BibleReference('Jn 3-6').toString();
|
||||
expect(ref).toBe('John 3:1 - 6:*');
|
||||
});
|
||||
|
||||
it('Should parse the reference: 1 Corinthians 3:5-6:5', () => {
|
||||
const ref = new BibleReference('1 Corinthians 3:5-6:5').toString();
|
||||
expect(ref).toBe('1 Corinthians 3:5 - 6:5');
|
||||
});
|
||||
|
||||
it('Should parse the reference: 2 Samuel 5:5-6:5', () => {
|
||||
expect(new BibleReference('II sam 5:5-6:5').toString()).toBe('2 Samuel 5:5 - 6:5');
|
||||
});
|
||||
|
||||
const booknames = [
|
||||
{ abbr: 'gen', actual: 'Genesis' },
|
||||
{ abbr: 'ge', actual: 'Genesis' },
|
||||
{ abbr: 'genesis', actual: 'Genesis' },
|
||||
{ abbr: 'gn', actual: 'Genesis' },
|
||||
{ abbr: 'exodus', actual: 'Exodus' },
|
||||
{ abbr: 'ex', actual: 'Exodus' },
|
||||
{ abbr: 'exo', actual: 'Exodus' },
|
||||
{ abbr: 'exod', actual: 'Exodus' },
|
||||
{ abbr: 'exd', actual: 'Exodus' },
|
||||
{ abbr: 'leviticus', actual: 'Leviticus' },
|
||||
{ abbr: 'lev', actual: 'Leviticus' },
|
||||
{ abbr: 'le', actual: 'Leviticus' },
|
||||
{ abbr: 'levi', actual: 'Leviticus' },
|
||||
{ abbr: 'lv', actual: 'Leviticus' },
|
||||
{ abbr: 'numbers', actual: 'Numbers' },
|
||||
{ abbr: 'num', actual: 'Numbers' },
|
||||
{ abbr: 'nu', actual: 'Numbers' },
|
||||
{ abbr: 'numb', actual: 'Numbers' },
|
||||
{ abbr: 'number', actual: 'Numbers' },
|
||||
{ abbr: 'deuteronomy', actual: 'Deuteronomy' },
|
||||
{ abbr: 'deut', actual: 'Deuteronomy' },
|
||||
{ abbr: 'de', actual: 'Deuteronomy' },
|
||||
{ abbr: 'dt', actual: 'Deuteronomy' },
|
||||
{ abbr: 'deu', actual: 'Deuteronomy' },
|
||||
{ abbr: 'joshua', actual: 'Joshua' },
|
||||
{ abbr: 'josh', actual: 'Joshua' },
|
||||
{ abbr: 'jos', actual: 'Joshua' },
|
||||
{ abbr: 'judges', actual: 'Judges' },
|
||||
{ abbr: 'jud', actual: 'Judges' },
|
||||
{ abbr: 'jdg', actual: 'Judges' },
|
||||
{ abbr: 'judg', actual: 'Judges' },
|
||||
{ abbr: 'ruth', actual: 'Ruth' },
|
||||
{ abbr: 'ru', actual: 'Ruth' },
|
||||
{ abbr: '1 samuel', actual: '1 Samuel' },
|
||||
{ abbr: '1 sa', actual: '1 Samuel' },
|
||||
{ abbr: '1 sam', actual: '1 Samuel' },
|
||||
{ abbr: '1 sml', actual: '1 Samuel' },
|
||||
{ abbr: 'i samuel', actual: '1 Samuel' },
|
||||
{ abbr: 'i sa', actual: '1 Samuel' },
|
||||
{ abbr: 'i sam', actual: '1 Samuel' },
|
||||
{ abbr: 'i sml', actual: '1 Samuel' },
|
||||
{ abbr: '1st samuel', actual: '1 Samuel' },
|
||||
{ abbr: '1st sa', actual: '1 Samuel' },
|
||||
{ abbr: '1st sam', actual: '1 Samuel' },
|
||||
{ abbr: '1st sml', actual: '1 Samuel' },
|
||||
{ abbr: 'first samuel', actual: '1 Samuel' },
|
||||
{ abbr: 'first sa', actual: '1 Samuel' },
|
||||
{ abbr: 'first sam', actual: '1 Samuel' },
|
||||
{ abbr: 'first sml', actual: '1 Samuel' },
|
||||
{ abbr: '2 samuel', actual: '2 Samuel' },
|
||||
{ abbr: '2 sa', actual: '2 Samuel' },
|
||||
{ abbr: '2 sam', actual: '2 Samuel' },
|
||||
{ abbr: '2 sml', actual: '2 Samuel' },
|
||||
{ abbr: 'ii samuel', actual: '2 Samuel' },
|
||||
{ abbr: 'ii sa', actual: '2 Samuel' },
|
||||
{ abbr: 'ii sam', actual: '2 Samuel' },
|
||||
{ abbr: 'ii sml', actual: '2 Samuel' },
|
||||
{ abbr: '2nd samuel', actual: '2 Samuel' },
|
||||
{ abbr: '2nd sa', actual: '2 Samuel' },
|
||||
{ abbr: '2nd sam', actual: '2 Samuel' },
|
||||
{ abbr: '2nd sml', actual: '2 Samuel' },
|
||||
{ abbr: 'second samuel', actual: '2 Samuel' },
|
||||
{ abbr: 'second sa', actual: '2 Samuel' },
|
||||
{ abbr: 'second sam', actual: '2 Samuel' },
|
||||
{ abbr: 'second sml', actual: '2 Samuel' },
|
||||
{ abbr: 'sec samuel', actual: '2 Samuel' },
|
||||
{ abbr: 'sec sa', actual: '2 Samuel' },
|
||||
{ abbr: 'sec sam', actual: '2 Samuel' },
|
||||
{ abbr: 'sec sml', actual: '2 Samuel' },
|
||||
{ abbr: '1 kings', actual: '1 Kings' },
|
||||
{ abbr: '1 king', actual: '1 Kings' },
|
||||
{ abbr: '1 kgs', actual: '1 Kings' },
|
||||
{ abbr: '1 kn', actual: '1 Kings' },
|
||||
{ abbr: '1 k', actual: '1 Kings' },
|
||||
{ abbr: '1 ki', actual: '1 Kings' },
|
||||
{ abbr: 'i kings', actual: '1 Kings' },
|
||||
{ abbr: 'i king', actual: '1 Kings' },
|
||||
{ abbr: 'i kgs', actual: '1 Kings' },
|
||||
{ abbr: 'i kn', actual: '1 Kings' },
|
||||
{ abbr: 'i k', actual: '1 Kings' },
|
||||
{ abbr: 'i ki', actual: '1 Kings' },
|
||||
{ abbr: '1st kings', actual: '1 Kings' },
|
||||
{ abbr: '1st king', actual: '1 Kings' },
|
||||
{ abbr: '1st kgs', actual: '1 Kings' },
|
||||
{ abbr: '1st kn', actual: '1 Kings' },
|
||||
{ abbr: '1st k', actual: '1 Kings' },
|
||||
{ abbr: '1st ki', actual: '1 Kings' },
|
||||
{ abbr: 'first kings', actual: '1 Kings' },
|
||||
{ abbr: 'first king', actual: '1 Kings' },
|
||||
{ abbr: 'first kgs', actual: '1 Kings' },
|
||||
{ abbr: 'first kn', actual: '1 Kings' },
|
||||
{ abbr: 'first k', actual: '1 Kings' },
|
||||
{ abbr: 'first ki', actual: '1 Kings' },
|
||||
{ abbr: '2 Kings', actual: '2 Kings' },
|
||||
{ abbr: '2 king', actual: '2 Kings' },
|
||||
{ abbr: '2 kgs', actual: '2 Kings' },
|
||||
{ abbr: '2 kn', actual: '2 Kings' },
|
||||
{ abbr: '2 k', actual: '2 Kings' },
|
||||
{ abbr: '2 ki', actual: '2 Kings' },
|
||||
{ abbr: 'ii kings', actual: '2 Kings' },
|
||||
{ abbr: 'ii king', actual: '2 Kings' },
|
||||
{ abbr: 'ii kgs', actual: '2 Kings' },
|
||||
{ abbr: 'ii kn', actual: '2 Kings' },
|
||||
{ abbr: 'ii k', actual: '2 Kings' },
|
||||
{ abbr: 'ii ki', actual: '2 Kings' },
|
||||
{ abbr: '2nd kings', actual: '2 Kings' },
|
||||
{ abbr: '2nd king', actual: '2 Kings' },
|
||||
{ abbr: '2nd kgs', actual: '2 Kings' },
|
||||
{ abbr: '2nd kn', actual: '2 Kings' },
|
||||
{ abbr: '2nd k', actual: '2 Kings' },
|
||||
{ abbr: '2nd ki', actual: '2 Kings' },
|
||||
{ abbr: 'second kings', actual: '2 Kings' },
|
||||
{ abbr: 'second king', actual: '2 Kings' },
|
||||
{ abbr: 'second kgs', actual: '2 Kings' },
|
||||
{ abbr: 'second kn', actual: '2 Kings' },
|
||||
{ abbr: 'second k', actual: '2 Kings' },
|
||||
{ abbr: 'second ki', actual: '2 Kings' },
|
||||
{ abbr: 'sec kings', actual: '2 Kings' },
|
||||
{ abbr: 'sec king', actual: '2 Kings' },
|
||||
{ abbr: 'sec kgs', actual: '2 Kings' },
|
||||
{ abbr: 'sec kn', actual: '2 Kings' },
|
||||
{ abbr: 'sec k', actual: '2 Kings' },
|
||||
{ abbr: 'sec ki', actual: '2 Kings' },
|
||||
{ abbr: '2 Chronicles', actual: '2 Chronicles' },
|
||||
{ abbr: '2 chronicles', actual: '2 Chronicles' },
|
||||
{ abbr: '2 chron', actual: '2 Chronicles' },
|
||||
{ abbr: '2 ch', actual: '2 Chronicles' },
|
||||
{ abbr: '2 chr', actual: '2 Chronicles' },
|
||||
{ abbr: 'ii Chronicles', actual: '2 Chronicles' },
|
||||
{ abbr: 'ii chronicles', actual: '2 Chronicles' },
|
||||
{ abbr: 'ii chron', actual: '2 Chronicles' },
|
||||
{ abbr: 'ii ch', actual: '2 Chronicles' },
|
||||
{ abbr: 'ii chr', actual: '2 Chronicles' },
|
||||
{ abbr: '2nd Chronicles', actual: '2 Chronicles' },
|
||||
{ abbr: '2nd chronicles', actual: '2 Chronicles' },
|
||||
{ abbr: '2nd chron', actual: '2 Chronicles' },
|
||||
{ abbr: '2nd ch', actual: '2 Chronicles' },
|
||||
{ abbr: '2nd chr', actual: '2 Chronicles' },
|
||||
{ abbr: 'second Chronicles', actual: '2 Chronicles' },
|
||||
{ abbr: 'second chronicles', actual: '2 Chronicles' },
|
||||
{ abbr: 'second chron', actual: '2 Chronicles' },
|
||||
{ abbr: 'second ch', actual: '2 Chronicles' },
|
||||
{ abbr: 'second chr', actual: '2 Chronicles' },
|
||||
{ abbr: 'sec Chronicles', actual: '2 Chronicles' },
|
||||
{ abbr: 'sec chronicles', actual: '2 Chronicles' },
|
||||
{ abbr: 'sec chron', actual: '2 Chronicles' },
|
||||
{ abbr: 'sec ch', actual: '2 Chronicles' },
|
||||
{ abbr: 'sec chr', actual: '2 Chronicles' },
|
||||
{ abbr: '1 Chronicles', actual: '1 Chronicles' },
|
||||
{ abbr: '1 chronicles', actual: '1 Chronicles' },
|
||||
{ abbr: '1 chron', actual: '1 Chronicles' },
|
||||
{ abbr: '1 ch', actual: '1 Chronicles' },
|
||||
{ abbr: '1 chr', actual: '1 Chronicles' },
|
||||
{ abbr: 'i Chronicles', actual: '1 Chronicles' },
|
||||
{ abbr: 'i chronicles', actual: '1 Chronicles' },
|
||||
{ abbr: 'i chron', actual: '1 Chronicles' },
|
||||
{ abbr: 'i ch', actual: '1 Chronicles' },
|
||||
{ abbr: 'i chr', actual: '1 Chronicles' },
|
||||
{ abbr: '1st Chronicles', actual: '1 Chronicles' },
|
||||
{ abbr: '1st chronicles', actual: '1 Chronicles' },
|
||||
{ abbr: '1st chron', actual: '1 Chronicles' },
|
||||
{ abbr: '1st ch', actual: '1 Chronicles' },
|
||||
{ abbr: '1st chr', actual: '1 Chronicles' },
|
||||
{ abbr: 'first Chronicles', actual: '1 Chronicles' },
|
||||
{ abbr: 'first chronicles', actual: '1 Chronicles' },
|
||||
{ abbr: 'first chron', actual: '1 Chronicles' },
|
||||
{ abbr: 'first ch', actual: '1 Chronicles' },
|
||||
{ abbr: 'first chr', actual: '1 Chronicles' },
|
||||
{ abbr: 'ezra', actual: 'Ezra' },
|
||||
{ abbr: 'ezr', actual: 'Ezra' },
|
||||
{ abbr: 'nehemiah', actual: 'Nehemiah' },
|
||||
{ abbr: 'neh', actual: 'Nehemiah' },
|
||||
{ abbr: 'ne', actual: 'Nehemiah' },
|
||||
{ abbr: 'nehamiah', actual: 'Nehemiah' },
|
||||
{ abbr: 'esther', actual: 'Esther' },
|
||||
{ abbr: 'est', actual: 'Esther' },
|
||||
{ abbr: 'es', actual: 'Esther' },
|
||||
{ abbr: 'esth', actual: 'Esther' },
|
||||
{ abbr: 'job', actual: 'Job' },
|
||||
{ abbr: 'jo', actual: 'Job' },
|
||||
{ abbr: 'jb', actual: 'Job' },
|
||||
{ abbr: 'psalms', actual: 'Psalm' },
|
||||
{ abbr: 'ps', actual: 'Psalm' },
|
||||
{ abbr: 'psa', actual: 'Psalm' },
|
||||
{ abbr: 'psalm', actual: 'Psalm' },
|
||||
{ abbr: 'psm', actual: 'Psalm' },
|
||||
{ abbr: 'proverbs', actual: 'Proverbs' },
|
||||
{ abbr: 'prov', actual: 'Proverbs' },
|
||||
{ abbr: 'pr', actual: 'Proverbs' },
|
||||
{ abbr: 'pro', actual: 'Proverbs' },
|
||||
{ abbr: 'proverb', actual: 'Proverbs' },
|
||||
{ abbr: 'prv', actual: 'Proverbs' },
|
||||
{ abbr: 'prvbs', actual: 'Proverbs' },
|
||||
{ abbr: 'ecclesiastes', actual: 'Ecclesiastes' },
|
||||
{ abbr: 'eccl', actual: 'Ecclesiastes' },
|
||||
{ abbr: 'ecc', actual: 'Ecclesiastes' },
|
||||
{ abbr: 'eccles', actual: 'Ecclesiastes' },
|
||||
{ abbr: 'ec', actual: 'Ecclesiastes' },
|
||||
{ abbr: 'ecl', actual: 'Ecclesiastes' },
|
||||
{ abbr: 'ecclesiaste', actual: 'Ecclesiastes' },
|
||||
{ abbr: 'song of solomon', actual: 'Song of Solomon' },
|
||||
{ abbr: 'song of songs', actual: 'Song of Solomon' },
|
||||
{ abbr: 'sos', actual: 'Song of Solomon' },
|
||||
{ abbr: 'ss', actual: 'Song of Solomon' },
|
||||
{ abbr: 'son', actual: 'Song of Solomon' },
|
||||
{ abbr: 'so', actual: 'Song of Solomon' },
|
||||
{ abbr: 'song', actual: 'Song of Solomon' },
|
||||
{ abbr: 'songs', actual: 'Song of Solomon' },
|
||||
{ abbr: 'isaiah', actual: 'Isaiah' },
|
||||
{ abbr: 'is', actual: 'Isaiah' },
|
||||
{ abbr: 'isah', actual: 'Isaiah' },
|
||||
{ abbr: 'isai', actual: 'Isaiah' },
|
||||
{ abbr: 'ia', actual: 'Isaiah' },
|
||||
{ abbr: 'jerimiah', actual: 'Jeremiah' },
|
||||
{ abbr: 'jeremiah', actual: 'Jeremiah' },
|
||||
{ abbr: 'jer', actual: 'Jeremiah' },
|
||||
{ abbr: 'je', actual: 'Jeremiah' },
|
||||
{ abbr: 'jere', actual: 'Jeremiah' },
|
||||
{ abbr: 'lamentations', actual: 'Lamentations' },
|
||||
{ abbr: 'lam', actual: 'Lamentations' },
|
||||
{ abbr: 'la', actual: 'Lamentations' },
|
||||
{ abbr: 'lamentation', actual: 'Lamentations' },
|
||||
];
|
||||
|
||||
for (const bk of booknames) {
|
||||
it('Should parse the references: ' + bk.abbr, () => {
|
||||
const book = BibleReference.parseBook(bk.abbr);
|
||||
expect(book.name).toBe(bk.actual);
|
||||
for (let i = 1; i <= book.last_chapter; i++) {
|
||||
expect(new BibleReference(bk.abbr + ' ' + i).toString()).toBe(bk.actual + ' ' + i + ':1 - *');
|
||||
}
|
||||
|
||||
for (let i = 1; i < book.last_chapter; i++) {
|
||||
expect(new BibleReference(bk.abbr + ' ' + i + '-' + (i + 1)).toString()).toBe(
|
||||
bk.actual + ' ' + i + ':1 - ' + (i + 1) + ':*'
|
||||
);
|
||||
expect(new BibleReference(bk.abbr + ' ' + i + ':3-' + (i + 1) + ':6').toString()).toBe(
|
||||
bk.actual + ' ' + i + ':3 - ' + (i + 1) + ':6'
|
||||
);
|
||||
}
|
||||
|
||||
expect(new BibleReference(bk.abbr + ' 1:4-2:5').toString()).toBe(bk.actual + ' 1:4 - 2:5');
|
||||
expect(new BibleReference(bk.abbr + ' 1:1 - 5').toString()).toBe(bk.actual + ' 1:1 - 5');
|
||||
expect(new BibleReference(bk.abbr + ' 1:4 - 5').toString()).toBe(bk.actual + ' 1:4 - 5');
|
||||
});
|
||||
}
|
||||
const refs = [
|
||||
{ src: '2 sam 3:4-6:8', actual: '2 Samuel 3:4 - 6:8' },
|
||||
// { "src": "gen 50 - ex 2", "actual": "Genesis 50:1 - Exodus 2:*" },
|
||||
];
|
||||
|
||||
for (const ref of refs) {
|
||||
it('Should parse the reference: ' + ref.src, () => {
|
||||
expect(new BibleReference(ref.src).toString()).toBe(ref.actual);
|
||||
});
|
||||
}
|
||||
});
|
1784
app/db/src/app/common/bible-reference.ts
Normal file
1784
app/db/src/app/common/bible-reference.ts
Normal file
File diff suppressed because it is too large
Load Diff
47
app/db/src/app/common/card.component.ts
Normal file
47
app/db/src/app/common/card.component.ts
Normal file
@ -0,0 +1,47 @@
|
||||
import {
|
||||
EventEmitter,
|
||||
Output,
|
||||
Input,
|
||||
ElementRef,
|
||||
Component,
|
||||
} from '@angular/core';
|
||||
import { CardItem, OpenData } from '../models/app-state';
|
||||
|
||||
@Component({
|
||||
template: '',
|
||||
})
|
||||
export class CardComponent {
|
||||
@Output()
|
||||
onItemClicked = new EventEmitter<OpenData>();
|
||||
|
||||
@Output()
|
||||
onClose = new EventEmitter<CardItem>();
|
||||
|
||||
@Input()
|
||||
cardItem: CardItem;
|
||||
|
||||
constructor(protected elementRef: ElementRef) {}
|
||||
|
||||
close(ev) {
|
||||
let translate = 'translate3d(200%, 0, 0)';
|
||||
if (ev != null && ev.direction === 2) {
|
||||
translate = 'translate3d(-200%, 0, 0)';
|
||||
}
|
||||
const d = 250;
|
||||
this.elementRef.nativeElement.parentElement.animate(
|
||||
{
|
||||
transform: ['none', translate],
|
||||
},
|
||||
{
|
||||
fill: 'forwards',
|
||||
duration: d,
|
||||
iterations: 1,
|
||||
easing: 'ease-in-out',
|
||||
}
|
||||
);
|
||||
|
||||
setTimeout(() => {
|
||||
this.onClose.emit(this.cardItem);
|
||||
}, d);
|
||||
}
|
||||
}
|
130
app/db/src/app/common/state-service.ts
Normal file
130
app/db/src/app/common/state-service.ts
Normal file
@ -0,0 +1,130 @@
|
||||
import { PreloadedState, Store, createStore } from 'redux';
|
||||
|
||||
import { BehaviorSubject, Observable } from 'rxjs';
|
||||
import { distinctUntilChanged, map } from 'rxjs/operators';
|
||||
|
||||
/**
|
||||
* A base class from which to extend a service to provide predictable state to components. You should not extend this
|
||||
* class directly; instead, use the {@link createStateService} function to create a base class specific to your service.
|
||||
* @see {@link createStateService}
|
||||
*/
|
||||
class StateService<TState, TAction extends { type: string }> {
|
||||
/** An observable that provides the entire state managed by the service. */
|
||||
public readonly state$: Observable<TState>;
|
||||
|
||||
private readonly store: Store<TState, TAction>;
|
||||
private readonly internalState$: BehaviorSubject<TState>;
|
||||
|
||||
protected constructor(reducer: (state: TState, action: TAction) => TState, initialState: TState) {
|
||||
this.store = createStore(
|
||||
reducer,
|
||||
initialState as PreloadedState<TState>, // this cast is required by Redux's typings, it should have no impact
|
||||
undefined // in the future, we may want to add some middleware to the Redux stores. that goes here!
|
||||
);
|
||||
|
||||
this.internalState$ = new BehaviorSubject<TState>(initialState as TState);
|
||||
|
||||
// BehaviorSubject.asObservable returns a new object, so hold onto it to avoid unnecessary allocations
|
||||
this.state$ = this.internalState$.asObservable();
|
||||
|
||||
this.store.subscribe(() => this.internalState$.next(this.store.getState()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an observable that provides data derived from the state managed by the service.
|
||||
* @param selector A selector that maps the state to the derived data.
|
||||
*/
|
||||
public select<TDerived>(selector: (state: TState) => TDerived): Observable<TDerived> {
|
||||
return this.state$.pipe(map(selector), distinctUntilChanged());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current state managed by the service. **You should only use the current state to validate operations or
|
||||
* help to construct actions to be dispatched.** Try not to expose any state retrieved using this method outside the
|
||||
* derived service class.
|
||||
*/
|
||||
protected getState(): TState {
|
||||
return this.store.getState();
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatches an action to the underlying Redux store.
|
||||
* @param action The action to dispatch.
|
||||
*/
|
||||
protected dispatch(action: TAction) {
|
||||
this.store.dispatch(action);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a deeply-immutable type from the provided type. Objects' properties will be marked as `readonly`, array types
|
||||
* will be replaced with {@link ReadonlyArray}, {@link Map} types will be replaced with {@link ReadonlyMap}, and
|
||||
* {@link Set} types will be replaced with {@link ReadonlySet}.
|
||||
*/
|
||||
export type Immutable<T> = T extends undefined | null | boolean | string | number
|
||||
? T
|
||||
: T extends Array<infer U>
|
||||
? ImmutableArray<U>
|
||||
: T extends Map<infer K, infer V>
|
||||
? ImmutableMap<K, V>
|
||||
: T extends Set<infer M>
|
||||
? ImmutableSet<M>
|
||||
: { readonly [N in keyof T]: Immutable<T[N]> };
|
||||
|
||||
interface ImmutableArray<T> extends ReadonlyArray<Immutable<T>> {}
|
||||
interface ImmutableMap<K, V> extends ReadonlyMap<Immutable<K>, Immutable<V>> {}
|
||||
interface ImmutableSet<T> extends ReadonlySet<Immutable<T>> {}
|
||||
|
||||
// The below type definition is simpler, but it only works with TypeScript 3.7 and above. Swap this in when we upgrade!
|
||||
|
||||
// export type Immutable<T> = T extends undefined | null | boolean | string | number
|
||||
// ? T
|
||||
// : T extends Array<infer U>
|
||||
// ? ReadonlyArray<Immutable<U>>
|
||||
// : T extends Map<infer K, infer V>
|
||||
// ? ReadonlyMap<Immutable<K>, Immutable<V>>
|
||||
// : T extends Set<infer M>
|
||||
// ? ReadonlySet<Immutable<M>>
|
||||
// : { readonly [N in keyof T]: Immutable<T[N]> };
|
||||
|
||||
type IfEquals<X, Y, A = X, B = never> = (<T>() => T extends X ? 1 : 2) extends <T>() => T extends Y ? 1 : 2 ? A : B;
|
||||
type IfImmutable<TState extends {}, TImmutable = TState, TMutable = never> = IfEquals<
|
||||
TState,
|
||||
{ readonly [K in keyof TState]: Immutable<TState[K]> },
|
||||
TImmutable,
|
||||
TMutable
|
||||
>;
|
||||
|
||||
// tslint:disable-next-line
|
||||
type YourStateTypeNeedsToBeImmutable<TState> = {};
|
||||
|
||||
/**
|
||||
* Creates a base class from which to extend a service to provide predictable state to components.
|
||||
*
|
||||
* **If you're looking at this function because calling it yields an error that looks like this:**
|
||||
*
|
||||
* ```text
|
||||
* Type 'YourStateTypeNeedsToBeImmutable<State>' is not a constructor function type.
|
||||
* ```
|
||||
*
|
||||
* **That error means that the type you specified as `TState` (most likely the first parameter and return type of your
|
||||
* reducer) isn't fully immutable.** The Redux pattern requires that you treat your state as completely immutable—that
|
||||
* means that every property in your state hierarchy must be marked as `readonly` and you must use immutable collection
|
||||
* types. You can either do this by hand, or use the {@link Immutable} helper type also exported by this module to
|
||||
* quickly make an entire type hierarchy immutable.
|
||||
*
|
||||
* @param reducer A function that takes the previous state and the dispatched action and returns the new state.
|
||||
* @param initialState The initial state of the service.
|
||||
*/
|
||||
export function createStateService<TState, TAction extends { type: string }>(
|
||||
reducer: (state: TState, action: TAction) => TState,
|
||||
initialState: TState
|
||||
) {
|
||||
const stateServiceClass = class extends StateService<TState, TAction> {
|
||||
constructor() {
|
||||
super(reducer, initialState);
|
||||
}
|
||||
};
|
||||
|
||||
return stateServiceClass as IfImmutable<TState, typeof stateServiceClass, YourStateTypeNeedsToBeImmutable<TState>>;
|
||||
}
|
5
app/db/src/app/constants.ts
Normal file
5
app/db/src/app/constants.ts
Normal file
@ -0,0 +1,5 @@
|
||||
export const PageTitles = {
|
||||
Search: 'Search',
|
||||
Help: 'Help',
|
||||
Settings: 'Settings',
|
||||
};
|
91
app/db/src/app/models/app-state.ts
Normal file
91
app/db/src/app/models/app-state.ts
Normal file
@ -0,0 +1,91 @@
|
||||
export interface AppState {
|
||||
readonly savedPages: readonly SavedPage[];
|
||||
readonly mainPages: readonly Page[];
|
||||
readonly cards: readonly CardItem[];
|
||||
readonly error: Error;
|
||||
readonly paragraphs: HashTable<Paragraph>;
|
||||
readonly displaySettings: DisplaySettings;
|
||||
}
|
||||
|
||||
export interface Error {
|
||||
readonly msg: string;
|
||||
}
|
||||
|
||||
export type Data = BiblePassageResult;
|
||||
|
||||
export interface DisplaySettings {
|
||||
readonly showStrongsAsModal: boolean;
|
||||
readonly appendCardToBottom: boolean;
|
||||
readonly insertCardNextToItem: boolean;
|
||||
readonly fontSize: number;
|
||||
readonly fontFamily: string;
|
||||
readonly showVersesOnNewLine: boolean;
|
||||
readonly showVerseNumbers: boolean;
|
||||
readonly showParagraphs: boolean;
|
||||
readonly showParagraphHeadings: boolean;
|
||||
}
|
||||
|
||||
export interface SavedPage {
|
||||
readonly queries: readonly CardItem[];
|
||||
readonly title: string;
|
||||
}
|
||||
|
||||
export interface CardItem {
|
||||
readonly qry: string;
|
||||
readonly data: Data;
|
||||
readonly type: string;
|
||||
readonly dict: string;
|
||||
}
|
||||
|
||||
export type OpenData = {
|
||||
card: CardItem;
|
||||
qry: string;
|
||||
from_search_bar: boolean;
|
||||
};
|
||||
|
||||
export class Page {
|
||||
readonly title: string;
|
||||
// readonly component: any;
|
||||
// readonly params: any;
|
||||
readonly icon?: string;
|
||||
}
|
||||
|
||||
export interface BiblePassageResult {
|
||||
readonly cs: readonly BibleParagraphPassage[];
|
||||
readonly testament: string;
|
||||
readonly ref: string;
|
||||
}
|
||||
|
||||
export interface BibleParagraph {
|
||||
readonly p: Paragraph;
|
||||
readonly vss: readonly BibleVerse[];
|
||||
}
|
||||
|
||||
export interface BibleParagraphPassage {
|
||||
readonly ch: number;
|
||||
readonly paras: readonly BibleParagraph[];
|
||||
}
|
||||
|
||||
export interface BiblePassage {
|
||||
readonly ch: number;
|
||||
readonly vss: readonly BibleVerse[];
|
||||
}
|
||||
|
||||
export interface BibleVerse {
|
||||
readonly v: number;
|
||||
readonly w: readonly [
|
||||
{
|
||||
readonly t: string;
|
||||
readonly s: string;
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
export interface Paragraph {
|
||||
readonly h: string;
|
||||
readonly p: number;
|
||||
}
|
||||
|
||||
export interface HashTable<T> {
|
||||
readonly [key: string]: T;
|
||||
}
|
41
app/db/src/app/search/components/passage/passage.html
Normal file
41
app/db/src/app/search/components/passage/passage.html
Normal file
@ -0,0 +1,41 @@
|
||||
<div class="card-title passage-title">
|
||||
<mat-icon aria-hidden="false" aria-label="Bible Passage Icon"
|
||||
>menu_book</mat-icon
|
||||
>
|
||||
<span *ngIf="ref">{{ ref }}</span>
|
||||
<button
|
||||
mat-icon
|
||||
class="card-close-button"
|
||||
aria-label="Remove the passage card from the list"
|
||||
(click)="close($event)"
|
||||
>
|
||||
<mat-icon>cancel</mat-icon>
|
||||
</button>
|
||||
</div>
|
||||
<div class="card-content" *ngIf="cardItem">
|
||||
<div class="passage-text" *ngFor="let ch of cardItem.data.cs">
|
||||
<h2>Chapter {{ ch.ch }}</h2>
|
||||
<ng-template ngFor let-para [ngForOf]="ch.paras">
|
||||
<span [ngClass]="{'as-paragraph': (showParagraphs$ | async)}">
|
||||
<h3
|
||||
class="paragraph-heading"
|
||||
*ngIf="(showParagraphHeadings$ | async) && hasHeader(para.p)"
|
||||
>
|
||||
{{ para.p.h }}
|
||||
</h3>
|
||||
<ng-template ngFor let-vs [ngForOf]="para.vss">
|
||||
<strong class="verse-number" *ngIf="(showVerseNumbers$ | async)"
|
||||
>{{ vs.v }}.</strong
|
||||
>
|
||||
<ng-template ngFor let-w [ngForOf]="vs.w">
|
||||
<ng-template [ngIf]="!isPunct(w.t)"> </ng-template
|
||||
><a *ngIf="w.s != null" (click)="openStrongs(w.s)">{{ w.t }}</a
|
||||
><ng-template [ngIf]="w.s == null"
|
||||
>{{ w.t }}</ng-template
|
||||
> </ng-template
|
||||
><br *ngIf="(showVersesOnNewLine$ | async)" />
|
||||
</ng-template>
|
||||
</span>
|
||||
</ng-template>
|
||||
</div>
|
||||
</div>
|
17
app/db/src/app/search/components/passage/passage.scss
Normal file
17
app/db/src/app/search/components/passage/passage.scss
Normal file
@ -0,0 +1,17 @@
|
||||
.passage-title {
|
||||
background-color: var(--passage-color-primary);
|
||||
}
|
||||
|
||||
.as-paragraph {
|
||||
display: block;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.paragraph-heading {
|
||||
font-family: var(--passage-heading-font-family);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.card-close-button {
|
||||
color: var(--passage-color-accent);
|
||||
}
|
165
app/db/src/app/search/components/passage/passage.ts
Normal file
165
app/db/src/app/search/components/passage/passage.ts
Normal file
@ -0,0 +1,165 @@
|
||||
import { Component, OnInit, ElementRef } from '@angular/core';
|
||||
import { BibleReference } from '../../../common/bible-reference';
|
||||
import { AppService } from '../../../services/app.service';
|
||||
import { CardComponent } from '../../../common/card.component';
|
||||
import { Paragraph } from '../../../models/app-state';
|
||||
|
||||
@Component({
|
||||
selector: 'app-passage',
|
||||
templateUrl: 'passage.html',
|
||||
styleUrls: ['./passage.scss'],
|
||||
preserveWhitespaces: true,
|
||||
})
|
||||
export class PassageComponent extends CardComponent implements OnInit {
|
||||
ref: BibleReference;
|
||||
showParagraphs$ = this.appService.select(
|
||||
(state) => state.displaySettings.showParagraphs
|
||||
);
|
||||
showParagraphHeadings$ = this.appService.select(
|
||||
(state) =>
|
||||
state.displaySettings.showParagraphHeadings &&
|
||||
state.displaySettings.showParagraphs
|
||||
);
|
||||
showVersesOnNewLine$ = this.appService.select(
|
||||
(state) => state.displaySettings.showVersesOnNewLine
|
||||
);
|
||||
showVerseNumbers$ = this.appService.select(
|
||||
(state) => state.displaySettings.showVerseNumbers
|
||||
);
|
||||
|
||||
constructor(
|
||||
protected elementRef: ElementRef,
|
||||
private appService: AppService
|
||||
) {
|
||||
super(elementRef);
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.ref = new BibleReference(this.cardItem.qry);
|
||||
}
|
||||
|
||||
contextMenu() {
|
||||
// cardContextMenu(this.profileService, this._actionSheet, this._pagesSvc, this._alertCtrl, this.cardItem);
|
||||
}
|
||||
|
||||
next() {
|
||||
const lastVerseForEnd = this.ref.Section.end.book.chapters[
|
||||
parseInt(this.ref.Section.end.chapter, 10)
|
||||
].toString();
|
||||
|
||||
if (
|
||||
this.ref.Section.end.verse !== '*' &&
|
||||
this.ref.Section.end.verse !== lastVerseForEnd
|
||||
) {
|
||||
this.ref.Section.end.chapter = this.ref.Section.end.chapter;
|
||||
} else {
|
||||
this.ref.Section.end.chapter = (
|
||||
parseInt(this.ref.Section.end.chapter, 10) + 1
|
||||
).toString();
|
||||
}
|
||||
|
||||
this.ref.Section.start.chapter = this.ref.Section.end.chapter;
|
||||
this.ref.Section.start.verse = '1';
|
||||
this.ref.Section.end.verse = '*';
|
||||
|
||||
this.appService.updatePassage(this.cardItem, this.ref);
|
||||
}
|
||||
|
||||
prev() {
|
||||
if (this.ref.Section.start.verse !== '1') {
|
||||
this.ref.Section.start.chapter = this.ref.Section.start.chapter;
|
||||
} else {
|
||||
this.ref.Section.start.chapter = (
|
||||
parseInt(this.ref.Section.start.chapter, 10) - 1
|
||||
).toString();
|
||||
}
|
||||
|
||||
this.ref.Section.end.chapter = this.ref.Section.start.chapter;
|
||||
this.ref.Section.start.verse = '1';
|
||||
this.ref.Section.end.verse = '*';
|
||||
|
||||
this.appService.updatePassage(this.cardItem, this.ref);
|
||||
}
|
||||
|
||||
expand() {
|
||||
const lastVerseForEnd = this.ref.Section.end.book.chapters[
|
||||
parseInt(this.ref.Section.end.chapter, 10)
|
||||
];
|
||||
|
||||
// if your verse is at the beginning, to go the prev chapter and add 3 verses from that
|
||||
if (parseInt(this.ref.Section.start.verse, 10) < 4) {
|
||||
this.ref.Section.start.chapter = (
|
||||
parseInt(this.ref.Section.start.chapter, 10) - 1
|
||||
).toString();
|
||||
this.ref.Section.start.verse =
|
||||
'*-' + (3 - parseInt(this.ref.Section.start.verse, 10));
|
||||
if (this.ref.Section.start.chapter === '0') {
|
||||
this.ref.Section.start.chapter = '1';
|
||||
this.ref.Section.start.verse = '1';
|
||||
}
|
||||
} else {
|
||||
// or go back 3 verses
|
||||
this.ref.Section.start.verse = (
|
||||
parseInt(this.ref.Section.start.verse, 10) - 3
|
||||
).toString();
|
||||
}
|
||||
|
||||
// if your verse is at the end, go to the next chapter
|
||||
if (
|
||||
this.ref.Section.end.verse === '*' ||
|
||||
parseInt(this.ref.Section.end.verse, 10) + 3 > lastVerseForEnd
|
||||
) {
|
||||
this.ref.Section.end.chapter = (
|
||||
parseInt(this.ref.Section.end.chapter, 10) + 1
|
||||
).toString();
|
||||
if (this.ref.Section.end.verse === '*') {
|
||||
this.ref.Section.end.verse = '3';
|
||||
} else {
|
||||
this.ref.Section.end.verse = (
|
||||
parseInt(this.ref.Section.end.verse, 10) +
|
||||
3 -
|
||||
lastVerseForEnd
|
||||
).toString();
|
||||
}
|
||||
|
||||
if (
|
||||
this.ref.Section.end.chapter ===
|
||||
(this.ref.Section.end.book.last_chapter + 1).toString()
|
||||
) {
|
||||
this.ref.Section.end.chapter = this.ref.Section.end.book.last_chapter.toString();
|
||||
this.ref.Section.end.verse = lastVerseForEnd.toString();
|
||||
}
|
||||
} else {
|
||||
// or add 3 verses
|
||||
this.ref.Section.end.verse = (
|
||||
parseInt(this.ref.Section.end.verse, 10) + 3
|
||||
).toString();
|
||||
}
|
||||
|
||||
if (this.ref.Section.start.verse === '0') {
|
||||
this.ref.Section.start.verse = '1';
|
||||
}
|
||||
|
||||
this.appService.updatePassage(this.cardItem, this.ref);
|
||||
}
|
||||
|
||||
openStrongs(strongs: string) {
|
||||
this.onItemClicked.emit({
|
||||
card: this.cardItem,
|
||||
qry: this.cardItem.dict + strongs,
|
||||
from_search_bar: false,
|
||||
});
|
||||
}
|
||||
|
||||
isPunct(c: string) {
|
||||
return new RegExp('^[.,;:?!]$').test(c);
|
||||
}
|
||||
|
||||
hasHeader(p: Paragraph) {
|
||||
if (p === undefined) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return p.h.length > 0;
|
||||
}
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
<mat-toolbar>
|
||||
<button type="button" mat-icon-button (click)="navService.toggle()">
|
||||
<mat-icon md-48 aria-hidden="false" aria-label="Menu Toggle">menu</mat-icon>
|
||||
</button>
|
||||
<div class="search-bar">
|
||||
<div class="searchbar-search-icon"></div>
|
||||
<input
|
||||
class="search-bar-input"
|
||||
type="text"
|
||||
aria-label="Search"
|
||||
placeholder="Search"
|
||||
type="search"
|
||||
autocomplete="off"
|
||||
matInput
|
||||
[formControl]="searchControl"
|
||||
[matAutocomplete]="auto"
|
||||
(keyup.enter)="search($event.target.value)"
|
||||
/>
|
||||
<mat-autocomplete #auto="matAutocomplete">
|
||||
<mat-option *ngFor="let option of suggestions$ | async" [value]="option">
|
||||
{{option}}
|
||||
</mat-option>
|
||||
</mat-autocomplete>
|
||||
</div>
|
||||
</mat-toolbar>
|
||||
|
||||
<ng-template [ngIf]="(cards$ | async).length > 0" [ngIfElse]="nocards">
|
||||
<div *ngFor="let item of cards$ | async">
|
||||
<mat-card>
|
||||
<app-passage
|
||||
*ngIf="isPassage(item)"
|
||||
[cardItem]="item"
|
||||
(onClose)="removeCard(item)"
|
||||
(onItemClicked)="getItemsNextToCard($event)"
|
||||
></app-passage>
|
||||
</mat-card>
|
||||
</div>
|
||||
</ng-template>
|
||||
<ng-template #nocards>
|
||||
<span> </span>
|
||||
</ng-template>
|
@ -0,0 +1,44 @@
|
||||
.full-width {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
mat-card {
|
||||
max-width: 400px;
|
||||
margin: 1em auto;
|
||||
padding: 0;
|
||||
}
|
||||
mat-toolbar {
|
||||
padding-left: 12px;
|
||||
}
|
||||
|
||||
.search-bar {
|
||||
width: 100%;
|
||||
position: relative;
|
||||
margin-left: 12px;
|
||||
}
|
||||
|
||||
.search-bar-input {
|
||||
width: 100%;
|
||||
border: 0;
|
||||
padding: 6px 55px;
|
||||
border-radius: 0.2rem;
|
||||
height: auto;
|
||||
font-size: 1.2rem;
|
||||
font-family: var(--font-family);
|
||||
line-height: 2.2rem;
|
||||
-webkit-box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14),
|
||||
0 3px 1px -2px rgba(0, 0, 0, 0.2), 0 1px 5px 0 rgba(0, 0, 0, 0.12);
|
||||
box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 3px 1px -2px rgba(0, 0, 0, 0.2),
|
||||
0 1px 5px 0 rgba(0, 0, 0, 0.12);
|
||||
touch-action: manipulation;
|
||||
}
|
||||
.searchbar-search-icon {
|
||||
position: absolute;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 20px;
|
||||
left: 16px;
|
||||
top: 11px;
|
||||
background-image: url("data:image/svg+xml;charset=utf-8,<svg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20512%20512'><path%20fill='%235b5b5b'%20d='M337.509,305.372h-17.501l-6.571-5.486c20.791-25.232,33.922-57.054,33.922-93.257C347.358,127.632,283.896,64,205.135,64C127.452,64,64,127.632,64,206.629s63.452,142.628,142.225,142.628c35.011,0,67.831-13.167,92.991-34.008l6.561,5.487v17.551L415.18,448L448,415.086L337.509,305.372z%20M206.225,305.372c-54.702,0-98.463-43.887-98.463-98.743c0-54.858,43.761-98.742,98.463-98.742c54.7,0,98.462,43.884,98.462,98.742C304.687,261.485,260.925,305.372,206.225,305.372z'/></svg>");
|
||||
width: 21px;
|
||||
height: 21px;
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { SearchPage } from './search.page';
|
||||
|
||||
describe('SearchComponent', () => {
|
||||
let component: SearchPage;
|
||||
let fixture: ComponentFixture<SearchPage>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [SearchPage],
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(SearchPage);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
139
app/db/src/app/search/components/search-page/search.page.ts
Normal file
139
app/db/src/app/search/components/search-page/search.page.ts
Normal file
@ -0,0 +1,139 @@
|
||||
import { Component, OnInit, HostListener } from '@angular/core';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { Observable } from 'rxjs';
|
||||
import { FormControl } from '@angular/forms';
|
||||
import { map, startWith } from 'rxjs/operators';
|
||||
import { AppService } from 'src/app/services/app.service';
|
||||
import { NavService } from 'src/app/services/nav.service';
|
||||
import { OpenData, CardItem } from 'src/app/models/app-state';
|
||||
import { BibleReference } from 'src/app/common/bible-reference';
|
||||
|
||||
@Component({
|
||||
selector: 'app-search-page',
|
||||
templateUrl: './search.page.html',
|
||||
styleUrls: ['./search.page.scss'],
|
||||
})
|
||||
export class SearchPage implements OnInit {
|
||||
cards$ = this.appService.select((state) => state.cards);
|
||||
suggestions$: Observable<string[]>;
|
||||
|
||||
searchControl = new FormControl();
|
||||
|
||||
constructor(
|
||||
private activatedRoute: ActivatedRoute,
|
||||
private appService: AppService,
|
||||
public navService: NavService
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
// if a route was passed in, perform a search.
|
||||
if (this.activatedRoute.snapshot.paramMap.has('term')) {
|
||||
const term = this.activatedRoute.snapshot.paramMap.get('term');
|
||||
this.search(term);
|
||||
}
|
||||
|
||||
this.suggestions$ = this.searchControl.valueChanges.pipe(
|
||||
startWith(''),
|
||||
map((value) => this.getSearchItems(value))
|
||||
);
|
||||
|
||||
this.appService.state$.subscribe((state) => {
|
||||
console.log(state);
|
||||
});
|
||||
}
|
||||
|
||||
//#region Search
|
||||
|
||||
getItemsNextToCard(data: OpenData) {}
|
||||
|
||||
removeCard(card: CardItem) {
|
||||
this.appService.removeCard(card);
|
||||
}
|
||||
|
||||
async search(search: string) {
|
||||
// clear search box.
|
||||
this.searchControl.setValue('');
|
||||
|
||||
try {
|
||||
const terms = search.split(';');
|
||||
for (const term of terms) {
|
||||
const q = term.trim();
|
||||
if (q !== '') {
|
||||
if (q.startsWith('note:')) {
|
||||
// // It's a note lookup
|
||||
// list.push({
|
||||
// qry: q.replace('note:', ''),
|
||||
// dict: '',
|
||||
// type: 'Note',
|
||||
// });
|
||||
} else if (q.search(/[0-9]/i) === -1) {
|
||||
// // its a search term.
|
||||
// list.push({ qry: q, dict: 'na', type: 'Words' });
|
||||
} else if (q.search(/(H|G)[0-9]/i) !== -1) {
|
||||
// // its a strongs lookup
|
||||
// let dict = q.substring(0, 1);
|
||||
// if (dict.search(/h/i) !== -1) {
|
||||
// dict = 'heb';
|
||||
// } else {
|
||||
// dict = 'grk';
|
||||
// }
|
||||
// q = q.substring(1, q.length);
|
||||
// list.push({ qry: q, dict, type: 'Strongs' });
|
||||
} else {
|
||||
// its a verse reference.
|
||||
if (q !== '') {
|
||||
const myref = new BibleReference(q.trim());
|
||||
this.appService.getNewPassage(myref);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
|
||||
isError(t: CardItem) {
|
||||
return t.type === 'Error';
|
||||
}
|
||||
isPassage(t: CardItem) {
|
||||
return t.type === 'Passage';
|
||||
}
|
||||
isNote(t: CardItem) {
|
||||
return t.type === 'Note';
|
||||
}
|
||||
isStrongs(t: CardItem) {
|
||||
return t.type === 'Strongs';
|
||||
}
|
||||
isWords(t: CardItem) {
|
||||
return t.type === 'Words';
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region Typeahead/Autocomplete
|
||||
private getSearchItems(value: string): string[] {
|
||||
const filterValue = value.toLowerCase();
|
||||
|
||||
return ['One', 'Two', 'Three'].filter((option) =>
|
||||
option.toLowerCase().includes(filterValue)
|
||||
);
|
||||
}
|
||||
|
||||
select(selection: any): void {
|
||||
this.search(selection);
|
||||
}
|
||||
|
||||
@HostListener('document:click', ['$event'])
|
||||
private documentClickHandler(event) {
|
||||
// if (this.searchbarElem) {
|
||||
// this.searchbarElem.getInputElement().then((el) => {
|
||||
// if (el.contains(event.target)) {
|
||||
// this.showList = false;
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
}
|
||||
|
||||
//#endregion
|
||||
}
|
16
app/db/src/app/services/app.service.spec.ts
Normal file
16
app/db/src/app/services/app.service.spec.ts
Normal file
@ -0,0 +1,16 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { AppService } from './app.service';
|
||||
|
||||
describe('AppService', () => {
|
||||
let service: AppService;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({});
|
||||
service = TestBed.inject(AppService);
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
});
|
380
app/db/src/app/services/app.service.ts
Normal file
380
app/db/src/app/services/app.service.ts
Normal file
@ -0,0 +1,380 @@
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { Injectable } from '@angular/core';
|
||||
import {
|
||||
AppState,
|
||||
SavedPage,
|
||||
HashTable,
|
||||
Paragraph,
|
||||
BiblePassage,
|
||||
BibleVerse,
|
||||
Data,
|
||||
Error,
|
||||
BibleParagraph,
|
||||
BibleParagraphPassage,
|
||||
CardItem,
|
||||
} from '../models/app-state';
|
||||
import { Section, BibleReference } from '../common/bible-reference';
|
||||
import { PageTitles } from '../constants';
|
||||
import { createStateService } from '../common/state-service';
|
||||
import * as math from 'mathjs';
|
||||
|
||||
const initialState: AppState = {
|
||||
cards: [],
|
||||
savedPages: [],
|
||||
mainPages: [
|
||||
{ title: PageTitles.Search, icon: 'search' },
|
||||
{ title: PageTitles.Settings, icon: 'settings' },
|
||||
{ title: PageTitles.Help, icon: 'help' },
|
||||
],
|
||||
error: null,
|
||||
paragraphs: null,
|
||||
displaySettings: {
|
||||
showStrongsAsModal: false,
|
||||
appendCardToBottom: true,
|
||||
insertCardNextToItem: true,
|
||||
fontSize: 11,
|
||||
fontFamily: 'PT Serif',
|
||||
showVersesOnNewLine: false,
|
||||
showVerseNumbers: false,
|
||||
showParagraphs: true,
|
||||
showParagraphHeadings: true,
|
||||
},
|
||||
};
|
||||
|
||||
type AppAction =
|
||||
| {
|
||||
type: 'UPDATE_PAGES';
|
||||
pages: SavedPage[];
|
||||
}
|
||||
| {
|
||||
type: 'ADD_CARD';
|
||||
card: CardItem;
|
||||
}
|
||||
| {
|
||||
type: 'UPDATE_CARD';
|
||||
newCard: CardItem;
|
||||
oldCard: CardItem;
|
||||
}
|
||||
| {
|
||||
type: 'REMOVE_CARD';
|
||||
card: CardItem;
|
||||
}
|
||||
| {
|
||||
type: 'UPDATE_ERROR';
|
||||
error: Error;
|
||||
}
|
||||
| {
|
||||
type: 'UPDATE_PARAGRAPHS';
|
||||
paragraphs: HashTable<Paragraph>;
|
||||
};
|
||||
|
||||
function reducer(state: AppState, action: AppAction): AppState {
|
||||
// somtimes the state is null. lets not complain if that happens.
|
||||
if (state === undefined) {
|
||||
state = initialState;
|
||||
}
|
||||
|
||||
switch (action.type) {
|
||||
case 'UPDATE_PAGES': {
|
||||
return {
|
||||
...state,
|
||||
savedPages: [...action.pages],
|
||||
};
|
||||
}
|
||||
case 'ADD_CARD': {
|
||||
return {
|
||||
...state,
|
||||
cards: [...state.cards, action.card],
|
||||
};
|
||||
}
|
||||
case 'UPDATE_CARD': {
|
||||
return {
|
||||
...state,
|
||||
cards: [
|
||||
...state.cards.map((c) => {
|
||||
if (c === action.oldCard) {
|
||||
return action.newCard;
|
||||
}
|
||||
return c;
|
||||
}),
|
||||
],
|
||||
};
|
||||
}
|
||||
case 'REMOVE_CARD': {
|
||||
return {
|
||||
...state,
|
||||
cards: [...state.cards.filter((c) => c !== action.card)],
|
||||
};
|
||||
}
|
||||
|
||||
case 'UPDATE_PARAGRAPHS': {
|
||||
return {
|
||||
...state,
|
||||
paragraphs: action.paragraphs,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class AppService extends createStateService(reducer, initialState) {
|
||||
constructor(private http: HttpClient) {
|
||||
super();
|
||||
}
|
||||
async getSavedPages() {
|
||||
this.dispatch({
|
||||
type: 'UPDATE_PAGES',
|
||||
pages: [
|
||||
{
|
||||
queries: [],
|
||||
title: 'My Page',
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
async getParagraphMarkers(): Promise<HashTable<Paragraph>> {
|
||||
const paras = await this.http
|
||||
.get<HashTable<Paragraph>>('assets/data/bibles/paras.json')
|
||||
.toPromise();
|
||||
this.dispatch({
|
||||
type: 'UPDATE_PARAGRAPHS',
|
||||
paragraphs: paras,
|
||||
});
|
||||
return paras;
|
||||
}
|
||||
|
||||
removeCard(card: CardItem) {
|
||||
this.dispatch({
|
||||
type: 'REMOVE_CARD',
|
||||
card,
|
||||
});
|
||||
}
|
||||
|
||||
//#region Bible Passages
|
||||
|
||||
async getNewPassage(ref: BibleReference) {
|
||||
const card = await this.composeBiblePassageCardItem(ref);
|
||||
this.dispatch({
|
||||
type: 'ADD_CARD',
|
||||
card,
|
||||
});
|
||||
}
|
||||
|
||||
async updatePassage(oldCard: CardItem, ref: BibleReference) {
|
||||
const newCard = await this.composeBiblePassageCardItem(ref);
|
||||
this.dispatch({
|
||||
type: 'UPDATE_CARD',
|
||||
oldCard,
|
||||
newCard,
|
||||
});
|
||||
}
|
||||
|
||||
private async composeBiblePassageCardItem(ref: BibleReference) {
|
||||
const result = await this.getPassageFromApi(ref.Section);
|
||||
return {
|
||||
qry: ref.toString(),
|
||||
dict: ref.Section.start.book.book_number > 39 ? 'G' : 'H',
|
||||
type: 'Passage',
|
||||
data: result,
|
||||
};
|
||||
}
|
||||
private async getPassageFromApi(section: Section) {
|
||||
try {
|
||||
const chapters = []; // the verses from the chapter.
|
||||
const result = {
|
||||
cs: [],
|
||||
testament: '',
|
||||
ref: BibleReference.toString(section),
|
||||
};
|
||||
|
||||
if (Number(section.start.chapter) > section.start.book.last_chapter) {
|
||||
this.dispatch({
|
||||
type: 'UPDATE_ERROR',
|
||||
error: {
|
||||
msg: `The requested chapter ${section.start.book.name} is out of range. Please pick a chapter between 1 and ${section.end.book.last_chapter}.`,
|
||||
},
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (Number(section.end.chapter) > section.end.book.last_chapter) {
|
||||
this.dispatch({
|
||||
type: 'UPDATE_ERROR',
|
||||
error: {
|
||||
msg: `The requested chapter ${section.end.book.name} is out of range. Please pick a chapter between 1 and ${section.end.book.last_chapter}.`,
|
||||
},
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
for (
|
||||
let i = Number(section.start.chapter);
|
||||
i <= Number(section.end.chapter);
|
||||
i++
|
||||
) {
|
||||
try {
|
||||
const d = await this.http
|
||||
.get<BiblePassage>(
|
||||
`assets/data/bibles/kjv_strongs/${section.start.book.book_number}-${i}.json`
|
||||
)
|
||||
.toPromise();
|
||||
chapters.push(d);
|
||||
} catch (err) {
|
||||
this.dispatch({
|
||||
type: 'UPDATE_ERROR',
|
||||
error: {
|
||||
msg: `Unable to retrieve bible passage ${result.ref}.`,
|
||||
},
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const passages: BiblePassage[] = [];
|
||||
|
||||
// prep the passages
|
||||
for (let j = 0; j < chapters.length; j++) {
|
||||
const vss: BibleVerse[] = [];
|
||||
let start: number;
|
||||
let end: string | number;
|
||||
|
||||
// figure out the start verse.
|
||||
if (j === 0) {
|
||||
if (section.start.verse.indexOf('*') !== -1) {
|
||||
// you sometimes use this as a shortcut to the last verse
|
||||
// replace the * with the last verse, then eval the expression.
|
||||
section.start.verse = section.start.verse.replace(
|
||||
'*',
|
||||
chapters[j].vss.length.toString()
|
||||
);
|
||||
|
||||
start = math.evaluate(section.start.verse);
|
||||
|
||||
// update the section and the ref.
|
||||
section.start.verse = start.toString();
|
||||
result.ref = BibleReference.toString(section);
|
||||
} else {
|
||||
start = parseInt(section.start.verse, 10);
|
||||
}
|
||||
} else {
|
||||
start = 1;
|
||||
}
|
||||
|
||||
// figure out the end verse
|
||||
if (j + 1 === chapters.length) {
|
||||
end = section.end.verse;
|
||||
} else {
|
||||
end = '*';
|
||||
}
|
||||
|
||||
// get the verses requested.
|
||||
const tvs = chapters[j].vss.length;
|
||||
if (end === '*' || parseInt(end, 10) > tvs) {
|
||||
end = tvs;
|
||||
}
|
||||
|
||||
// we're using c based indexes here, so the index is 1 less than the verse #.
|
||||
for (let i = start; i <= end; i++) {
|
||||
vss.push(chapters[j].vss[i - 1]);
|
||||
}
|
||||
|
||||
passages.push({
|
||||
ch: chapters[j].ch,
|
||||
vss,
|
||||
});
|
||||
}
|
||||
|
||||
// convert into paragraphs.
|
||||
result.cs = await this.convertToParagraphPassages(passages, section);
|
||||
|
||||
if (section.start.book.book_number >= 40) {
|
||||
result.testament = 'new';
|
||||
} else {
|
||||
result.testament = 'old';
|
||||
}
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
this.dispatch({
|
||||
type: 'UPDATE_ERROR',
|
||||
error: {
|
||||
msg: `An unknown error occurred: ${error}.`,
|
||||
},
|
||||
});
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
|
||||
private async convertToParagraphPassages(
|
||||
chapters: BiblePassage[],
|
||||
section: Section
|
||||
) {
|
||||
let paragraphMarkers: HashTable<Paragraph> = null;
|
||||
|
||||
if (this.getState().paragraphs === null) {
|
||||
paragraphMarkers = await this.getParagraphMarkers();
|
||||
} else {
|
||||
paragraphMarkers = this.getState().paragraphs;
|
||||
}
|
||||
|
||||
const passages: BibleParagraphPassage[] = [];
|
||||
for (const ch of chapters) {
|
||||
const passage = {
|
||||
ch: ch.ch,
|
||||
paras: this.convertToParagraphs(ch, section, paragraphMarkers),
|
||||
};
|
||||
|
||||
passages.push(passage);
|
||||
}
|
||||
|
||||
return passages;
|
||||
}
|
||||
|
||||
private convertToParagraphs(
|
||||
ch: BiblePassage,
|
||||
section: Section,
|
||||
paragraphMarkers: HashTable<Paragraph>
|
||||
): BibleParagraph[] {
|
||||
// group the verses into paragraphs.
|
||||
|
||||
// create an initial paragraph to hold verses that might come before a paragraph.
|
||||
let para = { p: { h: '', p: 0 }, vss: [] };
|
||||
const paras = [];
|
||||
const vss: BibleVerse[] = [];
|
||||
|
||||
// for each verse in the chapter, break them into paragraphs.
|
||||
for (const v of ch.vss) {
|
||||
if (this.getRefKey(v, section) in paragraphMarkers) {
|
||||
paras.push(para);
|
||||
para = {
|
||||
p: paragraphMarkers[this.getRefKey(v, section)],
|
||||
vss: [v],
|
||||
};
|
||||
|
||||
if (para.p === undefined) {
|
||||
para.p = { h: '', p: 0 };
|
||||
} // just in case you can't find a paragraph.
|
||||
} else {
|
||||
para.vss.push(v);
|
||||
}
|
||||
}
|
||||
|
||||
// add the final paragraph if it has verses.
|
||||
if (para.vss.length > 0) {
|
||||
paras.push(para);
|
||||
}
|
||||
|
||||
return paras;
|
||||
}
|
||||
|
||||
private getRefKey(vs: BibleVerse, section: Section) {
|
||||
return (
|
||||
section.start.book.book_number + ';' + section.start.chapter + ';' + vs.v
|
||||
);
|
||||
}
|
||||
|
||||
//#endregion
|
||||
}
|
25
app/db/src/app/services/nav.service.ts
Normal file
25
app/db/src/app/services/nav.service.ts
Normal file
@ -0,0 +1,25 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { MatSidenav } from '@angular/material/sidenav';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class NavService {
|
||||
private sidenav: MatSidenav;
|
||||
|
||||
public setSidenav(sidenav: MatSidenav) {
|
||||
this.sidenav = sidenav;
|
||||
}
|
||||
|
||||
public open() {
|
||||
return this.sidenav.open();
|
||||
}
|
||||
|
||||
public close() {
|
||||
return this.sidenav.close();
|
||||
}
|
||||
|
||||
public toggle(): void {
|
||||
this.sidenav.toggle();
|
||||
}
|
||||
}
|
0
app/db/src/assets/.gitkeep
Normal file
0
app/db/src/assets/.gitkeep
Normal file
1
app/db/src/assets/data/bibles/kjv_strongs/1-1.json
Normal file
1
app/db/src/assets/data/bibles/kjv_strongs/1-1.json
Normal file
File diff suppressed because one or more lines are too long
1
app/db/src/assets/data/bibles/kjv_strongs/1-10.json
Normal file
1
app/db/src/assets/data/bibles/kjv_strongs/1-10.json
Normal file
File diff suppressed because one or more lines are too long
1
app/db/src/assets/data/bibles/kjv_strongs/1-11.json
Normal file
1
app/db/src/assets/data/bibles/kjv_strongs/1-11.json
Normal file
File diff suppressed because one or more lines are too long
1
app/db/src/assets/data/bibles/kjv_strongs/1-12.json
Normal file
1
app/db/src/assets/data/bibles/kjv_strongs/1-12.json
Normal file
File diff suppressed because one or more lines are too long
1
app/db/src/assets/data/bibles/kjv_strongs/1-13.json
Normal file
1
app/db/src/assets/data/bibles/kjv_strongs/1-13.json
Normal file
File diff suppressed because one or more lines are too long
1
app/db/src/assets/data/bibles/kjv_strongs/1-14.json
Normal file
1
app/db/src/assets/data/bibles/kjv_strongs/1-14.json
Normal file
File diff suppressed because one or more lines are too long
1
app/db/src/assets/data/bibles/kjv_strongs/1-15.json
Normal file
1
app/db/src/assets/data/bibles/kjv_strongs/1-15.json
Normal file
File diff suppressed because one or more lines are too long
1
app/db/src/assets/data/bibles/kjv_strongs/1-16.json
Normal file
1
app/db/src/assets/data/bibles/kjv_strongs/1-16.json
Normal file
File diff suppressed because one or more lines are too long
1
app/db/src/assets/data/bibles/kjv_strongs/1-17.json
Normal file
1
app/db/src/assets/data/bibles/kjv_strongs/1-17.json
Normal file
File diff suppressed because one or more lines are too long
1
app/db/src/assets/data/bibles/kjv_strongs/1-18.json
Normal file
1
app/db/src/assets/data/bibles/kjv_strongs/1-18.json
Normal file
File diff suppressed because one or more lines are too long
1
app/db/src/assets/data/bibles/kjv_strongs/1-19.json
Normal file
1
app/db/src/assets/data/bibles/kjv_strongs/1-19.json
Normal file
File diff suppressed because one or more lines are too long
1
app/db/src/assets/data/bibles/kjv_strongs/1-2.json
Normal file
1
app/db/src/assets/data/bibles/kjv_strongs/1-2.json
Normal file
File diff suppressed because one or more lines are too long
1
app/db/src/assets/data/bibles/kjv_strongs/1-20.json
Normal file
1
app/db/src/assets/data/bibles/kjv_strongs/1-20.json
Normal file
File diff suppressed because one or more lines are too long
1
app/db/src/assets/data/bibles/kjv_strongs/1-21.json
Normal file
1
app/db/src/assets/data/bibles/kjv_strongs/1-21.json
Normal file
File diff suppressed because one or more lines are too long
1
app/db/src/assets/data/bibles/kjv_strongs/1-22.json
Normal file
1
app/db/src/assets/data/bibles/kjv_strongs/1-22.json
Normal file
File diff suppressed because one or more lines are too long
1
app/db/src/assets/data/bibles/kjv_strongs/1-23.json
Normal file
1
app/db/src/assets/data/bibles/kjv_strongs/1-23.json
Normal file
File diff suppressed because one or more lines are too long
1
app/db/src/assets/data/bibles/kjv_strongs/1-24.json
Normal file
1
app/db/src/assets/data/bibles/kjv_strongs/1-24.json
Normal file
File diff suppressed because one or more lines are too long
1
app/db/src/assets/data/bibles/kjv_strongs/1-25.json
Normal file
1
app/db/src/assets/data/bibles/kjv_strongs/1-25.json
Normal file
File diff suppressed because one or more lines are too long
1
app/db/src/assets/data/bibles/kjv_strongs/1-26.json
Normal file
1
app/db/src/assets/data/bibles/kjv_strongs/1-26.json
Normal file
File diff suppressed because one or more lines are too long
1
app/db/src/assets/data/bibles/kjv_strongs/1-27.json
Normal file
1
app/db/src/assets/data/bibles/kjv_strongs/1-27.json
Normal file
File diff suppressed because one or more lines are too long
1
app/db/src/assets/data/bibles/kjv_strongs/1-28.json
Normal file
1
app/db/src/assets/data/bibles/kjv_strongs/1-28.json
Normal file
File diff suppressed because one or more lines are too long
1
app/db/src/assets/data/bibles/kjv_strongs/1-29.json
Normal file
1
app/db/src/assets/data/bibles/kjv_strongs/1-29.json
Normal file
File diff suppressed because one or more lines are too long
1
app/db/src/assets/data/bibles/kjv_strongs/1-3.json
Normal file
1
app/db/src/assets/data/bibles/kjv_strongs/1-3.json
Normal file
File diff suppressed because one or more lines are too long
1
app/db/src/assets/data/bibles/kjv_strongs/1-30.json
Normal file
1
app/db/src/assets/data/bibles/kjv_strongs/1-30.json
Normal file
File diff suppressed because one or more lines are too long
1
app/db/src/assets/data/bibles/kjv_strongs/1-31.json
Normal file
1
app/db/src/assets/data/bibles/kjv_strongs/1-31.json
Normal file
File diff suppressed because one or more lines are too long
1
app/db/src/assets/data/bibles/kjv_strongs/1-32.json
Normal file
1
app/db/src/assets/data/bibles/kjv_strongs/1-32.json
Normal file
File diff suppressed because one or more lines are too long
1
app/db/src/assets/data/bibles/kjv_strongs/1-33.json
Normal file
1
app/db/src/assets/data/bibles/kjv_strongs/1-33.json
Normal file
File diff suppressed because one or more lines are too long
1
app/db/src/assets/data/bibles/kjv_strongs/1-34.json
Normal file
1
app/db/src/assets/data/bibles/kjv_strongs/1-34.json
Normal file
File diff suppressed because one or more lines are too long
1
app/db/src/assets/data/bibles/kjv_strongs/1-35.json
Normal file
1
app/db/src/assets/data/bibles/kjv_strongs/1-35.json
Normal file
File diff suppressed because one or more lines are too long
1
app/db/src/assets/data/bibles/kjv_strongs/1-36.json
Normal file
1
app/db/src/assets/data/bibles/kjv_strongs/1-36.json
Normal file
File diff suppressed because one or more lines are too long
1
app/db/src/assets/data/bibles/kjv_strongs/1-37.json
Normal file
1
app/db/src/assets/data/bibles/kjv_strongs/1-37.json
Normal file
File diff suppressed because one or more lines are too long
1
app/db/src/assets/data/bibles/kjv_strongs/1-38.json
Normal file
1
app/db/src/assets/data/bibles/kjv_strongs/1-38.json
Normal file
File diff suppressed because one or more lines are too long
1
app/db/src/assets/data/bibles/kjv_strongs/1-39.json
Normal file
1
app/db/src/assets/data/bibles/kjv_strongs/1-39.json
Normal file
File diff suppressed because one or more lines are too long
1
app/db/src/assets/data/bibles/kjv_strongs/1-4.json
Normal file
1
app/db/src/assets/data/bibles/kjv_strongs/1-4.json
Normal file
File diff suppressed because one or more lines are too long
1
app/db/src/assets/data/bibles/kjv_strongs/1-40.json
Normal file
1
app/db/src/assets/data/bibles/kjv_strongs/1-40.json
Normal file
File diff suppressed because one or more lines are too long
1
app/db/src/assets/data/bibles/kjv_strongs/1-41.json
Normal file
1
app/db/src/assets/data/bibles/kjv_strongs/1-41.json
Normal file
File diff suppressed because one or more lines are too long
1
app/db/src/assets/data/bibles/kjv_strongs/1-42.json
Normal file
1
app/db/src/assets/data/bibles/kjv_strongs/1-42.json
Normal file
File diff suppressed because one or more lines are too long
1
app/db/src/assets/data/bibles/kjv_strongs/1-43.json
Normal file
1
app/db/src/assets/data/bibles/kjv_strongs/1-43.json
Normal file
File diff suppressed because one or more lines are too long
1
app/db/src/assets/data/bibles/kjv_strongs/1-44.json
Normal file
1
app/db/src/assets/data/bibles/kjv_strongs/1-44.json
Normal file
File diff suppressed because one or more lines are too long
1
app/db/src/assets/data/bibles/kjv_strongs/1-45.json
Normal file
1
app/db/src/assets/data/bibles/kjv_strongs/1-45.json
Normal file
File diff suppressed because one or more lines are too long
1
app/db/src/assets/data/bibles/kjv_strongs/1-46.json
Normal file
1
app/db/src/assets/data/bibles/kjv_strongs/1-46.json
Normal file
File diff suppressed because one or more lines are too long
1
app/db/src/assets/data/bibles/kjv_strongs/1-47.json
Normal file
1
app/db/src/assets/data/bibles/kjv_strongs/1-47.json
Normal file
File diff suppressed because one or more lines are too long
1
app/db/src/assets/data/bibles/kjv_strongs/1-48.json
Normal file
1
app/db/src/assets/data/bibles/kjv_strongs/1-48.json
Normal file
File diff suppressed because one or more lines are too long
1
app/db/src/assets/data/bibles/kjv_strongs/1-49.json
Normal file
1
app/db/src/assets/data/bibles/kjv_strongs/1-49.json
Normal file
File diff suppressed because one or more lines are too long
1
app/db/src/assets/data/bibles/kjv_strongs/1-5.json
Normal file
1
app/db/src/assets/data/bibles/kjv_strongs/1-5.json
Normal file
File diff suppressed because one or more lines are too long
1
app/db/src/assets/data/bibles/kjv_strongs/1-50.json
Normal file
1
app/db/src/assets/data/bibles/kjv_strongs/1-50.json
Normal file
File diff suppressed because one or more lines are too long
1
app/db/src/assets/data/bibles/kjv_strongs/1-6.json
Normal file
1
app/db/src/assets/data/bibles/kjv_strongs/1-6.json
Normal file
File diff suppressed because one or more lines are too long
1
app/db/src/assets/data/bibles/kjv_strongs/1-7.json
Normal file
1
app/db/src/assets/data/bibles/kjv_strongs/1-7.json
Normal file
File diff suppressed because one or more lines are too long
1
app/db/src/assets/data/bibles/kjv_strongs/1-8.json
Normal file
1
app/db/src/assets/data/bibles/kjv_strongs/1-8.json
Normal file
File diff suppressed because one or more lines are too long
1
app/db/src/assets/data/bibles/kjv_strongs/1-9.json
Normal file
1
app/db/src/assets/data/bibles/kjv_strongs/1-9.json
Normal file
File diff suppressed because one or more lines are too long
1
app/db/src/assets/data/bibles/kjv_strongs/10-1.json
Normal file
1
app/db/src/assets/data/bibles/kjv_strongs/10-1.json
Normal file
File diff suppressed because one or more lines are too long
1
app/db/src/assets/data/bibles/kjv_strongs/10-10.json
Normal file
1
app/db/src/assets/data/bibles/kjv_strongs/10-10.json
Normal file
File diff suppressed because one or more lines are too long
1
app/db/src/assets/data/bibles/kjv_strongs/10-11.json
Normal file
1
app/db/src/assets/data/bibles/kjv_strongs/10-11.json
Normal file
File diff suppressed because one or more lines are too long
1
app/db/src/assets/data/bibles/kjv_strongs/10-12.json
Normal file
1
app/db/src/assets/data/bibles/kjv_strongs/10-12.json
Normal file
File diff suppressed because one or more lines are too long
1
app/db/src/assets/data/bibles/kjv_strongs/10-13.json
Normal file
1
app/db/src/assets/data/bibles/kjv_strongs/10-13.json
Normal file
File diff suppressed because one or more lines are too long
1
app/db/src/assets/data/bibles/kjv_strongs/10-14.json
Normal file
1
app/db/src/assets/data/bibles/kjv_strongs/10-14.json
Normal file
File diff suppressed because one or more lines are too long
1
app/db/src/assets/data/bibles/kjv_strongs/10-15.json
Normal file
1
app/db/src/assets/data/bibles/kjv_strongs/10-15.json
Normal file
File diff suppressed because one or more lines are too long
1
app/db/src/assets/data/bibles/kjv_strongs/10-16.json
Normal file
1
app/db/src/assets/data/bibles/kjv_strongs/10-16.json
Normal file
File diff suppressed because one or more lines are too long
1
app/db/src/assets/data/bibles/kjv_strongs/10-17.json
Normal file
1
app/db/src/assets/data/bibles/kjv_strongs/10-17.json
Normal file
File diff suppressed because one or more lines are too long
1
app/db/src/assets/data/bibles/kjv_strongs/10-18.json
Normal file
1
app/db/src/assets/data/bibles/kjv_strongs/10-18.json
Normal file
File diff suppressed because one or more lines are too long
1
app/db/src/assets/data/bibles/kjv_strongs/10-19.json
Normal file
1
app/db/src/assets/data/bibles/kjv_strongs/10-19.json
Normal file
File diff suppressed because one or more lines are too long
1
app/db/src/assets/data/bibles/kjv_strongs/10-2.json
Normal file
1
app/db/src/assets/data/bibles/kjv_strongs/10-2.json
Normal file
File diff suppressed because one or more lines are too long
1
app/db/src/assets/data/bibles/kjv_strongs/10-20.json
Normal file
1
app/db/src/assets/data/bibles/kjv_strongs/10-20.json
Normal file
File diff suppressed because one or more lines are too long
1
app/db/src/assets/data/bibles/kjv_strongs/10-21.json
Normal file
1
app/db/src/assets/data/bibles/kjv_strongs/10-21.json
Normal file
File diff suppressed because one or more lines are too long
1
app/db/src/assets/data/bibles/kjv_strongs/10-22.json
Normal file
1
app/db/src/assets/data/bibles/kjv_strongs/10-22.json
Normal file
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user