FEATURE: Move Help and Settings pages to modals

* fixed ton of bugs... apparently i didn't push up the work i did on the mac... and i don't have that anymore.
This commit is contained in:
walljm 2019-01-02 15:18:49 -05:00
parent 5e8d96f716
commit e444a5fddf
38 changed files with 774 additions and 826 deletions

File diff suppressed because one or more lines are too long

View File

@ -1,17 +1,18 @@
import { Component, ViewChild } from '@angular/core'; import { Component, ViewChild } from '@angular/core';
import { Platform, MenuController, Nav } from 'ionic-angular'; import { Platform, MenuController, Nav, ModalController } from 'ionic-angular';
import { StatusBar } from '@ionic-native/status-bar'; import { StatusBar } from '@ionic-native/status-bar';
import { SplashScreen } from '@ionic-native/splash-screen'; import { SplashScreen } from '@ionic-native/splash-screen';
import { SearchPage } from '../pages/search/search'; import { SearchPage } from '../pages/search/search';
import { PagesService } from '../services/pages-service'; import { PagesService, Page } from '../services/pages-service';
import { PageTitles } from '../libs/Constants';
@Component({ @Component({
templateUrl: 'app.html', templateUrl: 'app.html',
providers: [PagesService] providers: [PagesService]
}) })
export class MyApp export class MyApp {
{
@ViewChild(Nav) nav: Nav; @ViewChild(Nav) nav: Nav;
rootPage: any = SearchPage; rootPage: any = SearchPage;
@ -19,32 +20,39 @@ export class MyApp
constructor( constructor(
public platform: Platform, public platform: Platform,
public menu: MenuController, public menu: MenuController,
private statusBar: StatusBar, public pagesSvc: PagesService,
private splash: SplashScreen, private _statusBar: StatusBar,
private pagesSvc: PagesService private _splash: SplashScreen,
) private _modalCtrl: ModalController
{ ) {
this.initializeApp(); this.initializeApp();
} }
initializeApp() initializeApp() {
{ this.platform.ready().then(() => {
this.platform.ready().then(() =>
{
// Okay, so the platform is ready and our plugins are available. // Okay, so the platform is ready and our plugins are available.
// Here you can do any higher level native things you might need. // Here you can do any higher level native things you might need.
this.statusBar.styleDefault(); this._statusBar.styleDefault();
this.splash.hide(); this._splash.hide();
}); });
} }
openPage(page) openPage(page: Page) {
if (page.title === PageTitles.Help || page.title === PageTitles.Settings)
{ {
this.menu.close(); // close the menu
// this is a modal.
const modal = this._modalCtrl.create(page.component);
modal.present();
return;
}
// close the menu when clicking a link from the menu // close the menu when clicking a link from the menu
this.menu.close('pages'); this.menu.close('pages');
// because the actions menu is on a Page component, and you swap the SearchPage out, // because the actions menu is on a Page component, and you swap the SearchPage out,
// the menu get registered multiple times. to avoid some pages not opening the menu because // the menu gets registered multiple times. to avoid some pages not opening the menu because
// multiple menus with the same id exist and the first one in the list is returned (which happens // multiple menus with the same id exist and the first one in the list is returned (which happens
// be disabled when another of the same id is added) it won't show. because not enabled. // be disabled when another of the same id is added) it won't show. because not enabled.
// --- // ---

View File

@ -1,7 +1,7 @@
import { ProfileService } from './../services/profile-service'; import { ProfileService } from './../services/profile-service';
import { NgModule, ErrorHandler } from '@angular/core'; import { NgModule, ErrorHandler } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser'; import { BrowserModule } from '@angular/platform-browser';
import { HttpClient, HttpClientModule } from '@angular/common/http'; import { HttpClientModule } from '@angular/common/http';
import { HttpModule } from '@angular/http'; import { HttpModule } from '@angular/http';
import { IonicApp, IonicModule, IonicErrorHandler } from 'ionic-angular'; import { IonicApp, IonicModule, IonicErrorHandler } from 'ionic-angular';
@ -13,17 +13,17 @@ import { SplashScreen } from '@ionic-native/splash-screen';
import { MyApp } from './app.component'; import { MyApp } from './app.component';
import { SearchPage } from '../pages/search/search'; import { SearchPage } from '../pages/search/search';
import { SettingsPage } from '../pages/settings/settings';
import { HelpPage } from '../pages/help/help';
import { ComponentLoader } from '../components/component-loader/component-loader';
import { Passage } from '../components/passage/passage'; import { Passage } from '../components/passage/passage';
import { Strongs } from '../components/strongs/strongs'; import { Strongs } from '../components/strongs/strongs';
import { Words } from '../components/words/words'; import { Words } from '../components/words/words';
import { Error } from '../components/error/error'; import { Error } from '../components/error/error';
import { Settings } from '../components/settings/settings';
import { StrongsModal } from '../components/strongs-modal/strongs-modal'; import { StrongsModal } from '../components/strongs-modal/strongs-modal';
import { ErrorMessage } from '../components/error-message/error-message'; import { ErrorMessage } from '../components/error-message/error-message';
import { VersePickerModal } from '../components/verse-picker/verse-picker'; import { VersePickerModal } from '../components/verse-picker/verse-picker';
import { AboutModal } from '../components/about-modal/about-modal';
import { SettingsModal } from '../components/settings-modal/settings-modal';
import { AngularFireModule } from 'angularfire2'; import { AngularFireModule } from 'angularfire2';
import { AngularFireAuthModule } from 'angularfire2/auth'; import { AngularFireAuthModule } from 'angularfire2/auth';
@ -46,9 +46,9 @@ export const firebaseConfig = {
declarations: [ declarations: [
MyApp, MyApp,
SearchPage, SearchPage,
SettingsPage, AboutModal,
HelpPage, SettingsModal,
ComponentLoader, Settings,
Passage, Passage,
Strongs, Strongs,
StrongsModal, StrongsModal,
@ -72,8 +72,9 @@ export const firebaseConfig = {
entryComponents: [ entryComponents: [
MyApp, MyApp,
SearchPage, SearchPage,
SettingsPage, AboutModal,
HelpPage, SettingsModal,
Settings,
Passage, Passage,
Strongs, Strongs,
StrongsModal, StrongsModal,

View File

@ -0,0 +1,122 @@
<ion-header>
<ion-toolbar>
<ion-title>
<ion-icon name="help-circle" item-left></ion-icon> Dynamic Bible Help
</ion-title>
<ion-buttons start>
<button ion-button (click)="dismiss()" large>
<ion-icon name="md-close"></ion-icon>
</button>
</ion-buttons>
</ion-toolbar>
</ion-header>
<ion-content padding>
<h2>How to search for a verse</h2>
<p>
To bring up a passage, just type in a reference, or use the verse picker in the top right corner. Dynamic Bible will recognize most abbreviations of books, and can handle ranges within a book. Here are a few examples to get you started:
</p>
<h3>Examples of Search Items:</h3>
<ul>
<li><b>John 1</b> (displays whole chapter)</li>
<li><b>John 3:16</b> (displays single verse)</li>
<li><b>Jn 3:16</b> (handles abbreviation of John)</li>
<li><b>Jn 3:16-17</b> (displays verses starting with chapter 1 verse 3 through chapter 1 vs 5)</li>
<li><b>John 3:16-4:4</b></li>
<li><b>Jn 3-4</b> (displays both chapters)</li>
<li><b>John 3 - John 4</b> (displays both chapters)</li>
<li><b>Jn 3:1-*</b> (this will get all the verses in the chapter. the * char can be used in verse ranges, but not chapter ranges.)</li>
<li><b>H1234</b> (displays the strongs definition for the Hebrew # 1234)</li>
<li><b>G1234</b> (displays the strongs definition for the Greek # 1234)</li>
<li><b>Jesus</b> (searches for "Jesus". all search terms assume boolean AND, i.e. "Jesus Christ" assumes "Jesus AND Christ". A word without a number is interpreted as search term).</li>
</ul>
We currently don't support ranges that cross book boundaries. If you search for "John 12 - Romans 3" you will get an error. the second book name is ignored.
<h3>Further Notes:</h3>
<ul>
<li>Multiple lookups can be made using a semicolon as a seperator, i.e. "Ruth 1; g1234; spirit").</li>
<li>All greek/hebrew cross references translations are taken from the 1933 Websters version and are sometimes not the same as the KJV translation.</li>
<li>All source materials were taken from the <a href="http://sourceforge.net/projects/zefania-sharp/">Zefania XML Bible Project</a> on SourceForge.</li>
<li>Any Errors and Omission you find would be appreciated. Please contact me via <a href='http://www.jasonwall.org/'>www.jasonwall.org</a>.</li>
</ul>
<h3>Visit Us Online</h3>
<p>
<a href="http://www.dynamicbible.com">www.dynamicbible.com</a> hosts the online version of the web app. We are currently available on the
<a href="https://bitbucket.org/walljm/dynamicbible/downloads/DynamicBible-3.1.0.zip">Windows Platform</a> via the <a href="http://electron.atom.io/">Electron app from atom.io</a>
and on the <a href="https://play.google.com/store/apps/details?id=walljm.dynamicbible">Google Android Play store</a>.
We are working on an IOS distribution and will be releasing that soon.
</p>
<h3>Book Names and Accepted Abbreviations</h3>
<ul>
<li><b>Genesis</b>: gen, ge, gn</li>
<li><b>Exodus</b>: ex, exo, exod, exd</li>
<li><b>Leviticus</b>: lev, le, levi, lv</li>
<li><b>Numbers</b>: num, nu, numb, number</li>
<li><b>Deuteronomy</b>: deut, de, dt, deu</li>
<li><b>Joshua</b>: josh, jos</li>
<li><b>Judges</b>: jud, jdg, judg</li>
<li><b>Ruth</b>: ru</li>
<li><b>1 Samuel</b>: 1, i, 1st, first samuel, sa, sam, sml</li>
<li><b>2 Samuel</b>: 2, ii, 2nd, second, sec samuel, sa, sam, sml</li>
<li><b>1 Kings</b>: 1, i, 1st, first kings, king, kgs, kn, k, ki</li>
<li><b>2 Kings</b>: 2, ii, 2nd, second, sec kings, king, kgs, kn, k, ki</li>
<li><b>1 Chronicles</b>: 1, i, 1st, first chronicles, chron, ch, chr</li>
<li><b>2 Chronicles</b>: 2, ii, 2nd, second, sec chronicles, chron, ch, chr</li>
<li><b>Ezra</b>: ezr</li>
<li><b>Nehemiah</b>: neh, ne, nehamiah</li>
<li><b>Esther</b>: est, es, esth</li>
<li><b>Job</b>: jo, jb</li>
<li><b>Psalms</b>: ps, psa, psalm, psm</li>
<li><b>Proverbs</b>: prov, pr, pro, proverb, prv, prvbs</li>
<li><b>Ecclesiastes</b>: eccl, ecc, eccles, ec, ecl, ecclesiaste</li>
<li><b>Song of Solomon</b>: , song of songs, sos, ss, son, so, song, songs</li>
<li><b>Isaiah</b>: is, isah, isai, ia</li>
<li><b>Jerimiah</b>: jeremiah, jer, je, jere</li>
<li><b>Lamentations</b>: lam, la, lamentation</li>
<li><b>Ezekiel</b>: eze, ezk, ezek</li>
<li><b>Daniel</b>: dan, dn, dl, da</li>
<li><b>Hosea</b>: hos, ho</li>
<li><b>Joel</b>: joe, jl</li>
<li><b>Amos</b>: am, amo</li>
<li><b>Obadiah</b>: oba, ob, obad</li>
<li><b>Jonah</b>: jnh, jon</li>
<li><b>Micah</b>: mic, mi</li>
<li><b>Nahum</b>: nah, na</li>
<li><b>Habakkuk</b>: hab, ha, habakuk</li>
<li><b>Zephaniah</b>: zeph, zep</li>
<li><b>Haggia</b>: hag, hg, haggai</li>
<li><b>Zechariah</b>: zech, zch, zec</li>
<li><b>Malachi</b>: mal</li>
<li><b>Matthew</b>: mt, matt, mat</li>
<li><b>Mark</b>: mrk, mk, mr</li>
<li><b>luke</b>: lu, lke, luk, lk</li>
<li><b>John</b>: jn, jhn</li>
<li><b>Acts</b>: ac, act</li>
<li><b>Romans</b>: rom, ro, rm, roman</li>
<li><b>1 Corinthians</b>: 1, i, 1st, first corinthian, cor, corinthians, corinth, corin, corth, corint</li>
<li><b>2 Corinthians</b>: 2, ii, 2nd, second, sec corinthian, cor, corinthians, corinth, corin, corth, corint</li>
<li><b>Galatians</b>: galatian, galations, gal, ga, gala, galation, galat</li>
<li><b>Ephesians</b>: eph, ep, ephes, ephe, ephs</li>
<li><b>Philippians</b>: phi, phil, ph, philip</li>
<li><b>Colossians</b>: col, co, colossian, colos, coloss</li>
<li><b>1 Thessalonians</b>: 1, i, 1st, first thessalonians, the, thessa, thessalonian, thes, thess, th</li>
<li><b>2 Thessalonians</b>: 2, ii, 2nd, second, sec thessalonians, the, thessa, thessalonian, thes, thess, th</li>
<li><b>1 Timothy</b>: 1, i, 1st, first timothy, tim, ti, timoth, tm</li>
<li><b>2 Timothy</b>: 2, ii, 2nd, second, sec timothy, tim, timoth, tm</li>
<li><b>Titus</b>: tit</li>
<li><b>Philemon</b>: phlmn, phl, phm, phile, philem</li>
<li><b>Hebrews</b>: heb, he, hebrew</li>
<li><b>James</b>: jam, ja, jas, jms, jame, jm</li>
<li><b>1 Peter</b>: 1, i, 1st, first peter, pe, pet, pete, pt, p</li>
<li><b>2 Peter</b>: 2, ii, 2nd, second, sec peter, pe, pet, pete, pt, p</li>
<li><b>1 John</b>: 1, i, 1st, first john, jn, jo</li>
<li><b>2 John</b>: 2, ii, 2nd, second, sec john, jn, jo</li>
<li><b>3 John</b>: 3, iii, 3rd, third john, jn, jo</li>
<li><b>Jude</b>: ju</li>
<li><b>Revelation</b>: rev, re, revelations, rv</li>
</ul>
</ion-content>

View File

@ -0,0 +1,9 @@
about-modal {
ion-icon {
float: left;
}
ion-title {
text-align: center;
}
}

View File

@ -0,0 +1,14 @@
import { Component } from '@angular/core';
import { ViewController } from 'ionic-angular';
@Component({
selector: 'about-modal',
templateUrl: 'about-modal.html'
})
export class AboutModal {
constructor(private _viewCtrl: ViewController) {}
dismiss() {
this._viewCtrl.dismiss();
}
}

View File

@ -1,52 +0,0 @@
// our root app component
import {Component, Compiler, ViewContainerRef, ViewChild, Input, ComponentRef, ComponentFactoryResolver, ChangeDetectorRef} from '@angular/core'
// Helper component to add dynamic components
@Component({
selector: 'component-loader',
template: `<div #target></div>`
})
export class ComponentLoader {
@ViewChild('target', { read: ViewContainerRef }) target;
@Input() type;
@Input() data;
cmpRef: ComponentRef<any>;
private isViewInitialized = false;
constructor(private componentFactoryResolver: ComponentFactoryResolver, private compiler: Compiler,
private cdRef: ChangeDetectorRef) { }
updateComponent() {
if (!this.isViewInitialized) {
return;
}
if (this.cmpRef) {
this.cmpRef.destroy();
}
const factory = this.componentFactoryResolver.resolveComponentFactory(this.type);
this.cmpRef = this.target.createComponent(factory);
// to access the created instance use
// this.compRef.instance.someProperty = 'someValue';
// this.compRef.instance.someOutput.subscribe(val => doSomething());
this.cmpRef.instance.item = this.data;
this.cdRef.detectChanges();
}
ngOnChanges() {
this.updateComponent();
}
ngAfterViewInit() {
this.isViewInitialized = true;
this.updateComponent();
}
ngOnDestroy() {
if (this.cmpRef) {
this.cmpRef.destroy();
}
}
}

View File

@ -8,7 +8,9 @@
<br> <br>
<p>{{cardItem.qry}}</p> <p>{{cardItem.qry}}</p>
</ion-card-content> </ion-card-content>
<button ion-button icon-start clear small (click)="close()">
<ion-icon name="close-circle"></ion-icon> <div style="float: right">
<div>Close</div> <button ion-button icon-center clear (click)="contextMenu()">
<ion-icon name="more"></ion-icon>
</button> </button>
</div>

View File

@ -1,5 +1,9 @@
import { EventEmitter, Component, Input, Output, ElementRef } from '@angular/core'; import { EventEmitter, Component, Input, Output, ElementRef } from '@angular/core';
import { CardItem } from '../../pages/search/search'; import { CardItem } from '../../pages/search/search';
import { ProfileService } from '../../services/profile-service';
import { ActionSheetController, AlertController } from 'ionic-angular';
import { PagesService } from '../../services/pages-service';
import { cardContextMenu } from '../../libs/Common';
@Component({ @Component({
selector: 'error', selector: 'error',
@ -13,17 +17,25 @@ export class Error
@Input() @Input()
cardItem: CardItem; cardItem: CardItem;
constructor(private elementRef: ElementRef) constructor(
private _elementRef: ElementRef,
private _profileService: ProfileService,
private _actionSheet: ActionSheetController,
private _pagesSvc: PagesService,
private _alertCtrl: AlertController)
{ {
} }
contextMenu() {
cardContextMenu(this._profileService, this._actionSheet, this._pagesSvc, this._alertCtrl, this.cardItem);
}
close(ev) { close(ev) {
let translate = 'translate3d(110%, 0, 0)'; let translate = 'translate3d(110%, 0, 0)';
if (ev != null && ev.direction === 2) { if (ev != null && ev.direction === 2) {
translate = 'translate3d(-110%, 0, 0)'; translate = 'translate3d(-110%, 0, 0)';
} }
let d = 250; let d = 250;
this.elementRef.nativeElement.parentElement.animate({ this._elementRef.nativeElement.parentElement.animate({
transform: ['none', translate] transform: ['none', translate]
}, { }, {
fill: 'forwards', fill: 'forwards',

View File

@ -6,42 +6,37 @@
</ion-item> </ion-item>
<ion-card-content *ngIf="data !== undefined && data.status === 0"> <ion-card-content *ngIf="data !== undefined && data.status === 0">
<br> <br>
<ng-template [ngIf]="this.profileService.profile().show_paragraphs"><div class="passage-text" *ngFor="let ch of withParas"> <ng-template [ngIf]="profileService.profile().show_paragraphs"><div class="passage-text" *ngFor="let ch of withParas">
<h2 *ngIf="data.cs.length > 1"> <h2 *ngIf="data.cs.length > 1">
<b>Chapter {{ch.ch}}</b> <b>Chapter {{ch.ch}}</b>
</h2> </h2>
<div *ngFor="let para of ch.paras"> <div *ngFor="let para of ch.paras">
<h3 *ngIf="this.profileService.profile().show_paragraph_headings && hasHeader(para.p)">{{para.p.h}}</h3> <h3 *ngIf="profileService.profile().show_paragraph_headings && hasHeader(para.p)">{{para.p.h}}</h3>
<p> <p>
<ng-template ngFor let-vs [ngForOf]="para.vss"> <ng-template ngFor let-vs [ngForOf]="para.vss">
<b *ngIf="this.profileService.profile().show_verse_numbers">{{vs.v}}.</b> <ng-template ngFor let-w [ngForOf]="vs.w"> <b *ngIf="profileService.profile().show_verse_numbers">{{vs.v}}.</b> <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)" (press)="openMenu(w.s)">{{w.t}}</a> <ng-template [ngIf]="!isPunct(w.t)"> </ng-template><a *ngIf="w.s != null" (click)="openStrongs(w.s)" (press)="openMenu(w.s)">{{w.t}}</a>
<ng-template [ngIf]="w.s == null">{{w.t}}</ng-template> <ng-template [ngIf]="w.s == null">{{w.t}}</ng-template>
</ng-template><br *ngIf="this.profileService.profile().verses_on_new_line"> </ng-template><br *ngIf="profileService.profile().verses_on_new_line">
</ng-template> </ng-template>
</p> </p>
</div> </div>
</div></ng-template> </div></ng-template>
<ng-template [ngIf]="!this.profileService.profile().show_paragraphs"><div class="passage-text" *ngFor="let ch of data.cs"> <ng-template [ngIf]="!profileService.profile().show_paragraphs"><div class="passage-text" *ngFor="let ch of data.cs">
<h2 *ngIf="data.cs.length > 1"> <h2 *ngIf="data.cs.length > 1">
<b>Chapter {{ch.ch}}</b> <b>Chapter {{ch.ch}}</b>
</h2> </h2>
<ng-template ngFor let-vs [ngForOf]="ch.vss"> <ng-template ngFor let-vs [ngForOf]="ch.vss">
<b *ngIf="this.profileService.profile().show_verse_numbers">{{vs.v}}.</b> <ng-template ngFor let-w [ngForOf]="vs.w"> <b *ngIf="profileService.profile().show_verse_numbers">{{vs.v}}.</b> <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)" (press)="openMenu(w.s)">{{w.t}}</a> <ng-template [ngIf]="!isPunct(w.t)"> </ng-template><a *ngIf="w.s != null" (click)="openStrongs(w.s)" (press)="openMenu(w.s)">{{w.t}}</a>
<ng-template [ngIf]="w.s == null">{{w.t}}</ng-template> <ng-template [ngIf]="w.s == null">{{w.t}}</ng-template>
</ng-template><br *ngIf="this.profileService.profile().verses_on_new_line"> </ng-template><br *ngIf="profileService.profile().verses_on_new_line">
</ng-template> </ng-template>
</div></ng-template> </div></ng-template>
</ion-card-content> </ion-card-content>
<ion-card-content *ngIf="data !== undefined && data.status === -1"> <ion-card-content *ngIf="data !== undefined && data.status === -1">
<error-message [msg]="data.msg"></error-message> <error-message [msg]="data.msg"></error-message>
</ion-card-content> </ion-card-content>
<div style="float: left">
<button ion-button icon-start clear (click)="close()">
<ion-icon name="close-circle"></ion-icon>
</button>
</div>
<div style="float: right"> <div style="float: right">
<button ion-button icon-center clear (click)="prev()" *ngIf="ref !== undefined && ref.Section.start.chapter !== '1'"> <button ion-button icon-center clear (click)="prev()" *ngIf="ref !== undefined && ref.Section.start.chapter !== '1'">
<ion-icon name="rewind"></ion-icon> <ion-icon name="rewind"></ion-icon>
@ -52,7 +47,6 @@
<button ion-button icon-center clear (click)="next()" *ngIf="ref !== undefined && ref.Section.end.chapter !== ref.Section.end.book.last_chapter.toString()"> <button ion-button icon-center clear (click)="next()" *ngIf="ref !== undefined && ref.Section.end.chapter !== ref.Section.end.book.last_chapter.toString()">
<ion-icon name="fastforward"></ion-icon> <ion-icon name="fastforward"></ion-icon>
</button> </button>
<button ion-button icon-center clear (click)="contextMenu()"> <button ion-button icon-center clear (click)="contextMenu()">
<ion-icon name="more"></ion-icon> <ion-icon name="more"></ion-icon>
</button> </button>

View File

@ -5,6 +5,7 @@ passage {
} }
font-family: var(--card-font); font-family: var(--card-font);
} }
passage .button { passage .button {
@ -17,10 +18,12 @@ passage .button {
passage .passage-text { passage .passage-text {
padding-bottom: 12px; padding-bottom: 12px;
h3 { h3 {
font-size: 1.6rem; font-size: 1.6rem;
font-weight: bold; font-weight: bold;
} }
max-width: 600px; max-width: 600px;
margin: auto; margin: auto;
} }
@ -46,4 +49,3 @@ passage .passage-text {
// column-rule-width: 1px; // column-rule-width: 1px;
// } // }
// } // }

View File

@ -1,11 +1,12 @@
import { Component, EventEmitter, Output, Input, OnInit, ElementRef } from '@angular/core'; import { Component, EventEmitter, Output, Input, OnInit, ElementRef } from '@angular/core';
import { OpenData, CardItem } from '../../pages/search/search'; import { OpenData, CardItem } from '../../pages/search/search';
import { BiblePassageResult, BibleService, BiblePassage, BibleVerse, HashTable, Paragraph } from '../../services/bible-service'; import { BiblePassageResult, BibleService, BiblePassage, BibleVerse, Paragraph } from '../../services/bible-service';
import { Reference } from '../../libs/Reference'; import { Reference } from '../../libs/Reference';
import { ProfileService } from '../../services/profile-service'; import { ProfileService } from '../../services/profile-service';
import { ActionSheetController, GestureController, GESTURE_ITEM_SWIPE, AlertController } from 'ionic-angular'; import { ActionSheetController, AlertController } from 'ionic-angular';
import { PagesService } from '../../services/pages-service'; import { PagesService } from '../../services/pages-service';
import { GESTURE_PRIORITY_SLIDING_ITEM } from 'ionic-angular/umd/gestures/gesture-controller'; import { cardContextMenu } from '../../libs/Common';
@Component({ @Component({
selector: 'passage', selector: 'passage',
@ -24,22 +25,21 @@ export class Passage implements OnInit {
data: BiblePassageResult; data: BiblePassageResult;
withParas: BibleParaPassage[]; withParas: BibleParaPassage[];
ref: Reference; ref: Reference;
private gesture;
constructor( constructor(
private bibleService: BibleService, public profileService: ProfileService,
private elementRef: ElementRef, private _elementRef: ElementRef,
private profileService: ProfileService, private _bibleService: BibleService,
private actionSheet: ActionSheetController, private _actionSheet: ActionSheetController,
private pagesSvc: PagesService, private _pagesSvc: PagesService,
private alertCtrl: AlertController private _alertCtrl: AlertController
) { ) {
} }
ngOnInit(): void { ngOnInit(): void {
this.ref = new Reference(this.cardItem.qry); this.ref = new Reference(this.cardItem.qry);
this.bibleService.getResultAsPromise(this.ref.Section).then(data => { this._bibleService.getResultAsPromise(this.ref.Section).then(data => {
this.setData(data); this.setData(data);
}); });
@ -52,88 +52,7 @@ export class Passage implements OnInit {
} }
contextMenu() { contextMenu() {
const actions = this.actionSheet.create({ cardContextMenu(this.profileService, this._actionSheet, this._pagesSvc, this._alertCtrl, this.cardItem);
title: 'Passage Actions',
buttons: [
{
text: 'Add to a Saved Page',
icon: 'add-circle',
handler: () => {
let btns = this.pagesSvc.getSavedPages().map(p => {
return {
text: p.title,
icon: 'bookmark',
handler: () => {
const page = this.profileService.profile().saved_pages.find(
i => i.title === p.title
);
page.queries.push(this.cardItem);
this.profileService.save();
}
}
});
btns.push({
text: 'Add to New Page',
icon: 'create',
handler: () => {
const alert = this.alertCtrl.create({
title: 'Save Passage as Page',
inputs: [
{
name: 'title',
placeholder: 'Page Title'
}
],
buttons: [
{
text: 'Cancel',
role: 'cancel',
handler: (): void => {
console.log('Cancel clicked');
}
},
{
text: 'Save',
handler: data => {
const p = { queries: this.profileService.profile().items.slice(), title: data.title };
this.profileService.profile().saved_pages.push(p);
this.profileService.save();
this.pagesSvc.addPage(p);
}
}
]
});
alert.present();
}
});
this.actionSheet.create({
title: 'Saved Pages',
buttons: btns
}).present();
}
}
]
});
if (this.profileService.isOnSearchPage()) {
actions.addButton({
text: 'Remove from ' + this.profileService.title,
icon: 'remove-circle',
handler: () => {
const page = this.profileService.profile().saved_pages.find(
i =>
i.title === this.profileService.title
);
const idx = this.profileService.profile().items.indexOf(this.cardItem);
this.profileService.profile().items.splice(idx, 1);
page.queries = this.profileService.profile().items.slice();
this.profileService.save();
this.pagesSvc.initializePages(this.profileService.profile().saved_pages);
}
});
}
actions.present();
} }
close(ev) { close(ev) {
@ -142,7 +61,7 @@ export class Passage implements OnInit {
translate = 'translate3d(-110%, 0, 0)'; translate = 'translate3d(-110%, 0, 0)';
} }
let d = 250; let d = 250;
this.elementRef.nativeElement.parentElement.animate({ this._elementRef.nativeElement.parentElement.animate({
transform: ['none', translate] transform: ['none', translate]
}, { }, {
fill: 'forwards', fill: 'forwards',
@ -167,7 +86,7 @@ export class Passage implements OnInit {
this.ref.Section.start.verse = '1'; this.ref.Section.start.verse = '1';
this.ref.Section.end.verse = '*'; this.ref.Section.end.verse = '*';
this.bibleService.getResultAsPromise(this.ref.Section).then(data => { this._bibleService.getResultAsPromise(this.ref.Section).then(data => {
this.setData(data); this.setData(data);
this.cardItem.qry = data.ref; this.cardItem.qry = data.ref;
this.ref = new Reference(data.ref); this.ref = new Reference(data.ref);
@ -184,7 +103,7 @@ export class Passage implements OnInit {
this.ref.Section.start.verse = '1'; this.ref.Section.start.verse = '1';
this.ref.Section.end.verse = '*'; this.ref.Section.end.verse = '*';
this.bibleService.getResultAsPromise(this.ref.Section).then(data => { this._bibleService.getResultAsPromise(this.ref.Section).then(data => {
this.setData(data); this.setData(data);
this.cardItem.qry = data.ref; this.cardItem.qry = data.ref;
this.ref = new Reference(data.ref); this.ref = new Reference(data.ref);
@ -225,7 +144,7 @@ export class Passage implements OnInit {
if (this.ref.Section.start.verse === '0') if (this.ref.Section.start.verse === '0')
this.ref.Section.start.verse = '1'; this.ref.Section.start.verse = '1';
this.bibleService.getResultAsPromise(this.ref.Section).then(data => { this._bibleService.getResultAsPromise(this.ref.Section).then(data => {
this.setData(data); this.setData(data);
this.cardItem.qry = data.ref; this.cardItem.qry = data.ref;
this.ref = new Reference(data.ref); this.ref = new Reference(data.ref);

View File

@ -0,0 +1,28 @@
<ion-header>
<ion-toolbar>
<ion-title>
<ion-icon name='settings' item-left></ion-icon> Dynamic Bible Settings
</ion-title>
<ion-buttons start>
<button ion-button (click)='dismiss()' large>
<ion-icon name='md-close'></ion-icon>
</button>
</ion-buttons>
</ion-toolbar>
</ion-header>
<ion-content padding>
<ng-template [ngIf]='_profileService.profile()'>
<settings></settings>
<h4>Manage Pages</h4>
<ion-list>
<ion-item *ngFor='let p of _profileService.profile().saved_pages'>
{{p.title}}
<button ion-button item-end outline icon-end (click)='removePage(p)'>
Delete Page <ion-icon name='trash'></ion-icon>
</button>
</ion-item>
</ion-list>
</ng-template>
</ion-content>

View File

@ -0,0 +1,17 @@
settings-modal {
.list-header {
font-size: 1.3em;
}
.item-md .item-button {
height: 40px;
}
ion-icon {
float: left;
}
ion-title {
text-align: center;
}
}

View File

@ -0,0 +1,42 @@
import { Component } from '@angular/core';
import { ViewController, AlertController } from 'ionic-angular';
import { ProfileService, SavedPage } from '../../services/profile-service';
@Component({
selector: 'settings-modal',
templateUrl: 'settings-modal.html'
})
export class SettingsModal {
constructor(
private _alertCtrl: AlertController,
private _viewCtrl: ViewController,
private _profileService: ProfileService
) {}
dismiss() {
this._viewCtrl.dismiss();
}
removePage(page: SavedPage) {
let alert = this._alertCtrl.create({
title: 'Confirm Delete',
message: 'Do you want to delete the ' + page.title + ' page?',
buttons: [
{
text: 'Cancel',
role: 'cancel',
handler: () => {
console.log('Cancel clicked');
}
},
{
text: 'Ok',
handler: () => {
this._profileService.removePage(page);
}
}
]
});
alert.present();
}
}

View File

@ -0,0 +1,89 @@
<ion-list>
<ion-list-header>
<ion-icon name='search' item-left></ion-icon>Search Settings
</ion-list-header>
<ion-item>
<ion-label>Show Strongs as Modal</ion-label>
<ion-toggle color='dark' [(ngModel)]='profileService.profile().strongs_modal' (ionChange)='profileService.localSave()'></ion-toggle>
</ion-item>
<ion-item>
<ion-label>Clear Search after Query</ion-label>
<ion-toggle color='dark' [(ngModel)]='profileService.profile().clear_search_after_query' (ionChange)='profileService.localSave()'></ion-toggle>
</ion-item>
<ion-item>
<ion-label>Append Results Below</ion-label>
<ion-toggle color='dark' [(ngModel)]='profileService.profile().append_to_bottom' (ionChange)='profileService.localSave()'></ion-toggle>
</ion-item>
<ion-item>
<ion-label>Insert Result Next to Item</ion-label>
<ion-toggle color='dark' [(ngModel)]='profileService.profile().insert_next_to_item' (ionChange)='profileService.localSave()'></ion-toggle>
</ion-item>
</ion-list>
<ion-list>
<ion-list-header>
<ion-icon name='browsers' item-left></ion-icon>Display Settings
</ion-list-header>
<ion-item>
<ion-label>Each Verse on New Line</ion-label>
<ion-toggle color='dark' [(ngModel)]='profileService.profile().verses_on_new_line' (ionChange)='profileService.localSave()'></ion-toggle>
</ion-item>
<ion-item>
<ion-label>Show Verse #'s</ion-label>
<ion-toggle color='dark' [(ngModel)]='profileService.profile().show_verse_numbers' (ionChange)='profileService.localSave()'></ion-toggle>
</ion-item>
<ion-item>
<ion-label>Show Paragraphs</ion-label>
<ion-toggle color='dark' [(ngModel)]='profileService.profile().show_paragraphs' (ionChange)='profileService.localSave()'></ion-toggle>
</ion-item>
<ion-item>
<ion-label>Show Paragraph Headings</ion-label>
<ion-toggle color='dark' [(ngModel)]='profileService.profile().show_paragraph_headings' (ionChange)='profileService.localSave()'></ion-toggle>
</ion-item>
</ion-list>
<ion-list>
<ion-list-header>
<ion-icon name='sync' item-left></ion-icon>Profile Settings
</ion-list-header>
<ion-item>
<ion-label>Sync Search Items</ion-label>
<ion-toggle color='dark' [(ngModel)]='profileService.profile().sync_search_items' (ionChange)='profileService.localSave()'></ion-toggle>
</ion-item>
</ion-list>
<ion-list>
<ion-list-header>
<ion-icon name='text' item-left></ion-icon>Adjust Text
</ion-list-header>
<ion-item>
<ion-range min='6' max='20' step='1' snaps='true' [(ngModel)]='profileService.profile().font_size' (ionChange)='textSizeChanged()'>
<ion-label range-left class='small-text'>a</ion-label>
<ion-label range-right>A</ion-label>
</ion-range>
</ion-item>
<ion-item>
<ion-label>Font Family</ion-label>
<ion-select [(ngModel)]='profileService.profile().font_family' (ionChange)='fontFamilyChanged()'>
<ion-option value='Roboto, Helvetica, Arial, sans-serif'>Sans Serif</ion-option>
<ion-option value='"Times New Roman", serif'>Times</ion-option>
<ion-option value='Georgia, serif'>Georgia</ion-option>
<ion-option value='Merriweather, serif'>Merriweather</ion-option>
<ion-option value='Consolas, monotype'>Monospaced</ion-option>
<ion-option value='"PT Serif", serif'>PT Serif</ion-option>
</ion-select>
</ion-item>
</ion-list>
<ion-list>
<ion-list-header>
<ion-icon name='log-in' item-left></ion-icon>Login/Logout
</ion-list-header>
<ng-template [ngIf]='!profileService.currentUser()'>
<ion-item center>
<button ion-button block (click)='profileService.authenticate()'>Login With Google</button>
</ion-item>
</ng-template>
<ng-template [ngIf]='profileService.currentUser()'>
<ion-item center>
<button ion-button block (click)='profileService.logout()'>Logout</button>
</ion-item>
</ng-template>
</ion-list>

View File

@ -0,0 +1,3 @@
settings {
}

View File

@ -0,0 +1,30 @@
import { Component } from '@angular/core';
import { ProfileService } from '../../services/profile-service';
@Component({
selector: 'settings',
templateUrl: 'settings.html'
})
export class Settings {
constructor(
public profileService: ProfileService
) {}
textSizeChanged()
{
this.profileService.textSizeChanged();
this.profileService.localSave();
}
fontFamilyChanged()
{
this.profileService.fontFamilyChanged();
this.profileService.localSave();
}
reset()
{
this.profileService.reset()
}
}

View File

@ -1,5 +1,5 @@
import { EventEmitter, Component, Output, OnInit } from '@angular/core'; import { EventEmitter, Component, Output, OnInit } from '@angular/core';
import { Platform, NavParams, ViewController } from 'ionic-angular'; import { NavParams, ViewController } from 'ionic-angular';
import { Reference } from '../../libs/Reference'; import { Reference } from '../../libs/Reference';
import { StrongsResult, StrongsService } from '../../services/strongs-service'; import { StrongsResult, StrongsService } from '../../services/strongs-service';
@ -17,29 +17,29 @@ export class StrongsModal implements OnInit
@Output() @Output()
onItemClicked = new EventEmitter<string>(); onItemClicked = new EventEmitter<string>();
constructor(private strongsService: StrongsService, constructor(
public platform: Platform, private _strongsService: StrongsService,
public params: NavParams, private _params: NavParams,
public viewCtrl: ViewController private _viewCtrl: ViewController
) )
{ {
this.sn = this.params.get('sn') as number; this.sn = this._params.get('sn') as number;
this.dict = this.params.get('dict') as string; this.dict = this._params.get('dict') as string;
this.onItemClicked.subscribe(item => this.onItemClicked.subscribe(item =>
{ {
let pg = this.params.get('onItemClicked'); let pg = this._params.get('onItemClicked');
pg.updateUIwithItems(item, false); pg.updateUIwithItems(item, false);
}); });
} }
ngOnInit(): void ngOnInit(): void
{ {
this.strongsService.getResultAsPromise(this.sn, this.dict).then(data => this.item = data); this._strongsService.getResultAsPromise(this.sn, this.dict).then(data => this.item = data);
} }
dismiss() dismiss()
{ {
this.viewCtrl.dismiss(); this._viewCtrl.dismiss();
} }
openItem(p: string) openItem(p: string)

View File

@ -1,4 +1,4 @@
<ion-item class="title strongs-title {{data.prefix}}" padding (swipe)="close($event)"> <ion-item class="title strongs-title" padding (swipe)="close($event)">
<ion-icon name="paper" item-left></ion-icon> <span *ngIf="data !== undefined"><span *ngIf="data.status === -1">Error:</span> {{data.prefix}}{{data.sn}}</span> <ion-icon name="paper" item-left></ion-icon> <span *ngIf="data !== undefined"><span *ngIf="data.status === -1">Error:</span> {{data.prefix}}{{data.sn}}</span>
<button ion-button icon-only item-end large clear (click)="close()"> <button ion-button icon-only item-end large clear (click)="close()">
<ion-icon name="close-circle"></ion-icon> <ion-icon name="close-circle"></ion-icon>
@ -40,7 +40,9 @@
<ion-card-content *ngIf="data !== undefined && data.status === -1"> <ion-card-content *ngIf="data !== undefined && data.status === -1">
<error-message [msg]="data.msg"></error-message> <error-message [msg]="data.msg"></error-message>
</ion-card-content> </ion-card-content>
<button ion-button item-left icon-start clear small (click)="close()">
<ion-icon name="close-circle"></ion-icon> <div style="float: right">
<div>Close</div> <button ion-button icon-center clear (click)="contextMenu()">
<ion-icon name="more"></ion-icon>
</button> </button>
</div>

View File

@ -2,10 +2,7 @@
color: #307e4b; color: #307e4b;
} }
.heb-title { .strongs-title {
background-color:#c6efd4;
}
.grk-title {
background-color:#a7eebe; background-color:#a7eebe;
} }
strongs { strongs {

View File

@ -2,6 +2,10 @@ import { HostListener, EventEmitter, Component, Input, Output, OnInit, AfterView
import { Reference } from '../../libs/Reference'; import { Reference } from '../../libs/Reference';
import { OpenData, CardItem } from '../../pages/search/search'; import { OpenData, CardItem } from '../../pages/search/search';
import { StrongsResult, StrongsService } from '../../services/strongs-service'; import { StrongsResult, StrongsService } from '../../services/strongs-service';
import { cardContextMenu } from '../../libs/Common';
import { ProfileService } from '../../services/profile-service';
import { ActionSheetController, AlertController } from 'ionic-angular';
import { PagesService } from '../../services/pages-service';
@Component({ @Component({
selector: 'strongs', selector: 'strongs',
@ -21,10 +25,20 @@ export class Strongs implements AfterViewChecked, OnInit
data: StrongsResult; data: StrongsResult;
constructor(private strongsService: StrongsService, private elementRef: ElementRef) constructor(
private _elementRef: ElementRef,
private _strongsService: StrongsService,
private _profileService: ProfileService,
private _actionSheet: ActionSheetController,
private _pagesSvc: PagesService,
private _alertCtrl: AlertController)
{ {
} }
contextMenu() {
cardContextMenu(this._profileService, this._actionSheet, this._pagesSvc, this._alertCtrl, this.cardItem);
}
@HostListener('window:resize', ['$event']) @HostListener('window:resize', ['$event'])
onResize(evt) onResize(evt)
{ {
@ -45,7 +59,7 @@ export class Strongs implements AfterViewChecked, OnInit
ngOnInit(): void ngOnInit(): void
{ {
this.strongsService.getResultAsPromise(parseInt(this.cardItem.qry), this.cardItem.dict) this._strongsService.getResultAsPromise(parseInt(this.cardItem.qry), this.cardItem.dict)
.then(data => .then(data =>
{ {
this.data = data; this.data = data;
@ -64,7 +78,7 @@ export class Strongs implements AfterViewChecked, OnInit
translate = 'translate3d(-110%, 0, 0)'; translate = 'translate3d(-110%, 0, 0)';
} }
let d = 250; let d = 250;
this.elementRef.nativeElement.parentElement.animate({ this._elementRef.nativeElement.parentElement.animate({
transform: ['none', translate] transform: ['none', translate]
}, { }, {
fill: 'forwards', fill: 'forwards',

View File

@ -1,5 +1,5 @@
import { EventEmitter, Component, Output, OnInit } from '@angular/core'; import { EventEmitter, Component, Output, OnInit } from '@angular/core';
import { Platform, NavParams, ViewController } from 'ionic-angular'; import { NavParams, ViewController } from 'ionic-angular';
import { Book, Reference } from '../../libs/Reference'; import { Book, Reference } from '../../libs/Reference';
@Component({ @Component({
@ -15,16 +15,15 @@ export class VersePickerModal
book: Book; book: Book;
constructor( constructor(
public platform: Platform, private _params: NavParams,
public params: NavParams, private _viewCtrl: ViewController
public viewCtrl: ViewController
) )
{ {
this.hasBook = false; this.hasBook = false;
this.books = Reference.Books; this.books = Reference.Books;
this.onItemClicked.subscribe(item => this.onItemClicked.subscribe(item =>
{ {
let pg = this.params.get('onItemClicked'); let pg = this._params.get('onItemClicked');
pg.updateUIwithItems(item, false); pg.updateUIwithItems(item, false);
}); });
} }
@ -37,7 +36,7 @@ export class VersePickerModal
dismiss() dismiss()
{ {
this.viewCtrl.dismiss(); this._viewCtrl.dismiss();
} }
setBook(book: Book) setBook(book: Book)
{ {

View File

@ -12,7 +12,9 @@
<ion-card-content *ngIf="data !== undefined && data.status === -1"> <ion-card-content *ngIf="data !== undefined && data.status === -1">
<error-message [msg]="data.msg"></error-message> <error-message [msg]="data.msg"></error-message>
</ion-card-content> </ion-card-content>
<button ion-button icon-start clear small (click)="close()">
<ion-icon name="close-circle"></ion-icon> <div style="float: right">
<div>Close</div> <button ion-button icon-center clear (click)="contextMenu()">
<ion-icon name="more"></ion-icon>
</button> </button>
</div>

View File

@ -3,6 +3,10 @@ import { HostListener, EventEmitter, Component, Input, Output, AfterViewChecked,
import { Reference } from '../../libs/Reference'; import { Reference } from '../../libs/Reference';
import { OpenData, CardItem } from '../../pages/search/search'; import { OpenData, CardItem } from '../../pages/search/search';
import { WordLookupResult, WordService } from '../../services/word-service'; import { WordLookupResult, WordService } from '../../services/word-service';
import { cardContextMenu } from '../../libs/Common';
import { ProfileService } from '../../services/profile-service';
import { ActionSheetController, AlertController } from 'ionic-angular';
import { PagesService } from '../../services/pages-service';
@Component({ @Component({
selector: 'words', selector: 'words',
@ -24,10 +28,20 @@ export class Words implements AfterViewChecked, OnInit
data: WordLookupResult; data: WordLookupResult;
constructor(private wordService: WordService, private elementRef: ElementRef) constructor(
private _elementRef: ElementRef,
private _wordService: WordService,
private _profileService: ProfileService,
private _actionSheet: ActionSheetController,
private _pagesSvc: PagesService,
private _alertCtrl: AlertController)
{ {
} }
contextMenu() {
cardContextMenu(this._profileService, this._actionSheet, this._pagesSvc, this._alertCtrl, this.cardItem);
}
@HostListener('window:resize', ['$event']) @HostListener('window:resize', ['$event'])
onResize(evt) onResize(evt)
{ {
@ -75,7 +89,7 @@ export class Words implements AfterViewChecked, OnInit
ngOnInit(): void ngOnInit(): void
{ {
this.wordService.getResultAsPromise(this.cardItem.qry).then(data => this._wordService.getResultAsPromise(this.cardItem.qry).then(data =>
this.data = data this.data = data
); );
} }
@ -86,7 +100,7 @@ export class Words implements AfterViewChecked, OnInit
translate = 'translate3d(-110%, 0, 0)'; translate = 'translate3d(-110%, 0, 0)';
} }
let d = 250; let d = 250;
this.elementRef.nativeElement.parentElement.animate({ this._elementRef.nativeElement.parentElement.animate({
transform: ['none', translate] transform: ['none', translate]
}, { }, {
fill: 'forwards', fill: 'forwards',

View File

@ -0,0 +1,95 @@
import { ProfileService } from "../services/profile-service";
import { ActionSheetController, AlertController } from "ionic-angular";
import { PagesService } from "../services/pages-service";
import { CardItem } from "../pages/search/search";
export function cardContextMenu(
profileService: ProfileService,
actionSheet: ActionSheetController,
pagesSvc: PagesService,
alertCtrl: AlertController,
cardItem: CardItem
) {
const actions = actionSheet.create({
title: "Passage Actions",
buttons: [
{
text: "Add to a Saved Page",
icon: "add-circle",
handler: () => {
let btns = pagesSvc.getSavedPages().map(p => {
return {
text: p.title,
icon: "bookmark",
handler: () => {
const page = profileService.profile().saved_pages.find(i => i.title === p.title);
page.queries.push(cardItem);
profileService.save();
}
};
});
btns.push({
text: "Add to New Page",
icon: "create",
handler: () => {
const alert = alertCtrl.create({
title: "Save Passage as Page",
inputs: [
{
name: "title",
placeholder: "Page Title"
}
],
buttons: [
{
text: "Cancel",
role: "cancel",
handler: (): void => {
console.log("Cancel clicked");
}
},
{
text: "Save",
handler: data => {
const p = {
queries: profileService.profile().items.slice(),
title: data.title
};
profileService.profile().saved_pages.push(p);
profileService.save();
pagesSvc.addPage(p);
}
}
]
});
alert.present();
}
});
actionSheet
.create({
title: "Saved Pages",
buttons: btns
})
.present();
}
}
]
});
if (profileService.isOnSearchPage()) {
actions.addButton({
text: "Remove from " + profileService.title,
icon: "remove-circle",
handler: () => {
const page = profileService.profile().saved_pages.find(i => i.title === profileService.title);
const idx = profileService.profile().items.indexOf(cardItem);
profileService.profile().items.splice(idx, 1);
page.queries = profileService.profile().items.slice();
profileService.save();
pagesSvc.initializePages(profileService.profile().saved_pages);
}
});
}
actions.present();
}

View File

@ -0,0 +1,6 @@
export const PageTitles = {
Search: 'Search',
Help: 'Help',
Settings: 'Settings'
}

View File

@ -1,117 +0,0 @@
<ion-header>
<ion-navbar>
<button ion-button icon-only menuToggle left>
<ion-icon name="menu"></ion-icon>
</button>
<ion-title>Help</ion-title>
</ion-navbar>
</ion-header>
<ion-content padding>
<h2>How to search for a verse</h2>
<p>
To bring up a passage, just type in a reference, or use the verse picker in the top right corner. Dynamic Bible will recognize most abbreviations of books, and can handle ranges within a book. Here are a few examples to get you started:
</p>
<h3>Examples of Search Items:</h3>
<ul>
<li><b>John 1</b> (displays whole chapter)</li>
<li><b>John 3:16</b> (displays single verse)</li>
<li><b>Jn 3:16</b> (handles abbreviation of John)</li>
<li><b>Jn 3:16-17</b> (displays verses starting with chapter 1 verse 3 through chapter 1 vs 5)</li>
<li><b>John 3:16-4:4</b></li>
<li><b>Jn 3-4</b> (displays both chapters)</li>
<li><b>John 3 - John 4</b> (displays both chapters)</li>
<li><b>Jn 3:1-*</b> (this will get all the verses in the chapter. the * char can be used in verse ranges, but not chapter ranges.)</li>
<li><b>H1234</b> (displays the strongs definition for the Hebrew # 1234)</li>
<li><b>G1234</b> (displays the strongs definition for the Greek # 1234)</li>
<li><b>Jesus</b> (searches for "Jesus". all search terms assume boolean AND, i.e. "Jesus Christ" assumes "Jesus AND Christ". A word without a number is interpreted as search term).</li>
</ul>
We currently don't support ranges that cross book boundaries. If you search for "John 12 - Romans 3" you will get an error. the second book name is ignored.
<h3>Further Notes:</h3>
<ul>
<li>Multiple lookups can be made using a semicolon as a seperator, i.e. "Ruth 1; g1234; spirit").</li>
<li>All greek/hebrew cross references translations are taken from the 1933 Websters version and are sometimes not the same as the KJV translation.</li>
<li>All source materials were taken from the <a href="http://sourceforge.net/projects/zefania-sharp/">Zefania XML Bible Project</a> on SourceForge.</li>
<li>Any Errors and Omission you find would be appreciated. Please contact me via <a href='http://www.jasonwall.org/'>www.jasonwall.org</a>.</li>
</ul>
<h3>Visit Us Online</h3>
<p>
<a href="http://www.dynamicbible.com">www.dynamicbible.com</a> hosts the online version of the web app. We are currently available on the
<a href="https://bitbucket.org/walljm/dynamicbible/downloads/DynamicBible-3.1.0.zip">Windows Platform</a> via the <a href="http://electron.atom.io/">Electron app from atom.io</a>
and on the <a href="https://play.google.com/store/apps/details?id=walljm.dynamicbible">Google Android Play store</a>.
We are working on an IOS distribution and will be releasing that soon.
</p>
<h3>Book Names and Accepted Abbreviations</h3>
<ul>
<li><b>Genesis</b>: gen, ge, gn</li>
<li><b>Exodus</b>: ex, exo, exod, exd</li>
<li><b>Leviticus</b>: lev, le, levi, lv</li>
<li><b>Numbers</b>: num, nu, numb, number</li>
<li><b>Deuteronomy</b>: deut, de, dt, deu</li>
<li><b>Joshua</b>: josh, jos</li>
<li><b>Judges</b>: jud, jdg, judg</li>
<li><b>Ruth</b>: ru</li>
<li><b>1 Samuel</b>: 1, i, 1st, first samuel, sa, sam, sml</li>
<li><b>2 Samuel</b>: 2, ii, 2nd, second, sec samuel, sa, sam, sml</li>
<li><b>1 Kings</b>: 1, i, 1st, first kings, king, kgs, kn, k, ki</li>
<li><b>2 Kings</b>: 2, ii, 2nd, second, sec kings, king, kgs, kn, k, ki</li>
<li><b>1 Chronicles</b>: 1, i, 1st, first chronicles, chron, ch, chr</li>
<li><b>2 Chronicles</b>: 2, ii, 2nd, second, sec chronicles, chron, ch, chr</li>
<li><b>Ezra</b>: ezr</li>
<li><b>Nehemiah</b>: neh, ne, nehamiah</li>
<li><b>Esther</b>: est, es, esth</li>
<li><b>Job</b>: jo, jb</li>
<li><b>Psalms</b>: ps, psa, psalm, psm</li>
<li><b>Proverbs</b>: prov, pr, pro, proverb, prv, prvbs</li>
<li><b>Ecclesiastes</b>: eccl, ecc, eccles, ec, ecl, ecclesiaste</li>
<li><b>Song of Solomon</b>: , song of songs, sos, ss, son, so, song, songs</li>
<li><b>Isaiah</b>: is, isah, isai, ia</li>
<li><b>Jerimiah</b>: jeremiah, jer, je, jere</li>
<li><b>Lamentations</b>: lam, la, lamentation</li>
<li><b>Ezekiel</b>: eze, ezk, ezek</li>
<li><b>Daniel</b>: dan, dn, dl, da</li>
<li><b>Hosea</b>: hos, ho</li>
<li><b>Joel</b>: joe, jl</li>
<li><b>Amos</b>: am, amo</li>
<li><b>Obadiah</b>: oba, ob, obad</li>
<li><b>Jonah</b>: jnh, jon</li>
<li><b>Micah</b>: mic, mi</li>
<li><b>Nahum</b>: nah, na</li>
<li><b>Habakkuk</b>: hab, ha, habakuk</li>
<li><b>Zephaniah</b>: zeph, zep</li>
<li><b>Haggia</b>: hag, hg, haggai</li>
<li><b>Zechariah</b>: zech, zch, zec</li>
<li><b>Malachi</b>: mal</li>
<li><b>Matthew</b>: mt, matt, mat</li>
<li><b>Mark</b>: mrk, mk, mr</li>
<li><b>luke</b>: lu, lke, luk, lk</li>
<li><b>John</b>: jn, jhn</li>
<li><b>Acts</b>: ac, act</li>
<li><b>Romans</b>: rom, ro, rm, roman</li>
<li><b>1 Corinthians</b>: 1, i, 1st, first corinthian, cor, corinthians, corinth, corin, corth, corint</li>
<li><b>2 Corinthians</b>: 2, ii, 2nd, second, sec corinthian, cor, corinthians, corinth, corin, corth, corint</li>
<li><b>Galatians</b>: galatian, galations, gal, ga, gala, galation, galat</li>
<li><b>Ephesians</b>: eph, ep, ephes, ephe, ephs</li>
<li><b>Philippians</b>: phi, phil, ph, philip</li>
<li><b>Colossians</b>: col, co, colossian, colos, coloss</li>
<li><b>1 Thessalonians</b>: 1, i, 1st, first thessalonians, the, thessa, thessalonian, thes, thess, th</li>
<li><b>2 Thessalonians</b>: 2, ii, 2nd, second, sec thessalonians, the, thessa, thessalonian, thes, thess, th</li>
<li><b>1 Timothy</b>: 1, i, 1st, first timothy, tim, ti, timoth, tm</li>
<li><b>2 Timothy</b>: 2, ii, 2nd, second, sec timothy, tim, timoth, tm</li>
<li><b>Titus</b>: tit</li>
<li><b>Philemon</b>: phlmn, phl, phm, phile, philem</li>
<li><b>Hebrews</b>: heb, he, hebrew</li>
<li><b>James</b>: jam, ja, jas, jms, jame, jm</li>
<li><b>1 Peter</b>: 1, i, 1st, first peter, pe, pet, pete, pt, p</li>
<li><b>2 Peter</b>: 2, ii, 2nd, second, sec peter, pe, pet, pete, pt, p</li>
<li><b>1 John</b>: 1, i, 1st, first john, jn, jo</li>
<li><b>2 John</b>: 2, ii, 2nd, second, sec john, jn, jo</li>
<li><b>3 John</b>: 3, iii, 3rd, third john, jn, jo</li>
<li><b>Jude</b>: ju</li>
<li><b>Revelation</b>: rev, re, revelations, rv</li>
</ul>
</ion-content>

View File

@ -1,3 +0,0 @@
page-settings {
}

View File

@ -1,13 +0,0 @@
import { Component } from '@angular/core';
@Component({
selector: 'help',
templateUrl: 'help.html'
})
export class HelpPage
{
constructor()
{
}
}

View File

@ -1,4 +1,4 @@
<ion-menu side="right" [content]="searchcontent" id="actions"> <ion-menu side='right' [content]='searchcontent' id='actions'>
<ion-header> <ion-header>
<ion-toolbar> <ion-toolbar>
<ion-title>Actions</ion-title> <ion-title>Actions</ion-title>
@ -11,130 +11,43 @@
</ion-list-header> </ion-list-header>
<ion-list> <ion-list>
<button ion-item menuClose="actions" (click)="addPage()"> <button ion-item menuClose='actions' (click)='addPage()'>
<ion-icon name="bookmarks" item-left></ion-icon>Save Results as New Page <ion-icon name='bookmarks' item-left></ion-icon>Save Results as New Page
</button> </button>
<button *ngIf="this.profileService.isOnSearchPage()" ion-item menuClose="actions" (click)="updatePage()"> <button *ngIf='this.profileService.isOnSearchPage()' ion-item menuClose='actions' (click)='updatePage()'>
<ion-icon name="arrow-up" item-left></ion-icon>Update Page with Results <ion-icon name='arrow-up' item-left></ion-icon>Update Page with Results
</button> </button>
</ion-list> </ion-list>
<settings></settings>
<ion-list-header>
<ion-icon name="search" item-left></ion-icon>Search Settings
</ion-list-header>
<ion-item>
<ion-label>Show Strongs as Modal</ion-label>
<ion-toggle color="dark" [(ngModel)]="this.profileService.profile().strongs_modal" (ionChange)="this.profileService.localSave()"></ion-toggle>
</ion-item>
<ion-item>
<ion-label>Clear Search after Query</ion-label>
<ion-toggle color="dark" [(ngModel)]="this.profileService.profile().clear_search_after_query" (ionChange)="this.profileService.localSave()"></ion-toggle>
</ion-item>
<ion-item>
<ion-label>Append Results Below</ion-label>
<ion-toggle color="dark" [(ngModel)]="this.profileService.profile().append_to_bottom" (ionChange)="this.profileService.localSave()"></ion-toggle>
</ion-item>
<ion-item>
<ion-label>Insert Result Next to Item</ion-label>
<ion-toggle color="dark" [(ngModel)]="this.profileService.profile().insert_next_to_item" (ionChange)="this.profileService.localSave()"></ion-toggle>
</ion-item>
<ion-list-header>
<ion-icon name="browsers" item-left></ion-icon>Display Settings
</ion-list-header>
<ion-item>
<ion-label>Each Verse on New Line</ion-label>
<ion-toggle color="dark" [(ngModel)]="this.profileService.profile().verses_on_new_line" (ionChange)="this.profileService.localSave()"></ion-toggle>
</ion-item>
<ion-item>
<ion-label>Show Verse #'s</ion-label>
<ion-toggle color="dark" [(ngModel)]="this.profileService.profile().show_verse_numbers" (ionChange)="this.profileService.localSave()"></ion-toggle>
</ion-item>
<ion-item>
<ion-label>Show Paragraphs</ion-label>
<ion-toggle color="dark" [(ngModel)]="profileService.profile().show_paragraphs" (ionChange)="this.profileService.localSave()"></ion-toggle>
</ion-item>
<ion-item>
<ion-label>Show Paragraph Headings</ion-label>
<ion-toggle color="dark" [(ngModel)]="profileService.profile().show_paragraph_headings" (ionChange)="this.profileService.localSave()"></ion-toggle>
</ion-item>
<ion-list-header>
<ion-icon name="sync" item-left></ion-icon>Profile Settings
</ion-list-header>
<ion-item>
<ion-label>Sync Search Items</ion-label>
<ion-toggle color="dark" [(ngModel)]="profileService.profile().sync_search_items" (ionChange)="this.profileService.localSave()"></ion-toggle>
</ion-item>
<ion-list-header>
<ion-icon name="text" item-left></ion-icon>Adjust Text
</ion-list-header>
<ion-list>
<ion-item>
<ion-range min="6" max="20" step="1" snaps="true" [(ngModel)]="this.profileService.profile().font_size"
(ionChange)="textSizeChanged()">
<ion-label range-left class="small-text">a</ion-label>
<ion-label range-right>A</ion-label>
</ion-range>
</ion-item>
<ion-item>
<ion-label>Font Family</ion-label>
<ion-select [(ngModel)]="this.profileService.profile().font_family" (ionChange)="fontFamilyChanged()">
<ion-option value="Roboto, Helvetica, Arial, sans-serif">Sans Serif</ion-option>
<ion-option value="'Times New Roman', serif">Times</ion-option>
<ion-option value="Georgia, serif">Georgia</ion-option>
<ion-option value="Merriweather, serif">Merriweather</ion-option>
<ion-option value="Consolas, monotype">Monospaced</ion-option>
<ion-option value="'PT Serif'ß, serif">PT Serif</ion-option>
</ion-select>
</ion-item>
</ion-list>
<ion-list-header>
<ion-icon name="log-in" item-left></ion-icon>Login/Logout
</ion-list-header>
<ng-template [ngIf]="!profileService.currentUser()">
<ion-item center>
<button ion-button block (click)="profileService.authenticate()">Login With Google</button>
</ion-item>
</ng-template>
<ng-template [ngIf]="profileService.currentUser()">
<ion-item center>
<button ion-button block (click)="profileService.logout()">Logout</button>
</ion-item>
</ng-template>
</ion-content> </ion-content>
</ion-menu> </ion-menu>
<ion-header> <ion-header>
<ion-navbar> <ion-navbar>
<button ion-button icon-only menuToggle left> <button ion-button icon-only menuToggle left>
<ion-icon name="menu" large></ion-icon> <ion-icon name='menu' large></ion-icon>
</button> </button>
<ion-auto-complete [dataProvider]="autocompleteService" (search)="getQuery($event)" (input)="setQuery($event)" <ion-auto-complete [dataProvider]='autocompleteService' (search)='getQuery($event)' (input)='setQuery($event)'
(itemSelected)="itemSelected($event)" [options]="{ showCancelButton : 'true' }" #searchbar></ion-auto-complete> (itemSelected)='itemSelected($event)' [options]='{ showCancelButton : "true" }' #searchbar></ion-auto-complete>
<!--<ion-searchbar (search)="getQuery($event)" (input)="setQuery($event)" [showCancelButton]="true"></ion-searchbar>-->
<ion-buttons right> <ion-buttons right>
<button ion-button icon-only secondary (click)="versePicker()"> <button ion-button icon-only secondary (click)='versePicker()'>
<ion-icon name="albums" large></ion-icon> <ion-icon name='albums' large></ion-icon>
</button> </button>
<button ion-button icon-only secondary (click)="actionsMenu()"> <button ion-button icon-only secondary (click)='actionsMenu()'>
<ion-icon name="apps" large></ion-icon> <ion-icon name='apps' large></ion-icon>
</button> </button>
</ion-buttons> </ion-buttons>
</ion-navbar> </ion-navbar>
</ion-header> </ion-header>
<ion-content #searchcontent padding class="search-card"> <ion-content #searchcontent padding class='search-card'>
<ion-card *ngFor="let item of this.profileService.profile().items"> <ion-card *ngFor='let item of this.profileService.profile().items'>
<passage *ngIf="isPassage(item.type)" [cardItem]="item" (onClose)="this.profileService.removeItem($event)" <passage *ngIf='isPassage(item.type)' [cardItem]='item' (onClose)='this.profileService.removeItem($event)'
(onItemClicked)="getItemsNextToCard($event)"></passage> (onItemClicked)='getItemsNextToCard($event)'></passage>
<strongs *ngIf="isStrongs(item.type)" [cardItem]="item" (onClose)="this.profileService.removeItem($event)" <strongs *ngIf='isStrongs(item.type)' [cardItem]='item' (onClose)='this.profileService.removeItem($event)'
(onItemClicked)="getItemsNextToCard($event)"></strongs> (onItemClicked)='getItemsNextToCard($event)'></strongs>
<words *ngIf="isWords(item.type)" [cardItem]="item" (onClose)="this.profileService.removeItem($event)" <words *ngIf='isWords(item.type)' [cardItem]='item' (onClose)='this.profileService.removeItem($event)'
(onItemClicked)="getItemsNextToCard($event)"></words> (onItemClicked)='getItemsNextToCard($event)'></words>
<error *ngIf="isError(item.type)" [cardItem]="item" (onClose)="this.profileService.removeItem($event)"></error> <error *ngIf='isError(item.type)' [cardItem]='item' (onClose)='this.profileService.removeItem($event)'></error>
</ion-card> </ion-card>
</ion-content> </ion-content>

View File

@ -1,17 +1,18 @@
import { Type, Component, OnInit, ViewChild } from '@angular/core'; import { Type, Component, OnInit, ViewChild } from '@angular/core';
import { Loading, LoadingController, ModalController, NavParams, AlertController, MenuController } from 'ionic-angular'; import { Loading, LoadingController, ModalController, NavParams, AlertController, MenuController } from 'ionic-angular';
import { Storage } from '@ionic/storage';
import { StrongsModal } from '../../components/strongs-modal/strongs-modal';
import { PagesService } from '../../services/pages-service';
import { ProfileService, User } from './../../services/profile-service';
import { Reference } from '../../libs/Reference';
import { VersePickerModal } from '../../components/verse-picker/verse-picker';
import { AutoCompleteComponent } from 'ionic2-auto-complete'; import { AutoCompleteComponent } from 'ionic2-auto-complete';
import { StrongsModal } from '../../components/strongs-modal/strongs-modal';
import { VersePickerModal } from '../../components/verse-picker/verse-picker';
import { Settings } from '../../components/settings/settings';
import { PagesService } from '../../services/pages-service';
import { ProfileService, User } from './../../services/profile-service';
import { SearchAutoCompleteService } from '../../services/search-autocomplete-service'; import { SearchAutoCompleteService } from '../../services/search-autocomplete-service';
import { Reference } from '../../libs/Reference';
@Component({ @Component({
templateUrl: 'search.html', templateUrl: 'search.html',
providers: [SearchAutoCompleteService] providers: [SearchAutoCompleteService]

View File

@ -1,96 +0,0 @@
<ion-header>
<ion-navbar>
<button ion-button icon-only menuToggle left>
<ion-icon name="menu"></ion-icon>
</button>
<ion-title>Settings</ion-title>
</ion-navbar>
</ion-header>
<ion-content padding>
<ng-template [ngIf]="profileService.profile()">
<ion-list-header><ion-icon name="search" item-left></ion-icon>Search Settings</ion-list-header>
<ion-item>
<ion-label>Show Strongs as Modal</ion-label>
<ion-toggle color="dark" [(ngModel)]="profileService.profile().strongs_modal" (ionChange)="save()"></ion-toggle>
</ion-item>
<ion-item>
<ion-label>Clear Search after Query</ion-label>
<ion-toggle color="dark" [(ngModel)]="profileService.profile().clear_search_after_query" (ionChange)="save()"></ion-toggle>
</ion-item>
<ion-item>
<ion-label>Append Results Below</ion-label>
<ion-toggle color="dark" [(ngModel)]="profileService.profile().append_to_bottom" (ionChange)="save()"></ion-toggle>
</ion-item>
<ion-item>
<ion-label>Insert Result Next to Item</ion-label>
<ion-toggle color="dark" [(ngModel)]="profileService.profile().insert_next_to_item" (ionChange)="save()"></ion-toggle>
</ion-item>
<ion-list-header><ion-icon name="browsers" item-left></ion-icon>Display Settings</ion-list-header>
<ion-item>
<ion-label>Each Verse on New Line</ion-label>
<ion-toggle color="dark" [(ngModel)]="profileService.profile().verses_on_new_line" (ionChange)="save()"></ion-toggle>
</ion-item>
<ion-item>
<ion-label>Show Verse #'s</ion-label>
<ion-toggle color="dark" [(ngModel)]="this.profileService.profile().show_verse_numbers" (ionChange)="save()"></ion-toggle>
</ion-item>
<ion-item>
<ion-label>Show Paragraphs</ion-label>
<ion-toggle color="dark" [(ngModel)]="profileService.profile().show_paragraphs" (ionChange)="save()"></ion-toggle>
</ion-item>
<ion-item>
<ion-label>Show Paragraph Headings</ion-label>
<ion-toggle color="dark" [(ngModel)]="profileService.profile().show_paragraph_headings" (ionChange)="save()"></ion-toggle>
</ion-item>
<ion-list-header><ion-icon name="sync" item-left></ion-icon>Profile Settings</ion-list-header>
<ion-item>
<ion-label>Sync Search Items</ion-label>
<ion-toggle color="dark" [(ngModel)]="profileService.profile().sync_search_items" (ionChange)="save()"></ion-toggle>
</ion-item>
<ion-list-header><ion-icon name="text" item-left></ion-icon>Adjust Text</ion-list-header>
<ion-list>
<ion-item>
<ion-range min="6" max="20" step="1" snaps="true" [(ngModel)]="this.profileService.profile().font_size" (ionChange)="textSizeChanged()">
<ion-label range-left class="small-text">a</ion-label>
<ion-label range-right>A</ion-label>
</ion-range>
</ion-item>
<ion-item>
<ion-label>Font Family</ion-label>
<ion-select [(ngModel)]="this.profileService.profile().font_family" (ionChange)="fontFamilyChanged()">
<ion-option value="Roboto, Helvetica, Arial, sans-serif">Sans Serif</ion-option>
<ion-option value="'Times New Roman', serif">Times</ion-option>
<ion-option value="Georgia, serif">Georgia</ion-option>
<ion-option value="Merriweather, serif">Merriweather</ion-option>
<ion-option value="Consolas, monotype">Monospaced</ion-option>
</ion-select>
</ion-item>
</ion-list>
<ion-list-header><ion-icon name="log-in" item-left></ion-icon>Login/Logout</ion-list-header>
<ng-template [ngIf]="!profileService.currentUser()">
<ion-item>
<button ion-button block (click)="profileService.authenticate()">Login With Google</button>
</ion-item>
</ng-template>
<ng-template [ngIf]="profileService.currentUser()">
<ion-item>
<button ion-button block (click)="profileService.logout()">Logout</button>
</ion-item>
</ng-template>
<h4>Manage Pages</h4>
<ion-list>
<ion-item *ngFor="let p of profileService.profile().saved_pages">
{{p.title}}
<button ion-button item-end outline icon-end (click)="removePage(p)">
Delete Page <ion-icon name="trash"></ion-icon>
</button>
</ion-item>
</ion-list>
</ng-template>
</ion-content>

View File

@ -1,6 +0,0 @@
.list-header {
font-size: 1.3em;
}
.item-md .item-button {
height: 40px;
}

View File

@ -1,61 +0,0 @@
/// <reference path="../../../typings/globals/jquery/index.d.ts" />
import { Component } from '@angular/core';
import { NavController, AlertController } from 'ionic-angular';
import { Storage } from '@ionic/storage';
import { ProfileService, SavedPage } from '../../services/profile-service';
@Component({
selector: 'settings',
templateUrl: 'settings.html',
})
export class SettingsPage
{
constructor(
public navCtrl: NavController
, private alertCtrl: AlertController
, public profileService: ProfileService
) {}
textSizeChanged()
{
this.profileService.textSizeChanged();
this.profileService.localSave();
}
fontFamilyChanged()
{
this.profileService.fontFamilyChanged();
this.profileService.localSave();
}
reset()
{
this.profileService.reset()
}
removePage(page: SavedPage)
{
let alert = this.alertCtrl.create({
title: 'Confirm Delete',
message: 'Do you want to delete the ' + page.title + ' page?',
buttons: [
{
text: 'Cancel',
role: 'cancel',
handler: () =>
{
console.log('Cancel clicked');
}
},
{
text: 'Ok',
handler: () =>
{
this.profileService.removePage(page);
}
}
]
});
alert.present();
}
}

View File

@ -1,40 +1,45 @@
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { Storage } from '@ionic/storage'; import { Storage } from '@ionic/storage';
import { SearchPage } from '../pages/search/search'; import { SearchPage } from '../pages/search/search';
import { SettingsPage } from '../pages/settings/settings';
import { HelpPage } from '../pages/help/help';
import { SavedPage } from './profile-service'; import { SavedPage } from './profile-service';
import { PageTitles } from '../libs/Constants';
import { SettingsModal } from '../components/settings-modal/settings-modal';
import { AboutModal } from '../components/about-modal/about-modal';
@Injectable() @Injectable()
export class PagesService export class PagesService
{ {
pages: Array<{ title: string, component: any, params: any, icon: string }>; pages: Array<Page>;
savedPages: Array<{ title: string, component: any, params: any }>; savedPages: Array<Page>;
constructor(public local: Storage) constructor(public local: Storage)
{ {
this.pages = [ this.pages = [
{ title: 'Search', component: SearchPage, params: { queries: [], title: 'Search' }, icon: 'search' }, { title: PageTitles.Search, component: SearchPage, params: { queries: [], title: PageTitles.Search }, icon: 'search' },
{ title: 'Settings', component: SettingsPage, params: {}, icon: 'settings' }, { title: PageTitles.Settings, component: SettingsModal, params: {}, icon: 'settings' },
{ title: 'Help', component: HelpPage, params: {}, icon: 'help-circle' } { title: PageTitles.Help, component: AboutModal, params: {}, icon: 'help-circle' }
]; ];
this.savedPages = []; this.savedPages = [];
} }
getMainPages(): Array<{ title: string, component: any }> getMainPages(): Array<Page>
{ {
return this.pages; return this.pages;
} }
getSavedPages(): Array<{ title: string, component: any }> getSavedPages(): Array<Page>
{ {
return this.savedPages; return this.savedPages;
} }
addPage(page: SavedPage) addPage(page: SavedPage)
{ {
this.savedPages.push({ title: page.title, component: SearchPage, params: { queries: page.queries, title: page.title } }); this.savedPages.push({
title: page.title,
component: SearchPage,
params: { queries: page.queries, title: page.title }
});
} }
initializePages(page_array: SavedPage[]) initializePages(page_array: SavedPage[])
@ -43,7 +48,19 @@ export class PagesService
for (let p of page_array) for (let p of page_array)
{ {
this.savedPages.push({ title: p.title, component: SearchPage, params: { queries: p.queries, title: p.title } }); this.savedPages.push({
title: p.title,
component: SearchPage,
params: { queries: p.queries, title: p.title }
});
} }
} }
} }
export class Page
{
title: string;
component: any;
params: any;
icon?: string;
}

View File

@ -1,4 +1,4 @@
/// <reference path="../../typings/globals/jquery/index.d.ts" /> /// <reference path='../../typings/globals/jquery/index.d.ts' />
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { AngularFireDatabase, AngularFireObject } from 'angularfire2/database'; import { AngularFireDatabase, AngularFireObject } from 'angularfire2/database';
import { AngularFireAuth } from 'angularfire2/auth'; import { AngularFireAuth } from 'angularfire2/auth';
@ -16,10 +16,10 @@ import { Output, EventEmitter } from '@angular/core';
export const DEFAULT_USER_NAME = 'john_doe'; export const DEFAULT_USER_NAME = 'john_doe';
@Injectable() @Injectable()
export class ProfileService export class ProfileService {
{
@Output() @Output()
onSavedPagesChanged = new EventEmitter<SavedPage[]>(); onSavedPagesChanged = new EventEmitter<SavedPage[]>();
@Output() @Output()
onLocalStorageLoaded = new EventEmitter<User>(); onLocalStorageLoaded = new EventEmitter<User>();
@ -34,32 +34,24 @@ export class ProfileService
last: CardItem; last: CardItem;
title: string; title: string;
constructor( constructor(private local: Storage, private db: AngularFireDatabase, public firebaseAuth: AngularFireAuth) {
private local: Storage,
private db: AngularFireDatabase,
public firebaseAuth: AngularFireAuth
)
{
this.url = document.URL; this.url = document.URL;
this.isWeb = (document.URL.startsWith('http') && !document.URL.startsWith('http://localhost:8080')); this.isWeb = document.URL.startsWith('http') && !document.URL.startsWith('http://localhost:8080');
this.localIsLoaded = false; this.localIsLoaded = false;
// asyncrounosly kick off a poller that does the work of syncing remotely when the // asyncrounosly kick off a poller that does the work of syncing remotely when the
// profile needs to be synced. // profile needs to be synced.
(function poll(self) (function poll(self) {
{ setTimeout(function() {
setTimeout(function ()
{
// Setup the next poll recursively // Setup the next poll recursively
if (self.needsSync) if (self.needsSync) {
{
// do the sync here. // do the sync here.
// If we have a remote profile then save it there too // If we have a remote profile then save it there too
if (self.remoteProfile && self.localProfile.uid) if (self.remoteProfile && self.localProfile.uid) {
{ let st = new Date();
let st = new Date(); console.log('Saving the remote profile...'); console.log('Saving the remote profile...');
self.remoteProfile.ref.set(self.localProfile); self.remoteProfile.ref.set(self.localProfile);
console.log(' Finished saving remote profile. ' + self.elapsed(st, new Date) + 'ms'); console.log(' Finished saving remote profile. ' + self.elapsed(st, new Date()) + 'ms');
} }
self.needsSync = false; self.needsSync = false;
} }
@ -68,12 +60,10 @@ export class ProfileService
}, 2000); }, 2000);
})(this); })(this);
this.local.get('profile').then(json_profile => this.local.get('profile').then(json_profile => {
{
let t = this.profile(); let t = this.profile();
if (json_profile !== null) if (json_profile !== null) t = JSON.parse(json_profile);
t = JSON.parse(json_profile);
this.localProfile = t; this.localProfile = t;
this.localIsLoaded = true; this.localIsLoaded = true;
@ -81,9 +71,9 @@ export class ProfileService
}); });
this.firebaseAuth.authState.subscribe(state => this.subscribeToRemoteProfile(this.db, state)); this.firebaseAuth.authState.subscribe(state => this.subscribeToRemoteProfile(this.db, state));
} }
//#region Profile
removeItem(item) { removeItem(item) {
const idx = this.profile().items.indexOf(item); const idx = this.profile().items.indexOf(item);
@ -98,15 +88,12 @@ export class ProfileService
if (this.last != null && this.profile().insert_next_to_item) { if (this.last != null && this.profile().insert_next_to_item) {
const idx = this.profile().items.indexOf(this.last); const idx = this.profile().items.indexOf(this.last);
this.profile().items.splice(idx + 1, 0, item); this.profile().items.splice(idx + 1, 0, item);
} else } else this.profile().items.push(item);
this.profile().items.push(item); } else {
}
else {
if (this.last != null && this.profile().insert_next_to_item) { if (this.last != null && this.profile().insert_next_to_item) {
const idx = this.profile().items.indexOf(this.last); const idx = this.profile().items.indexOf(this.last);
this.profile().items.splice(idx, 0, item); this.profile().items.splice(idx, 0, item);
} else } else this.profile().items.unshift(item);
this.profile().items.unshift(item);
} }
this.last = null; this.last = null;
} }
@ -115,25 +102,22 @@ export class ProfileService
return this.title !== 'Search'; return this.title !== 'Search';
} }
profile(): User profile(): User {
{ if (!this.localProfile) {
if (!this.localProfile)
{
this.localProfile = ProfileService.createDefaultUser(); this.localProfile = ProfileService.createDefaultUser();
} }
return this.localProfile; return this.localProfile;
} }
subscribeToRemoteProfile(db: AngularFireDatabase, user: firebase.User) subscribeToRemoteProfile(db: AngularFireDatabase, user: firebase.User) {
{
console.log('subscribeToRemoteProfile'); console.log('subscribeToRemoteProfile');
if (!user || this.firebaseUser) return; if (!user || this.firebaseUser) return;
console.log('You got the firebase user.'); console.log('You got the firebase user.');
let obj = db.object('/settings/' + user.uid); let obj = db.object('/settings/' + user.uid);
this.remoteProfile = { this.remoteProfile = {
ref: obj as AngularFireObject<User>, ref: obj as AngularFireObject<User>,
stream: obj.valueChanges() as Observable<User>, stream: obj.valueChanges() as Observable<User>
}; };
this.firebaseUser = user; this.firebaseUser = user;
this.profile().username = user.displayName; this.profile().username = user.displayName;
@ -141,131 +125,104 @@ export class ProfileService
this.remoteProfile.stream.subscribe( this.remoteProfile.stream.subscribe(
user => this.handleRemotePreferenceChange(user), user => this.handleRemotePreferenceChange(user),
error => console.log(error)); error => console.log(error)
);
} }
comparePage(a: SavedPage, b: SavedPage) {
comparePage(a: SavedPage, b: SavedPage) if (a.title > b.title) return 1;
{ if (a.title === b.title) return 0;
if (a.title > b.title) if (a.title < b.title) return -1;
return 1;
if (a.title === b.title)
return 0;
if (a.title < b.title)
return -1;
} }
handleRemotePreferenceChange(user: User) handleRemotePreferenceChange(user: User) {
{
console.log('handleRemotePreferenceChange'); console.log('handleRemotePreferenceChange');
if (user) if (user) {
{
let changed = false; let changed = false;
if (user.saved_pages !== undefined) if (user.saved_pages !== undefined) {
{
this.localProfile.saved_pages = user.saved_pages; this.localProfile.saved_pages = user.saved_pages;
changed = true; changed = true;
} }
if (this.profile().sync_search_items) if (this.profile().sync_search_items) {
{ if (user === undefined || user.items === undefined) {
if (user === undefined || user.items === undefined)
{
this.localProfile.items = []; this.localProfile.items = [];
} } else {
else
{
this.localProfile.items = user.items; this.localProfile.items = user.items;
} }
changed = true; changed = true;
} }
// don't sync things that don't make sense. // don't sync things that don't make sense.
if (user.uid !== undefined && this.profile().uid !== user.uid) if (user.uid !== undefined && this.profile().uid !== user.uid) {
{
this.profile().uid = user.uid; this.profile().uid = user.uid;
changed = true; changed = true;
} }
if (user.username !== undefined && this.profile().username !== user.username) if (user.username !== undefined && this.profile().username !== user.username) {
{
this.profile().username = user.username; this.profile().username = user.username;
changed = true; changed = true;
} }
// We only save the local change here since this is an update from our remote profile. // We only save the local change here since this is an update from our remote profile.
if (changed) if (changed) {
{
this.localSave(); this.localSave();
} }
this.onSavedPagesChanged.emit(user.saved_pages); this.onSavedPagesChanged.emit(user.saved_pages);
} } else {
else
{
this.save(); this.save();
} }
} }
currentUser(): firebase.User currentUser(): firebase.User {
{
return this.firebaseAuth.auth.currentUser; return this.firebaseAuth.auth.currentUser;
} }
authenticate() authenticate() {
{
console.log('Authenticating to remote...'); console.log('Authenticating to remote...');
let self = this; let self = this;
let provider = new firebase.auth.GoogleAuthProvider(); let provider = new firebase.auth.GoogleAuthProvider();
if (this.isWeb) if (this.isWeb) {
{
this.firebaseAuth.auth.signInWithPopup(provider); this.firebaseAuth.auth.signInWithPopup(provider);
} } else {
else firebase
{ .auth()
firebase.auth().signInWithRedirect(provider).then(function () .signInWithRedirect(provider)
{ .then(function() {
return firebase.auth().getRedirectResult(); return firebase.auth().getRedirectResult();
}).catch(function (error) })
{ .catch(function(error) {
// Handle Errors here. // Handle Errors here.
console.log(error); console.log(error);
}); });
} }
this.firebaseAuth.authState.subscribe(state => this.subscribeToRemoteProfile(this.db, state)); this.firebaseAuth.authState.subscribe(state => this.subscribeToRemoteProfile(this.db, state));
this.remoteLoggedIn = true; this.remoteLoggedIn = true;
} }
refresh() refresh() {
{
console.log('refresh'); console.log('refresh');
this.logout(); this.logout();
this.authenticate(); this.authenticate();
} }
logout() logout() {
{
console.log('logout'); console.log('logout');
this.firebaseAuth.auth.signOut(); // sign out this.firebaseAuth.auth.signOut(); // sign out
this.remoteProfile = null; // inform the profile service not to bother this.remoteProfile = null; // inform the profile service not to bother
this.remoteLoggedIn = false; this.remoteLoggedIn = false;
} }
save() save() {
{
this.localSave(); this.localSave();
this.needsSync = true; this.needsSync = true;
} }
localSave() localSave() {
{
console.log('saving local'); console.log('saving local');
this.local.set('profile', JSON.stringify(this.profile())); this.local.set('profile', JSON.stringify(this.profile()));
} }
private elapsed(start: Date, finish: Date) {
private elapsed(start: Date, finish: Date)
{
let difference = new Date(); let difference = new Date();
difference.setTime(finish.getTime() - start.getTime()); difference.setTime(finish.getTime() - start.getTime());
return difference.getMilliseconds(); return difference.getMilliseconds();
@ -273,34 +230,27 @@ export class ProfileService
// this function updates a user object, in case new properties have been introduced // this function updates a user object, in case new properties have been introduced
// in a release. // in a release.
update(t: User): boolean update(t: User): boolean {
{
let updated = false; let updated = false;
let k; let k;
const user = this.profile(); const user = this.profile();
for (k in user) for (k in user) {
{ if (user.hasOwnProperty(k)) {
if (user.hasOwnProperty(k)) if (t[k] === undefined) {
{
if (t[k] === undefined)
{
t[k] = user[k]; t[k] = user[k];
updated = true; updated = true;
} }
} }
} }
for (k in user) for (k in user) if (user.hasOwnProperty(k)) user[k] = t[k];
if (user.hasOwnProperty(k))
user[k] = t[k];
this.textSizeChanged(); this.textSizeChanged();
this.fontFamilyChanged(); this.fontFamilyChanged();
return updated; return updated;
} }
private resetUser() private resetUser() {
{
this.profile().strongs_modal = true; this.profile().strongs_modal = true;
this.profile().clear_search_after_query = false; this.profile().clear_search_after_query = false;
this.profile().items = []; this.profile().items = [];
@ -315,15 +265,13 @@ export class ProfileService
this.profile().sync_search_items = false; this.profile().sync_search_items = false;
} }
reset() reset() {
{
this.resetUser(); this.resetUser();
this.remoteProfile.ref.set(this.profile()) this.remoteProfile.ref.set(this.profile());
this.save(); this.save();
} }
removePage(page: SavedPage) removePage(page: SavedPage) {
{
let idx = this.profile().saved_pages.indexOf(page); let idx = this.profile().saved_pages.indexOf(page);
this.profile().saved_pages.splice(idx, 1); this.profile().saved_pages.splice(idx, 1);
this.onSavedPagesChanged.emit(this.localProfile.saved_pages); this.onSavedPagesChanged.emit(this.localProfile.saved_pages);
@ -333,19 +281,16 @@ export class ProfileService
} }
// TODO(jwall): This belongs somewhere else. // TODO(jwall): This belongs somewhere else.
textSizeChanged() textSizeChanged() {
{
$('html').css('font-size', this.profile().font_size + 'px'); $('html').css('font-size', this.profile().font_size + 'px');
} }
fontFamilyChanged() fontFamilyChanged() {
{
document.querySelector('html').style.cssText = '--card-font: ' + this.profile().font_family; document.querySelector('html').style.cssText = '--card-font: ' + this.profile().font_family;
this.textSizeChanged(); this.textSizeChanged();
} }
static createDefaultUser(): User static createDefaultUser(): User {
{
return { return {
username: DEFAULT_USER_NAME, username: DEFAULT_USER_NAME,
uid: null, uid: null,
@ -365,34 +310,33 @@ export class ProfileService
sync_search_items: false sync_search_items: false
}; };
} }
//#endregion
} }
type fbObject<T> = { type fbObject<T> = {
ref: AngularFireObject<T>, ref: AngularFireObject<T>;
stream: Observable<T>, stream: Observable<T>;
}; };
export type User = { export type User = {
username: string, username: string;
uid: string | null, uid: string | null;
strongs_modal: boolean, strongs_modal: boolean;
clear_search_after_query: boolean, clear_search_after_query: boolean;
items: CardItem[], items: CardItem[];
append_to_bottom: boolean, append_to_bottom: boolean;
insert_next_to_item: boolean, insert_next_to_item: boolean;
font_size: number, font_size: number;
font_family: string, font_family: string;
saved_pages: SavedPage[], saved_pages: SavedPage[];
verses_on_new_line: boolean, verses_on_new_line: boolean;
show_verse_numbers: boolean, show_verse_numbers: boolean;
show_paragraphs: boolean, show_paragraphs: boolean;
show_paragraph_headings: boolean, show_paragraph_headings: boolean;
sync_search_items: boolean sync_search_items: boolean;
} };
export type SavedPage = { export type SavedPage = {
queries: CardItem[], queries: CardItem[];
title: string, title: string;
} };

View File

@ -40,7 +40,7 @@
"no-var-keyword": true, "no-var-keyword": true,
"object-literal-sort-keys": false, "object-literal-sort-keys": false,
"quotemark": [ "quotemark": [
true, false,
"single" "single"
], ],
"semicolon": [ "semicolon": [