Update to Angular 7

Transactions
Dan 2019-01-21 14:33:08 -07:00
parent 6835d6b60a
commit c3743cd8f0
138 changed files with 3115 additions and 1 deletions

View File

@ -0,0 +1,34 @@
/* tslint:disable:no-unused-variable */
import { TestBed, async } from '@angular/core/testing';
import { AppComponent } from './app.component';
describe('AppComponent', () => {
beforeEach(() => {
TestBed.configureTestingModule({
declarations: [
AppComponent
],
});
TestBed.compileComponents();
});
it('should create the app', async(() => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.debugElement.componentInstance;
expect(app).toBeTruthy();
}));
it(`should have as title 'app works!'`, async(() => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.debugElement.componentInstance;
expect(app.title).toEqual('app works!');
}));
it('should render title in a h1 tag', async(() => {
const fixture = TestBed.createComponent(AppComponent);
fixture.detectChanges();
const compiled = fixture.debugElement.nativeElement;
expect(compiled.querySelector('h1').textContent).toContain('app works!');
}));
});

View File

@ -0,0 +1,87 @@
import { GoogleAnalyticsService } from './../../services/google-analytics.service';
import { Component, Inject, Injectable, HostListener } from '@angular/core';
import { Router, NavigationEnd, Event } from '@angular/router';
import { EventService } from '../../services/event.service';
import { AudioPlayerService } from '../../services/audio-player.service';
import { BibleVerseService } from '../../services/bible-verse.service';
@Injectable()
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
providers:[EventService, AudioPlayerService, BibleVerseService]
})
export class AppComponent {
title = 'app works w!';
menuOpen: boolean = false;
headerOpacity: number = 0;
fadeHeader: boolean = true;
copyrightYear: number = (new Date()).getUTCFullYear();
lastRoute: string = '';
currRoute: string = '';
lastScrollPos: number = 0;
showAudioPlayer: boolean = false;
constructor(private router: Router,
private audioPlayerService: AudioPlayerService,
private googleAnalyticsService: GoogleAnalyticsService){
this.router.events.subscribe((event:Event) => {
if(event instanceof NavigationEnd) {
this.lastRoute = this.currRoute;
this.currRoute = event.urlAfterRedirects;
let doc = document.documentElement;
let scrollPos = (window.pageYOffset || doc.scrollTop) - (doc.clientTop || 0);
let url = event.urlAfterRedirects;
if (url === '/home'){
this.fadeHeader = true;
this.headerOpacity = 0;
} else {
this.fadeHeader = false;
this.headerOpacity = 1;
}
if (this.lastRoute === event.url){
window.scrollTo(0,this.lastScrollPos);
} else {
window.scrollTo(0, 0);
}
this.lastRoute = event.url;
this.lastScrollPos = scrollPos;
// Google Analytics
googleAnalyticsService.pageView(url.substr(1),url);
}
});
}
mainMenuClick(){
this.menuOpen = !this.menuOpen;
}
audioPlayerClosed(): void{
this.showAudioPlayer = false;
this.audioPlayerService.updateAudioPlayerOpen(this.showAudioPlayer);
}
audioPlayerStarted(): void{
this.showAudioPlayer = true;
this.audioPlayerService.updateAudioPlayerOpen(this.showAudioPlayer);
}
@HostListener('window:scroll', ['$event'])
onScroll(event){
if (!this.fadeHeader){
return;
}
//this.scrollElement = event.target.documentElement || event.target.body || window;
let scrollTop = event.target.documentElement.scrollTop || event.target.body.scrollTop || window.pageYOffset;
let allIn = 100;
this.headerOpacity = scrollTop / allIn;
this.headerOpacity = this.headerOpacity > 1 ? 1 : this.headerOpacity;
}
}

View File

@ -0,0 +1,45 @@
.wrapper{
text-align: left;
}
.audio-control-button{
font-size: 40px;
margin: 10px;
display: table-cell;
vertical-align: middle;
}
.padding5{
padding: 5px;
}
.duration{
display: table-cell;
vertical-align: middle;
}
.audio-control-button:hover{
cursor: pointer;
}
.audio-control-button-c{
display: table-cell;
vertical-align: middle;
}
.slider{
width: 100%;
max-width: 1000px;
display: table;
}
.slider-control{
width: 100%;
display: table-cell;
vertical-align: middle;
}
.text{
text-align: center;
margin-top: -15px;
padding-bottom: 20px;
}

View File

@ -0,0 +1,28 @@
<div class="wrapper">
<div class="slider">
<span [ngSwitch]="audioState" >
<span *ngSwitchDefault><i ofbicon class="audio-control-button">slow_motion_video</i></span>
<span *ngSwitchCase="audioStates.Loading"><i ofbicon class="audio-control-button padding5">slow_motion_video</i></span>
<span *ngSwitchCase="audioStates.Paused"><i ofbicon (click)="play()" class="audio-control-button padding5">play_arrow</i></span>
<span *ngSwitchCase="audioStates.Playing"><i ofbicon (click)="pause()" class="audio-control-button padding5">pause</i></span>
<span *ngSwitchCase="audioStates.Error"><i ofbicon (click)="play()" class="audio-control-button padding5">play_arrow</i></span>
</span>
<mat-slider class="slider-control padding5" min="0" [max]="audioDuration" [value]="currentTime" (change)="changeTime($event)"></mat-slider>
<span class="duration padding5">{{ currentTime | duration:audioDuration }}</span>
<span><i ofbicon class="audio-control-button padding5" (click)="close()">close</i></span>
</div>
<div [ngSwitch]="audioState" class="text">
<div *ngSwitchCase="audioStates.Loading">
Loading...
</div>
<div *ngSwitchCase="audioStates.Paused">
{{ audioFileTitle }}
</div>
<div *ngSwitchCase="audioStates.Playing">
{{ audioFileTitle }}
</div>
<div *ngSwitchCase="audioStates.Error">
Error Playing... {{ errorMessage }}
</div>
</div>
</div>

View File

@ -0,0 +1,110 @@
import { GoogleAnalyticsService } from './../../services/google-analytics.service';
import { Component, OnInit, OnDestroy, Output, EventEmitter } from '@angular/core';
import { AudioPlayerService } from '../../services/audio-player.service';
import { AudioStates } from '../../enum/audio-states';
@Component({
selector: 'audio-player-component',
templateUrl: './audio-player.component.html',
styleUrls: ['./audio-player.component.css'],
})
export class AudioPlayerComponent implements OnInit, OnDestroy {
audioStates = AudioStates; //Store reference to enum so we can access it in the template
audioState: AudioStates;
audioFileId: number;
audioFileTitle: string;
audioFileAuthor: string;
audioDuration: number;
currentTime: number;
primarySourceUrl: string = '';
errorMessage: string = 'Unknown Error';
private interval: any;
onStateChangedSubscription: any;
@Output()
closed: EventEmitter<string> = new EventEmitter();
@Output()
started: EventEmitter<string> = new EventEmitter();
constructor(private audioPlayerService: AudioPlayerService,
private googleAnalyticsService: GoogleAnalyticsService){
}
ngOnInit(): void{
this.onStateChangedSubscription = this.audioPlayerService.onStateChanged().subscribe(({metaData,state,error})=>{
this.parseMetaData(metaData);
this.audioState = state;
this.errorMessage = error;
this.audioDuration = this.audioPlayerService.getDuration();
this.currentTime = this.audioPlayerService.getCurrentTime();
if (this.audioState == AudioStates.Loading){
this.started.emit("loading");
}
if (this.audioState == AudioStates.Playing){
this.started.emit("playing");
this.interval = setInterval(()=>{
this.audioDuration = this.audioPlayerService.getDuration();
this.currentTime = this.audioPlayerService.getCurrentTime();
},1000);
} else {
clearInterval(this.interval);
}
this.sendGoogleAnalytics(state,error);
});
}
sendGoogleAnalytics(state: AudioStates, error: any){
switch (state) {
case AudioStates.Error:
this.googleAnalyticsService.audioError(this.audioFileId, this.audioFileTitle, error)
break;
case AudioStates.Loading:
this.googleAnalyticsService.loadingSermon(this.audioFileId,this.audioFileTitle);
break;
case AudioStates.Paused:
this.googleAnalyticsService.pauseSermon(this.audioFileId,this.audioFileTitle,this.audioPlayerService.getCurrentTime())
break;
case AudioStates.Playing:
this.googleAnalyticsService.playSermon(this.audioFileId,this.audioFileTitle,this.audioPlayerService.getCurrentTime())
break;
default:
break;
}
}
play(): void{
this.audioPlayerService.resume();
}
pause(): void{
this.audioPlayerService.pause();
}
close(): void{
this.audioPlayerService.stop();
this.closed.emit("closed");
}
changeTime(event){
this.audioPlayerService.setPosition(event.value);
}
ngOnDestroy(): void{
this.onStateChangedSubscription.unsubscribe();
}
private parseMetaData(metaData): void{
this.audioFileId = metaData.id;
this.audioFileTitle = metaData.title;
this.audioFileAuthor = metaData.author;
}
}

View File

@ -0,0 +1,15 @@
.full-width{
width: 100%;
}
.hide{
display: none;
}
.errorMessages{
color: white;
background-color: rgb(255,90,90);
padding: 10px;
border-radius: 3px;
margin-bottom: 5px;
}

View File

@ -0,0 +1,35 @@
<secondary-page-component [hideSideBarOnMobile]="true" >
<div mainContent>
<br>
<div *ngIf="!formSubmitted">
<form class="form" #contactForm="ngForm" (ngSubmit)="onSubmit()">
<mat-form-field class="full-width">
<input matInput type="text" placeholder="Name" required value="" [(ngModel)]="name" name="name" >
</mat-form-field>
<mat-form-field class="full-width">
<input matInput type="email" placeholder="Email" required value="" [(ngModel)]="email" name="email" pattern="^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$">
</mat-form-field>
<mat-form-field class="full-width">
<input matInput type="tel" placeholder="Phone" value="" [(ngModel)]="phone" name="phone">
</mat-form-field>
<mat-form-field class="full-width">
<textarea matInput type="text" placeholder="Message" required value="" [(ngModel)]="body" name="body" rows="5" ></textarea>
</mat-form-field>
<mat-form-field class="hide">
<input matInput type="text" placeholder="hp" required value="" [(ngModel)]="hp" name="subject">
</mat-form-field>
<div class="errorMessages" *ngIf="errorMessages.length > 0">
<p *ngFor="let error of errorMessages">{{error}}</p>
</div>
<button mat-raised-button type="submit" [disabled]="!contactForm.form.valid || submitButtonDisabled">{{submitButtonText}}</button>
</form>
</div>
<div *ngIf="formSubmitted">
<p><b>Thank You!</b></p>
<p>Your message has been sent.</p>
</div>
</div>
<div sideBar ofbFadeInOnScroll>
</div>
</secondary-page-component>

View File

@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { ContactPageComponent } from './contact-page.component';
describe('ContactPageComponent', () => {
let component: ContactPageComponent;
let fixture: ComponentFixture<ContactPageComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ ContactPageComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(ContactPageComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should be created', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,79 @@
import { Router } from '@angular/router';
import { MatDialogConfig } from '@angular/material';
import { OkPopupComponent } from './../popups/ok-popup/ok-popup.component';
import { EmailService } from './../../services/email.service';
import { Component, OnInit } from '@angular/core';
import { MatDialog } from '@angular/material';
@Component({
selector: 'app-contact-page',
templateUrl: './contact-page.component.html',
styleUrls: ['./contact-page.component.css']
})
export class ContactPageComponent implements OnInit {
public submitButtonText: string = "Submit";
public submitButtonDisabled: boolean = false;
public formSubmitted: boolean = false;
public name: string;
public email: string;
public phone: string;
public body: string;
public hp: string = ".";
public errorMessages: string[] = [];
constructor(private emailService: EmailService,
private MatDialog: MatDialog,
private router: Router) { }
ngOnInit() {
}
onSubmit(){
this.errorMessages = [];
if (this.name == null || this.name == ""){
this.errorMessages.push("Please enter a name");
}
if (this.email == null || this.email == ""){
this.errorMessages.push("Please enter an email address");
}
if (this.body == null || this.body == ""){
this.errorMessages.push("Please enter a message");
}
if (this.errorMessages.length > 0){ return; }
this.submitButtonText = "Please Wait...";
this.submitButtonDisabled = true;
this.emailService.sendEmail(this.name,
this.email,
this.phone,
this.body,
this.hp)
.subscribe(
success => {this.emailSuccess();},
error => {this.emailError();});
}
private emailSuccess(){
let opts = new MatDialogConfig;
opts.data = { title:'Email Sent','message':'Thank You! Your message has been sent.' };
let popup = this.MatDialog.open(OkPopupComponent,opts);
this.submitButtonText = "Submit";
this.submitButtonDisabled = false;
popup.afterClosed().subscribe(()=>{
this.formSubmitted = true;
});
}
private emailError(){
console.error("error");
let opts = new MatDialogConfig;
opts.data = { title:'Email Error','message':'Please make sure that you have entered a valid email address.' };
let popup = this.MatDialog.open(OkPopupComponent,opts);
this.submitButtonText = "Submit";
this.submitButtonDisabled = false;
}
}

View File

@ -0,0 +1,25 @@
import { Component, Input, OnInit } from '@angular/core';
import { MONTHS } from '../../constants/months';
@Component({
selector: 'date-component',
template: '<div class="wrapper"><div class="month">{{ month }}</div><div class="day">{{ day }}</div></div>',
styles: ['.month{ color:white; background-color:red; width:40px; font-size: .75rem; text-align:center; } .day{ text-align:center; background:linear-gradient(to bottom, #ededed 0%,#ffffff 100%); }'],
})
export class DateComponent implements OnInit {
@Input()
date: Date = new Date();
month: number;
day: number;
constructor(){
}
ngOnInit(): void{
let monthNum = this.date.getMonth();
this.month = MONTHS[monthNum];
this.day = this.date.getDate();
}
}

View File

@ -0,0 +1,92 @@
.wrapper{
box-shadow: 0 3px 1px -2px rgba(0,0,0,.2), 0 2px 2px 0 rgba(0,0,0,.14), 0 1px 5px 0 rgba(0,0,0,.12);
}
.photo{
box-sizing: border-box;
display: inline-block;
max-width: 100px;
margin: 0;
vertical-align: top;
}
.fade{
text-align: center;
vertical-align: bottom;
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 20px;
padding-bottom: 2px;
/* Permalink - use to edit and share this gradient: http://colorzilla.com/gradient-editor/#ffffff+0,ffffff+100&0+0,0.8+100 */
background: -moz-linear-gradient(top, rgba(255,255,255,0) 0%, rgba(255,255,255,0.8) 100%); /* FF3.6-15 */
background: -webkit-linear-gradient(top, rgba(255,255,255,0) 0%,rgba(255,255,255,0.8) 100%); /* Chrome10-25,Safari5.1-6 */
background: linear-gradient(to bottom, rgba(255,255,255,0) 0%,rgba(255,255,255,0.8) 100%); /* W3C, IE10+, FF16+, Chrome26+, Opera12+, Safari7+ */
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#00ffffff', endColorstr='#ccffffff',GradientType=0 ); /* IE6-9 */
}
.photo button{
padding: 0;
vertical-align: top;
}
button img{
display: block;
height: 100%;
width: 100%;
}
.info{
display: inline-block;
vertical-align: top;
padding: 2px 2px 8px 5px;
box-sizing: border-box;
width: calc(100% - 100px);
overflow-y: hidden;
position: relative;
height: 100px;
}
.title{
font-weight: bold;
}
.date, .author{
font-style: italic;
font-size: 14px;
}
.description{
font-size: 14px;
color: darkgray;
word-wrap: none;
border-top: 0px solid lightgray;
padding-top:6px;
margin-top: 6px;
}
.expanded{
overflow-y: hidden;
}
.expanded-content{
margin: 10px;
}
.buttons, .buttons-edit{
background-color: lightgray;
}
.buttons button, .buttons .filler{
display: inline-block;
width: 33.33333%;
}
.border{
margin: 0px 5px 0px 5px;
border-top: 1px inset lightgray;
}
.buttons-edit button{
display: inline-block;
width: 50%;
}

View File

@ -0,0 +1,32 @@
<div class="wrapper" [@inout]="startFadeIn">
<div class="photo">
<button mat-button (click)="toggleState()">
<img src="{{imgSrc}}" />
</button>
</div><!--
--><div class="info" (click)="toggleState()">
<span class="title">{{ title }}</span>
<br>
<span class="date">{{ startDate | ofbDate:true }}</span>
<br>
<p class="description" [@toggleAnimationFade]="state">
{{ description }}
</p>
<div class="fade"><i ofbicon *ngIf="state === 'closed'">arrow_drop_down</i><i ofbicon *ngIf="state === 'open'">arrow_drop_up</i></div>
</div>
<div class="expanded" [@toggleAnimation]="state" (click)="toggleState()">
<p class="expanded-content">
{{ description }}
</p>
</div>
<div class="buttons">
<span class="action pct10 filler"></span><!--
--><span class="action pct10 filler"></span><!--
--><button mat-button class="action pct10" (click)="share()" ><i ofbicon>share</i> Share</button>
</div>
<div class="buttons-edit" *ngIf="loggedIn">
<div class="border"></div>
<span class="action filler"></span><!--
--><button mat-button class="action" (click)="delete()" ><i ofbicon>delete_forever</i> Delete</button>
</div>
</div>

View File

@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { EventLargeComponent } from './event-large.component';
describe('EventLargeComponent', () => {
let component: EventLargeComponent;
let fixture: ComponentFixture<EventLargeComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ EventLargeComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(EventLargeComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should be created', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,109 @@
import { Component, AfterContentInit, Input, Inject } from '@angular/core';
import { MatDialog, MatDialogConfig } from '@angular/material';
import { trigger, state, style, transition, animate } from '@angular/animations';
import { EventService } from './../../services/event.service';
import { SharePopupComponent } from './../popups/share-popup/share-popup.component';
import { YesNoPopupComponent } from './../popups/yes-no-popup/yes-no-popup.component';
import { MONTHS_FULL } from '../../constants/months';
@Component({
selector: 'event-large-component',
templateUrl: './event-large.component.html',
styleUrls: ['./event-large.component.css'],
animations:[
trigger("inout",[
state("1", style({ opacity: '1' })),
state("0",style({ opacity: '0' })),
transition('* => 1',[
animate('500ms ease-in-out')
]),
transition(':enter',[
animate('0ms')
])
]),
trigger('toggleAnimation', [
state('open', style({
height: '*',
})),
state('closed', style({
height: '0px',
})),
transition('open <=> closed', animate('500ms ease-in-out'))
]),
trigger('toggleAnimationFade', [
state('open', style({
opacity: 0,
})),
state('closed', style({
opacity: 1,
})),
transition('open <=> closed', animate('500ms ease-in-out'))
])
]
})
export class EventLargeComponent implements AfterContentInit {
public state: string = 'closed';
@Input()
public loggedIn: boolean;
@Input()
id: number;
@Input()
title: string;
@Input()
description: string;
@Input()
startDate: Date;
@Input()
endDate: Date;
public startFadeIn: boolean = false;
@Input()
public delayFadeIn: number = 100;
public _imgSrc: string;
public get imgSrc(): string{
var monthNum = this.startDate.getMonth();
var monthName = '';
monthName = MONTHS_FULL[monthNum];
return "/api2/cim/" + monthName + "/" + this.startDate.getDate();
}
constructor(private dialog: MatDialog,
private eventService: EventService){
}
ngAfterContentInit(): void{
setTimeout(() => this.startFadeIn = true, this.delayFadeIn);
}
toggleState(){
this.state = this.state === 'open' ? 'closed' : 'open'
}
share(){
let opts = new MatDialogConfig;
opts.data = { prefix: 'e', id: this.id, title: this.title, description: this.description };
let dialog = this.dialog.open(SharePopupComponent, opts);
}
delete(){
let opts = new MatDialogConfig;
opts.data = { title:'Delete','message':'Are you sure you want to delete "' + this.title + '"?' };
let dialog = this.dialog.open(YesNoPopupComponent, opts);
dialog.afterClosed().subscribe(s => {
s = s as boolean;
if (s === true){
this.eventService.deleteEvent(this.id).subscribe(
success => { },
error => { console.error(error); }
);
}
});
}
}

View File

@ -0,0 +1,23 @@
.inner-wrapper{
position:relative;
}
.title{
display: table;
font-size: 1.25em;
}
.title-text{
vertical-align: middle;
display: table-cell;
padding-left: 10px;
font-size: .95rem;
}
a{
position: absolute;
right: 0;
bottom: 0;
left: 0;
top: 0;
}

View File

@ -0,0 +1,9 @@
<div class="wrapper" [@inout]="startFadeIn">
<div class="inner-wrapper">
<div class="title">
<a></a>
<date-component class="title-date" [date]="startDate"></date-component>
<span class="title-text">{{ title }}</span>
</div>
</div>
</div>

View File

@ -0,0 +1,40 @@
import { Component, AfterContentInit, Input } from '@angular/core';
import { trigger, state, style, transition, animate } from '@angular/animations';
@Component({
selector: 'event-component',
templateUrl: './event.component.html',
styleUrls: ['./event.component.css'],
animations:[
trigger("inout",[
state("1", style({ opacity: '1', transform:'translateX(0)' })),
state("0",style({ opacity: '0', transform:'translateX(5%)' })),
transition('* => 1',[
animate('500ms ease-in-out')
]),
transition(':enter',[
animate('0ms')
])
])
]
})
export class EventComponent implements AfterContentInit {
@Input()
title: string;
@Input()
description: string;
@Input()
startDate: Date;
@Input()
endDate: Date;
public startFadeIn: boolean = false;
@Input()
public delayFadeIn: number = 100;
constructor(){
}
ngAfterContentInit(): void{
setTimeout(() => this.startFadeIn = true, this.delayFadeIn);
}
}

View File

@ -0,0 +1,49 @@
ul{
list-style: none;
}
li{
margin: 40px 0px 40px 0px; /* top right bottom left */
}
li:first-child{
margin-top: 0px;
}
li:last-child{
margin-bottom: 0px;
}
.side-bar{
position: fixed;
}
.width100{
width: 100%;
}
.fab-buttons{
display: none;
}
#showAllButton{
margin-top: 10px;
}
#audio-player-filler{
height: 60px;
background-color: inherit;
}
@media(max-width:800px){
.mobile-search{
display: block;
}
.fab-buttons{
display: inline-block;
position: fixed;
bottom: 20px;
right: 20px;
}
}

View File

@ -0,0 +1,27 @@
<secondary-page-component [hideSideBarOnMobile]="true" >
<div mainContent>
<p *ngIf="loading">Loading upcoming events...</p>
<ul>
<li *ngFor="let event of events; let i = index;">
<event-large-component
[id]="event.id"
[title]="event.title"
[startDate]="event.startDate"
[description]="event.description"
[delayFadeIn]="(i+1)*200"
[loggedIn]="loggedIn"
></event-large-component>
</li>
</ul>
<button mat-button *ngIf="showAllButtonVisible" id="showAllButton" class="width100" style="text-align: center;" routerLink="/events" >
<i ofbicon >expand_more</i><span *ngIf="!loading"> Show All</span>
</button>
<div class="fab-buttons" >
<button mat-fab *ngIf="loggedIn" (click)="addEvent()"><i ofbicon>add</i></button>
<div id="audio-player-filler" *ngIf="audioPlayerOpen"></div>
</div>
</div>
<div sideBar class="side-bar">
<button mat-raised-button *ngIf="loggedIn" class="width100" (click)="addEvent()" >Add Event</button>
</div>
</secondary-page-component>

View File

@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { EventsPageComponent } from './events-page.component';
describe('EventsPageComponent', () => {
let component: EventsPageComponent;
let fixture: ComponentFixture<EventsPageComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ EventsPageComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(EventsPageComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should be created', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,45 @@
import { Component, OnChanges, SimpleChanges, Input } from '@angular/core';
import { trigger, transition, state, style, animate } from '@angular/animations';
@Component({
selector: 'icon-button-component',
template: `<button mat-button *ngIf="routerLink" [@menuOpened]="menuOpened" class="width100" style="text-align: left;" routerLink="{{routerLink}}"><i ofbicon [iconName]="icon"></i> {{text}}</button>
<button mat-button *ngIf="!routerLink" [@menuOpened]="menuOpened" class="width100" style="text-align: left;"><i ofbicon [iconName]="icon"></i> {{text}}</button>`,
styles: ['.width100{width: 100%;}button{font-size: 16px;margin-bottom: 10px;}'],
animations: [trigger(
'menuOpened',
[
state('1',style({transform:'translateX(0)'})),
state('0',style({transform: 'translateX(80%)'})),
transition("0 => 1", [
animate('400ms ease-in-out')
]),
transition("1 => 0", [
animate('300ms ease-in-out')
])
])]
})
export class IconButtonComponent implements OnChanges {
@Input()
public routerLink: string;
@Input()
public icon: string;
@Input()
public text: string;
@Input()
public menuOpen: boolean = true;
public menuOpened: boolean = false;
@Input()
public animationDelay: number = 100;
ngOnChanges(changes: SimpleChanges){
var self = this;
if(changes.hasOwnProperty("menuOpen")){
if (self.menuOpen === true){
setTimeout(function(){ self.menuOpened = self.menuOpen; },self.animationDelay);
} else {
setTimeout(function(){ self.menuOpened = self.menuOpen; },self.animationDelay);
}
}
}
}

View File

@ -0,0 +1,9 @@
.map{
width: 100%;
height: 450px;
}
#verseFrame{
display: block;
border: none;
}

View File

@ -0,0 +1,12 @@
<secondary-page-component [hideSideBarOnMobile]="true" >
<div mainContent class="mapWrapper">
<p *ngIf="mapLoading">Loading Google Map...</p>
<iframe class="map" (load)="mapLoaded()" src="https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d2773.999802164379!2d-112.51416668494458!3d45.951286979109824!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x535b078c3c74ea33%3A0xac299097142c5894!2sOld+Fashion+Baptist+Church!5e0!3m2!1sen!2sus!4v1493451790286" frameborder="0" style="border:0" allowfullscreen></iframe>
</div>
<div sideBar ofbFadeInOnScroll>
We are located South West of the Copper King hotel in Butte, Montana just off of Motor View Road.
<br><br><br>
Trust in the LORD with all thine heart; and lean not unto thine own understanding.
In all thy ways acknowledge him, and he shall direct thy paths. <a href="https://www.kingjamesbibleonline.org/Proverbs-3-5_3-6/">(Proverbs 3:5 - 6)</a>
</div>
</secondary-page-component>

View File

@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { LocationComponent } from './location.component';
describe('LocationComponent', () => {
let component: LocationComponent;
let fixture: ComponentFixture<LocationComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ LocationComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(LocationComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,30 @@
import { BibleVerseService } from './../../services/bible-verse.service';
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'location-component',
templateUrl: './location.component.html',
styleUrls: ['./location.component.css']
})
export class LocationComponent implements OnInit {
public mapLoading: boolean = true;
constructor(private bibleVerseService: BibleVerseService) { }
ngOnInit() {
// this.bibleVerseService.randomVerse().subscribe(
// verse => {
// },
// error => console.log(error) );
}
mapLoaded(){
this.mapLoading = false;
}
verseLoaded(){
console.log("DONE");
}
}

View File

@ -0,0 +1,32 @@
#menu-background{
position:fixed;
top: 0px;
left: 0px;
width: 100%;
height: 100%;
background-color: rgba(0,0,0,.7);
z-index: 10000;
}
.width100{
width: 100%;
}
#close-button{
margin-left: 7px;
}
#header-menu{
position: fixed;
top: 0px;
bottom: 0px;
right: 0px;
width: 160px;
overflow-y: auto;
background-color: rgba(50,50,50,1);
color: white;
overflow-x: hidden;
}

View File

@ -0,0 +1,16 @@
<div id="menu-background" [@backgroundMenuOpen]="menuOpen" (click)="menuClick($event)">
<div id="header-menu" [@menuOpen]="menuOpen">
<ul class="width100">
<button mat-icon-button id="close-button" (click)="menuClick($event)"><i ofbicon>close</i></button>
<li class="width100"><icon-button-component [menuOpen]="menuOpen" animationDelay="0" icon="help_outline" routerLink="/whoweare" text="Who We Are"></icon-button-component></li>
<li class="width100"><icon-button-component [menuOpen]="menuOpen" animationDelay="50" icon="access_time" routerLink="/services" text="Service Times"></icon-button-component></li>
<li class="width100"><icon-button-component [menuOpen]="menuOpen" animationDelay="100" icon="place" routerLink="/location" text="Location"></icon-button-component></li>
<li class="width100"><icon-button-component [menuOpen]="menuOpen" animationDelay="150" icon="headset" routerLink="/sermons" text="Sermons"></icon-button-component></li>
<li class="width100"><icon-button-component [menuOpen]="menuOpen" animationDelay="200" icon="event" routerLink="/events" text="Events"></icon-button-component></li>
<li class="width100"><icon-button-component [menuOpen]="menuOpen" animationDelay="250" icon="mail_outline" routerLink="/contact" text="Contact"></icon-button-component></li>
<li class="width100"><icon-button-component [menuOpen]="menuOpen" animationDelay="300" icon="input" (click)="showLogin($event)" text="Login"></icon-button-component></li>
</ul>
</div>
</div>

View File

@ -0,0 +1,59 @@
import { Component,
Input,
Output,
EventEmitter } from '@angular/core';
import { trigger,
state,
style,
transition,
animate } from '@angular/animations';
import { MatDialog } from '@angular/material';
import { LoginPopupComponent } from '../popups/login-popup/login-popup.component';
@Component({
selector: 'menu-component',
templateUrl: './menu.component.html',
styleUrls: ['./menu.component.css'],
animations: [
trigger('menuOpen', [
state('1', style({transform: 'translateX(0)'})),
state('0', style({transform: 'translateX(100%)'})),
transition('0 => 1', [
animate('400ms ease-in-out', style({transform: 'translateX(0)'}))
]),
transition('1 => 0', [
animate('400ms 100ms ease-in-out', style({transform: 'translateX(100%)'}))
])
]),
trigger('backgroundMenuOpen', [
state('1', style({opacity: '1'})),
state('0', style({opacity: '0', display:'none'})),
transition('0 => 1', [
animate('400ms ease-in-out', style({opacity: '1'}))
]),
transition('1 => 0', [
animate('400ms 100ms ease-in-out', style({opacity: '0'}))
])
])]
})
export class MenuComponent {
@Input()
menuOpen: boolean = false;
@Output()
closed: EventEmitter<boolean> = new EventEmitter<boolean>();
constructor(private MatDialog: MatDialog){
}
menuClick(event): void{
event.stopPropagation();
this.menuOpen = !this.menuOpen;
this.closed.emit(this.menuOpen);
}
showLogin(event){
event.preventDefault();
let dialog = this.MatDialog.open(LoginPopupComponent);
}
}

View File

@ -0,0 +1,15 @@
.full-width{
width: 100%;
}
form{
margin-top: 7px;
}
button{
margin: 20px 0px 20px 20px;
}
.first{
margin-left: 0px;
}

View File

@ -0,0 +1,28 @@
<div md-dialog-title>
<p>Add Event</p>
</div>
<div md-dialog-content>
<form #addEventForm="ngForm" (ngSubmit)="onSubmit()">
<mat-form-field class="full-width">
<input matInput placeholder="Title" required [(ngModel)]="eventTitle" name="title" >
</mat-form-field>
<mat-form-field class="full-width">
<input matInput placeholder="Start Date" required [ngModel]="eventStartDate | date:'yyyy-MM-ddTHH:mm'" (ngModelChange)="eventStartDate = $event" name="startDate" type="datetime-local">
</mat-form-field>
<mat-form-field class="full-width">
<input matInput placeholder="End Date" required [ngModel]="eventEndDate | date:'yyyy-MM-ddTHH:mm'" (ngModelChange)="eventEndDate = $event" name="endDate" type="datetime-local">
</mat-form-field>
<mat-form-field class="full-width">
<input matInput placeholder="Description" required [(ngModel)]="eventDescription" name="description" >
</mat-form-field>
<br><br>
<div *ngIf="errorMessages.length > 0" class="errorMessages">
<p *ngFor="let error of errorMessages">
{{error}}
</p>
</div>
<button mat-raised-button class="first" (click)="cancel($event)" >Cancel</button><!--
--><button mat-raised-button type="submit" [disabled]="!addEventForm.form.valid || addEventButtonDisabled">{{ addEventButtonText }}</button>
</form>
</div>

View File

@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { AddEventPopupComponent } from './add-event-popup.component';
describe('AddEventPopupComponent', () => {
let component: AddEventPopupComponent;
let fixture: ComponentFixture<AddEventPopupComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ AddEventPopupComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(AddEventPopupComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should be created', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,34 @@
.full-width{
width: 100%;
}
form{
margin-top: 7px;
}
.progressPct{
display: inline-block;
width: 80px;
margin-right: 10px;
}
.progressBar{
width: calc(100% - 100px);
}
button{
margin: 20px 0px 20px 20px;
}
.first{
margin-left: 0px;
}
.errorMessages{
color: white;
background-color: rgb(255,90,90);
padding: 10px;
border-radius: 3px;
margin-bottom: 5px;
}

View File

@ -0,0 +1,35 @@
<div md-dialog-title>
Add Sermon<br>
<div *ngIf="monitorProgress">
<span class="progressPct">{{ uploadProgress/uploadTotal | percent:'1.0-2' }}</span>
<mat-slider class="progressBar" min="0" [max]="uploadTotal" step="1" [value]="uploadProgress"></mat-slider>
</div>
</div>
<div md-dialog-content>
<form #addSermonForm="ngForm" (ngSubmit)="onSubmit()">
<mat-form-field class="full-width">
<input matInput placeholder="Title" required [(ngModel)]="sermonTitle" name="title" >
</mat-form-field>
<mat-form-field>
<input matInput placeholder="Date" required [ngModel]="sermonDate | date:'yyyy-MM-ddTHH:mm'" (ngModelChange)="sermonDate = $event" name="date" type="datetime-local">
</mat-form-field>
<mat-form-field class="full-width">
<input matInput placeholder="Speaker" required [(ngModel)]="sermonSpeaker" name="speaker" >
</mat-form-field>
<mat-form-field class="full-width">
<input matInput placeholder="Description" required [(ngModel)]="sermonDescription" name="description" >
</mat-form-field>
<input type="file" (change)="onFileChange($event)" placeholder="Choose File" accept=".mp3,audio/mpeg3" >
<br><br>
<div *ngIf="errorMessages.length > 0" class="errorMessages">
<p *ngFor="let error of errorMessages">
{{error}}
</p>
</div>
<button mat-raised-button class="first" (click)="cancel($event)" >Cancel</button><!--
--><button mat-raised-button type="submit" [disabled]="!addSermonForm.form.valid || addSermonButtonDisabled">{{ addSermonButtonText }}</button>
</form>
</div>

View File

@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { AddSermonPopupComponent } from './add-sermon-popup.component';
describe('AddSermonPopupComponent', () => {
let component: AddSermonPopupComponent;
let fixture: ComponentFixture<AddSermonPopupComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ AddSermonPopupComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(AddSermonPopupComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should be created', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,7 @@
.align-right{
text-align: right;
}
.display-block{
display: block;
}

View File

@ -0,0 +1,10 @@
<div md-dialog-title>{{title}}</div>
<div md-dialog-content>
{{message}}<br>
<mat-form-field>
<input matInput [(ngModel)]="searchTerm" (keyup.enter)="onEnter()" name="search" placeholder="Search">
</mat-form-field>
</div>
<div md-dialog-actions class="align-right display-block">
<button mat-raised-button (click)="ok()" class="display-inline">OK</button>
</div>

View File

@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { InputPopupComponent } from './input-popup.component';
describe('InputPopupComponent', () => {
let component: InputPopupComponent;
let fixture: ComponentFixture<InputPopupComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ InputPopupComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(InputPopupComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should be created', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,32 @@
import { MatDialogRef } from '@angular/material';
import { MAT_DIALOG_DATA } from '@angular/material';
import { Component, OnInit, Inject } from '@angular/core';
@Component({
selector: 'app-input-popup',
templateUrl: './input-popup.component.html',
styleUrls: ['./input-popup.component.css']
})
export class InputPopupComponent implements OnInit {
public title: string;
public message: string;
public searchTerm: string = "";
constructor(@Inject(MAT_DIALOG_DATA) public data: any, private MatDialogRef: MatDialogRef<InputPopupComponent>) {
this.title = data.title;
this.message = data.message;
}
ngOnInit() {
}
ok(){
this.MatDialogRef.close(this.searchTerm);
}
onEnter(){
this.MatDialogRef.close(this.searchTerm);
}
}

View File

@ -0,0 +1,14 @@
.input-container{
display: block;
padding: 10px 0px 10px 0px;
}
button{
margin: 20px 0px 20px 20px;
}
.first{
margin-left: 0px;
}

View File

@ -0,0 +1,15 @@
<div md-dialog-title>
Login
</div>
<div md-dialog-content>
<form class="form" #loginForm="ngForm" (ngSubmit)="onSubmit()">
<mat-form-field class="input-container">
<input matInput placeholder="Username" required value="" [(ngModel)]="username" name="username">
</mat-form-field>
<mat-form-field class="input-container">
<input type="password" matInput placeholder="Password" required value="" [(ngModel)]="password" name="password">
</mat-form-field>
<button mat-raised-button type="button" class="first" (click)="cancel()">Cancel</button><!--
--><button mat-raised-button type="submit" [disabled]="!loginForm.form.valid || loginButtonDisabled">{{loginButtonText}}</button>
</form>
</div>

View File

@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { LoginPopupComponent } from './login-popup.component';
describe('LoginPopupComponent', () => {
let component: LoginPopupComponent;
let fixture: ComponentFixture<LoginPopupComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ LoginPopupComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(LoginPopupComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should be created', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,5 @@
<div md-dialog-title>{{title}}</div>
<div md-dialog-content>{{message}}</div>
<div md-dialog-actions>
<button mat-raised-button (click)="close()">OK</button>
</div>

View File

@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { OkPopupComponent } from './ok-popup.component';
describe('OkPopupComponent', () => {
let component: OkPopupComponent;
let fixture: ComponentFixture<OkPopupComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ OkPopupComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(OkPopupComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should be created', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,27 @@
import { MatDialogRef } from '@angular/material';
import { MAT_DIALOG_DATA } from '@angular/material';
import { Component, OnInit, Inject } from '@angular/core';
@Component({
selector: 'app-ok-popup',
templateUrl: './ok-popup.component.html',
styleUrls: ['./ok-popup.component.css']
})
export class OkPopupComponent implements OnInit {
public title: string;
public message: string;
constructor(@Inject(MAT_DIALOG_DATA) public data: any, private MatDialogRef: MatDialogRef<OkPopupComponent>) {
this.title = data.title;
this.message = data.message;
}
ngOnInit() {
}
close(){
this.MatDialogRef.close();
}
}

View File

@ -0,0 +1,41 @@
.width100{
width: 100%;
}
.shareHeader{
font-size: 25px;
}
.social-wrapper{
box-sizing: border-box;
width: calc(100% - 10px);
text-align: center;
color: white;
padding: 10px;
border-radius: 5px;
margin: 5px;
}
.social-wrapper:hover{
cursor: pointer;
}
.social-wrapper > span{
vertical-align: top;
}
.facebook{
background-color: #3B5998;
}
.facebook:hover{
background-color: #3B5998;
}
.twitter{
background-color: #55acee;
}
.copy{
background-color: #7d7d7d;
}

View File

@ -0,0 +1,39 @@
<div md-dialog-title>Share</div>
<div md-dialog-content>
<a (click)="facebookShare()" [href]="facebookIframeUrl | safeUrl" target="_blank">
<div class="social-wrapper facebook" data-network="facebook" draggable="false" style="display: inline-block;">
<svg fill="#fff" preserveAspectRatio="xMidYMid meet" height="1.2em" width="1.2em" viewBox="0 0 40 40">
<g>
<path d="m21.7 16.7h5v5h-5v11.6h-5v-11.6h-5v-5h5v-2.1c0-2 0.6-4.5 1.8-5.9 1.3-1.3 2.8-2 4.7-2h3.5v5h-3.5c-0.9 0-1.5 0.6-1.5 1.5v3.5z"></path>
</g>
</svg>
<span class="st-label">Share</span>
</div>
</a>
<a (click)="twitterShare()" [href]="twitterUrl | safeUrl" target="_blank">
<div class="social-wrapper twitter" data-network="facebook" draggable="false" style="display: inline-block;">
<svg fill="#fff" preserveAspectRatio="xMidYMid meet" height="1.2em" width="1.2em" viewBox="0 0 40 40">
<g>
<path d="m31.5 11.7c1.3-0.8 2.2-2 2.7-3.4-1.4 0.7-2.7 1.2-4 1.4-1.1-1.2-2.6-1.9-4.4-1.9-1.7 0-3.2 0.6-4.4 1.8-1.2 1.2-1.8 2.7-1.8 4.4 0 0.5 0.1 0.9 0.2 1.3-5.1-0.1-9.4-2.3-12.7-6.4-0.6 1-0.9 2.1-0.9 3.1 0 2.2 1 3.9 2.8 5.2-1.1-0.1-2-0.4-2.8-0.8 0 1.5 0.5 2.8 1.4 4 0.9 1.1 2.1 1.8 3.5 2.1-0.5 0.1-1 0.2-1.6 0.2-0.5 0-0.9 0-1.1-0.1 0.4 1.2 1.1 2.3 2.1 3 1.1 0.8 2.3 1.2 3.6 1.3-2.2 1.7-4.7 2.6-7.6 2.6-0.7 0-1.2 0-1.5-0.1 2.8 1.9 6 2.8 9.5 2.8 3.5 0 6.7-0.9 9.4-2.7 2.8-1.8 4.8-4.1 6.1-6.7 1.3-2.6 1.9-5.3 1.9-8.1v-0.8c1.3-0.9 2.3-2 3.1-3.2-1.1 0.5-2.3 0.8-3.5 1z"></path>
</g>
</svg>
<span class="st-label">Tweet</span>
</div>
</a>
<a (click)="copyLink(shareText)">
<div class="social-wrapper copy" data-network="facebook" draggable="false" style="display: inline-block;">
<svg fill="#fff" preserveAspectRatio="xMidYMid meet" height="1.2em" width="1.2em" viewBox="0 0 40 40">
<g>
<path d="m30 26.8c2.7 0 4.8 2.2 4.8 4.8s-2.1 5-4.8 5-4.8-2.3-4.8-5c0-0.3 0-0.7 0-1.1l-11.8-6.8c-0.9 0.8-2.1 1.3-3.4 1.3-2.7 0-5-2.3-5-5s2.3-5 5-5c1.3 0 2.5 0.5 3.4 1.3l11.8-6.8c-0.1-0.4-0.2-0.8-0.2-1.1 0-2.8 2.3-5 5-5s5 2.2 5 5-2.3 5-5 5c-1.3 0-2.5-0.6-3.4-1.4l-11.8 6.8c0.1 0.4 0.2 0.8 0.2 1.2s-0.1 0.8-0.2 1.2l11.9 6.8c0.9-0.7 2.1-1.2 3.3-1.2z"></path>
</g>
</svg>
<span class="st-label">Copy Link</span>
</div>
</a>
<mat-form-field class="width100">
<input matInput #shareText value="{{shareUrl}}">
</mat-form-field>
</div>
<div md-dialog-actions>
<button mat-raised-button (click)="close()">OK</button>
</div>

View File

@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { SharePopupComponent } from './share-popup.component';
describe('SharePopupComponent', () => {
let component: SharePopupComponent;
let fixture: ComponentFixture<SharePopupComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ SharePopupComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(SharePopupComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should be created', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,74 @@
import { GoogleAnalyticsService } from './../../../services/google-analytics.service';
import { MatDialogRef, MatSnackBar } from '@angular/material';
import { DOCUMENT } from '@angular/platform-browser';
import { MAT_DIALOG_DATA } from '@angular/material';
import { Component, OnInit, Inject } from '@angular/core';
@Component({
selector: 'app-share-popup',
templateUrl: './share-popup.component.html',
styleUrls: ['./share-popup.component.css']
})
export class SharePopupComponent implements OnInit {
//private urlPartA: string = "https://www.facebook.com/plugins/share_button.php?href=";
//private urlPartB: string = "&layout=button_count&size=large&mobile_iframe=true&width=106&height=28&appId";
private urlPartA: string = "https://www.facebook.com/sharer/sharer.php?kid_directed_site=0&u=";
private urlPartB: string = "&display=popup&ref=plugin&src=share_button";
private twitterPartA: string = "https://twitter.com/intent/tweet?text=";
private twitterPartB: string = "https://twitter.com/intent/tweet?text=ShareThis&url=http%3A%2F%2Fwww.sharethis.com%2F";
public twitterUrl: string;
private id: string;
public facebookIframeUrl: string;
private shareBaseUrl: string = "/api2/share/";
public shareUrl: string;
constructor(@Inject(MAT_DIALOG_DATA) public data: any,
@Inject(DOCUMENT) private document,
private MatDialogRef: MatDialogRef<SharePopupComponent>,
private snackbar: MatSnackBar,
private googleAnalyticsService: GoogleAnalyticsService) {
this.id = data.id;
let port = this.document.location.port ? ":"+this.document.location.port : "";
this.shareUrl = this.document.location.protocol +'//'+ this.document.location.hostname + port + this.shareBaseUrl + data.prefix + this.id;
this.facebookIframeUrl = this.urlPartA + this.shareUrl + this.urlPartB;
this.twitterUrl = this.twitterPartA + data.title + " - " + data.description + "&url=" + this.shareUrl;
}
ngOnInit() {
}
close(){
this.MatDialogRef.close();
}
copyLink(target){
target.select();
try {
var successful = document.execCommand('copy');
var msg = successful ? 'successful' : 'unsuccessful';
target.blur();
let s = this.snackbar.open("Link Copied","OK");
s.onAction().subscribe(()=>{ s.dismiss(); });
s.afterOpened().subscribe(()=>{ setTimeout(()=>{ s.dismiss(); },3000); });
this.share("link");
} catch (err) {
console.error('Oops, unable to copy');
}
}
share(method: string){
if (this.data.prefix === 's'){
this.googleAnalyticsService.shareSermon(Number(this.id),this.data.title,method);
} else if (this.data.prefix === 'e'){
this.googleAnalyticsService.shareEvent(Number(this.id),this.data.title,method);
}
}
facebookShare(){
this.share('facebook');
}
twitterShare(){
this.share('twitter');
}
}

View File

@ -0,0 +1,15 @@
mat-form-field{
display:block;
padding-top: 10px;
padding-bottom: 10px;
}
button{
margin: 20px 0px 20px 20px;
}
.first{
margin-left: 0px;
}

View File

@ -0,0 +1,23 @@
<div md-dialog-title>
Add Sermon
</div>
<div md-dialog-content>
<form #updateSermonForm="ngForm" (ngSubmit)="onSubmit()">
<mat-form-field class="example-full-width">
<input matInput required [(ngModel)]="sermonTitle" name="title" placeholder="Title">
</mat-form-field>
<mat-form-field>
<input matInput required [ngModel]="sermonDate | date:'yyyy-MM-ddTHH:mm'" (ngModelChange)="sermonDate = $event" name="date" type="datetime-local" placeholder="Choose a date">
</mat-form-field>
<mat-form-field class="example-full-width">
<input matInput required [(ngModel)]="sermonSpeaker" name="speaker" placeholder="Speaker">
</mat-form-field>
<mat-form-field class="example-full-width">
<input matInput required [(ngModel)]="sermonDescription" name="description" placeholder="Description">
</mat-form-field>
<button mat-raised-button class="first" type="button" (click)="cancel()" >Cancel</button><!--
--><button mat-raised-button type="submit" [disabled]="!updateSermonForm.form.valid || updateSermonButtonDisabled">{{ updateSermonButtonText }}</button>
</form>
</div>

View File

@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { UpdateSermonPopupComponent } from './update-sermon-popup.component';
describe('UpdateSermonPopupComponent', () => {
let component: UpdateSermonPopupComponent;
let fixture: ComponentFixture<UpdateSermonPopupComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ UpdateSermonPopupComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(UpdateSermonPopupComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should be created', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,6 @@
<div md-dialog-title>{{title}}</div>
<div md-dialog-content>{{message}}</div>
<div md-dialog-actions>
<button mat-raised-button (click)="yes()">YES</button>
<button mat-raised-button (click)="no()">NO</button>
</div>

View File

@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { YesNoPopupComponent } from './yes-no-popup.component';
describe('YesNoPopupComponent', () => {
let component: YesNoPopupComponent;
let fixture: ComponentFixture<YesNoPopupComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ YesNoPopupComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(YesNoPopupComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should be created', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,31 @@
import { MatDialogRef } from '@angular/material';
import { MAT_DIALOG_DATA } from '@angular/material';
import { Component, OnInit, Inject } from '@angular/core';
@Component({
selector: 'app-yes-no-popup',
templateUrl: './yes-no-popup.component.html',
styleUrls: ['./yes-no-popup.component.css']
})
export class YesNoPopupComponent implements OnInit {
public title: string;
public message: string;
constructor(@Inject(MAT_DIALOG_DATA) public data: any, private MatDialogRef: MatDialogRef<YesNoPopupComponent>) {
this.title = data.title;
this.message = data.message;
}
ngOnInit() {
}
yes(){
this.MatDialogRef.close(true);
}
no(){
this.MatDialogRef.close(false);
}
}

View File

@ -0,0 +1,16 @@
.sermon-header{
font-size: 1.2em;
font-weight: bold;
text-align: center;
border-bottom: 1px solid gray;
margin-bottom: 10px;
}
ul{
list-style: none;
}
li{
padding: 5px;
}

View File

@ -0,0 +1,14 @@
.section-header{
font-size: 1.2em;
font-weight: bold;
border-bottom: 1px solid gray;
margin-bottom: 10px;
}
a{
color: inherit;
}
a:visited{
color: inherit;
}

View File

@ -0,0 +1,67 @@
<secondary-page-component [hideSideBarOnMobile]="true">
<div mainContent>
<p ofbFadeInOnScroll class="section-header">Salvation</p>
<p ofbFadeInOnScroll class="section-paragraph">
Salvation put simply means being saved from sin, from Satan and from Hell. The truth is that
we are all sinners and we have fallen short. The punishment or wage for our sin is eternal death
and seperation from God. So, how does someone receive salvation?
</p>
<br><br>
<p ofbFadeInOnScroll class="section-header">God says we won't arrive</p>
<div ofbFadeInOnScroll class="section-paragraph">
<div class="verse">"For all have sinned and come short of the glory of God." <a href="https://www.kingjamesbibleonline.org/Romans-3-23/">Romans 3:23</a></div>
<br>
We are all sinners and <a href="https://www.kingjamesbibleonline.org/Romans-6-23/">Romans 6:23</a> tells us that the consequence of our sin is death.
<br>
<div class="verse">"For the wages of sin is death;..." <a href="https://www.kingjamesbibleonline.org/Romans-6-23/">Romans 6:23</a></div>
<br>
We have all sinned, we have all earned death and eternal seperation from God. The outlook
for us is not too good when we consider our position. God, knowing all about our helpless situation, has made
a way for every individual to avoid death, to be set free and receive the gift of eternal life.
The second half of <a href="https://www.kingjamesbibleonline.org/Romans-6-23/">Romans 6:23</a> says:
<div class="verse">"...but the gift of God is eternal life through Jesus Christ our Lord." <a href="https://www.kingjamesbibleonline.org/Romans-6-23/">Romans 6:23</a></div>
</div>
<br><br>
<p ofbFadeInOnScroll class="section-header">God says the road ends</p>
<div ofbFadeInOnScroll class="section-paragraph">
<div class="verse">"There is a way that seemeth right unto a man, but the end thereof are the ways of
death." <a href="https://www.kingjamesbibleonline.org/Proverbs-16-25/">Proverbs 16:25</a></div>
</div>
<br><br>
<b></b>
<p ofbFadeInOnScroll class="section-header">God says that we need to turn around</p>
<div ofbFadeInOnScroll class="section-paragraph">
<div class="verse">"But, except ye repent, ye shall all likewise perish." <a href="https://www.kingjamesbibleonline.org/Luke-13-3/">Luke 13:3</a></div>
</div>
<br><br>
<p ofbFadeInOnScroll class="section-header">God says there is only one way</p>
<div ofbFadeInOnScroll class="section-paragraph">
<div class="verse">"Jesus says, <span class="red">I am the way, the truth, and the life: no man cometh unto the Father,
but by me.</span>" <a href="https://www.kingjamesbibleonline.org/John-14-6/">John 11:6</a></div>
</div>
<br><br>
<p ofbFadeInOnScroll class="section-header">God says we need to change drivers</p>
<div ofbFadeInOnScroll class="section-paragraph">
<div class="verse">"But as many as received him to them gave he power to become the sons of God,
even to them that believe on His name." <a href="https://www.kingjamesbibleonline.org/John-1-12/">John 1:12</a></div>
<br><br>
Salvation is when we realize our own sin and because of that realization we repent of our wrong doing
and turn to Christ asking Him to save us and by faith receiving Jesus Christ as our personal living Saviour.
Each individual has the choice to reject or accept the gift of eternal life. What will your choice be?
If you understand that you are a sinner and the need to repent, you can do so right now. Pray to God, repent
of your sin and ask Him to come into your life.
<br><br>
If you would like more information or if you have put your faith in Jesus alone for salvation and would like further
guidance please click <a routerLink="/contact">here to contact us.</a>
</div>
</div>
<div sideBar class="side-bar">
</div>
</secondary-page-component>

View File

@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { SalvationPageComponent } from './salvation-page.component';
describe('SalvationPageComponent', () => {
let component: SalvationPageComponent;
let fixture: ComponentFixture<SalvationPageComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ SalvationPageComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(SalvationPageComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,15 @@
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-salvation-page',
templateUrl: './salvation-page.component.html',
styleUrls: ['./salvation-page.component.css']
})
export class SalvationPageComponent implements OnInit {
constructor() { }
ngOnInit() {
}
}

View File

@ -0,0 +1,39 @@
#content{
margin: auto;
width: 100%;
max-width: 1000px;
display: table;
}
.main-content{
width: 70%;
display: table-cell;
padding: 20px 10px 10px 10px;
}
.side-bar{
width: 30%;
display: table-cell;
height: 100%;
padding: 20px;
}
.align-top{
vertical-align: top;
}
@media(max-width:800px){
.side-bar{
display: block;
width: 100%;
}
.hide-on-mobile{
display: none;
}
.main-content{
display: block;
width: 100%;
}
}

View File

@ -0,0 +1,10 @@
<div id="content-wrapper">
<div id="content">
<div class="main-content align-top">
<ng-content select="[mainContent]"></ng-content>
</div><!--
--><div class="side-bar align-top" [class.hide-on-mobile]="hideSideBarOnMobile">
<ng-content select="[sideBar]"></ng-content>
</div>
</div>
</div>

View File

@ -0,0 +1,19 @@
import { Component, Input, OnInit } from '@angular/core';
@Component({
selector: 'secondary-page-component',
templateUrl: './secondary-page.component.html',
styleUrls: ['./secondary-page.component.css'],
})
export class SecondaryPageComponent implements OnInit {
@Input()
public hideSideBarOnMobile: boolean;
constructor(){
}
ngOnInit(){
}
}

View File

@ -0,0 +1,92 @@
.wrapper{
box-shadow: 0 3px 1px -2px rgba(0,0,0,.2), 0 2px 2px 0 rgba(0,0,0,.14), 0 1px 5px 0 rgba(0,0,0,.12);
}
.photo{
box-sizing: border-box;
display: inline-block;
max-width: 100px;
margin: 0;
vertical-align: top;
}
.fade{
text-align: center;
vertical-align: bottom;
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 20px;
padding-bottom: 2px;
/* Permalink - use to edit and share this gradient: http://colorzilla.com/gradient-editor/#ffffff+0,ffffff+100&0+0,0.8+100 */
background: -moz-linear-gradient(top, rgba(255,255,255,0) 0%, rgba(255,255,255,0.8) 100%); /* FF3.6-15 */
background: -webkit-linear-gradient(top, rgba(255,255,255,0) 0%,rgba(255,255,255,0.8) 100%); /* Chrome10-25,Safari5.1-6 */
background: linear-gradient(to bottom, rgba(255,255,255,0) 0%,rgba(255,255,255,0.8) 100%); /* W3C, IE10+, FF16+, Chrome26+, Opera12+, Safari7+ */
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#00ffffff', endColorstr='#ccffffff',GradientType=0 ); /* IE6-9 */
}
.photo button{
padding: 0;
vertical-align: top;
}
button img{
display: block;
height: 100%;
width: 100%;
}
.info{
display: inline-block;
vertical-align: top;
padding: 2px 2px 8px 5px;
box-sizing: border-box;
width: calc(100% - 100px);
overflow-y: hidden;
position: relative;
height: 100px;
}
.title{
font-weight: bold;
}
.date, .author{
font-style: italic;
font-size: 14px;
}
.description{
font-size: 14px;
color: darkgray;
word-wrap: none;
border-top: 0px solid lightgray;
padding-top:6px;
margin-top: 6px;
}
.expanded{
overflow-y: hidden;
}
.expanded-content{
margin: 10px;
}
.buttons, .buttons-edit{
background-color: lightgray;
}
.buttons button{
display: inline-block;
width: 33.33333%;
}
.border{
margin: 0px 5px 0px 5px;
border-top: 1px inset lightgray;
}
.buttons-edit button{
display: inline-block;
width: 50%;
}

View File

@ -0,0 +1,32 @@
<div class="wrapper" [@inout]="startFadeIn">
<div class="photo">
<button mat-button (click)="play()">
<img src="../../assets/images/tiny/facebookplay.png" />
</button>
</div><!--
--><div class="info" (click)="toggleState()">
<span class="title">{{ title }}</span>
<br>
<span class="date">{{ date |ofbDate:false }}</span> | <span class="author">{{ author }}</span>
<br>
<p class="description" [@toggleAnimationFade]="state">
{{ description }}
</p>
<div class="fade"><i ofbicon *ngIf="state === 'closed'">arrow_drop_down</i><i ofbicon *ngIf="state === 'open'">arrow_drop_up</i></div>
</div>
<div class="expanded" [@toggleAnimation]="state" (click)="toggleState()">
<p class="expanded-content">
{{ description }}
</p>
</div>
<div class="buttons">
<button mat-button class="action pct40" (click)="play()" ><i ofbicon>headset</i> Listen</button><!--
--><button mat-button class="action pct50" (click)="download()" ><i ofbicon>file_download</i> Download</button><!--
--><button mat-button class="action pct10" (click)="share()" ><i ofbicon>share</i> Share</button>
</div>
<div class="buttons-edit" *ngIf="loggedIn">
<div class="border"></div>
<button mat-button class="action" (click)="edit()" ><i ofbicon>edit</i> Edit</button><!--
--><button mat-button class="action" (click)="delete()" ><i ofbicon>delete_forever</i> Delete</button>
</div>
</div>

View File

@ -0,0 +1,35 @@
.inner-wrapper{
position:relative;
padding: 0px 0px 10px 0px;
padding: 10px;
box-shadow: 0 3px 1px -2px rgba(0,0,0,.2), 0 2px 2px 0 rgba(0,0,0,.14), 0 1px 5px 0 rgba(0,0,0,.12);
}
.title{
display: inline-block;
font-size: 1.15em;
font-weight: bold;
}
.action-buttons{
text-align: right;
}
.speaker-date{
font-style: italic;
color: darkslategray;
float: right;
}
.description{
margin: 15px 0px 15px 0px;
}
@media(max-width:700px){
.speaker-date{
float: none;
}
}

View File

@ -0,0 +1,21 @@
<div class="wrapper" [@inout]="startFadeIn">
<div class="inner-wrapper">
<p class="title">{{ title }}</p>
<div class="speaker-date">
<p>{{ author }} | {{ date |date:'yyyy-MM-dd' }}</p>
</div>
<div class="info">
<div class="description">{{ description }}</div><!--
--><div class="action-buttons">
<button mat-button class="action pct40" (click)="play()" ><i ofbicon>headset</i> Listen</button><!--
--><button mat-button class="action pct50" (click)="download()" ><i ofbicon>file_download</i> Download</button><!--
--><button mat-button class="action pct10" (click)="share()" ><i ofbicon>share</i> Share</button>
</div><!--
--><div class="action-buttons" *ngIf="loggedIn">
<button mat-button class="action" (click)="edit()" ><i ofbicon>edit</i> Edit</button><!--
--><button mat-button class="action" (click)="delete()" ><i ofbicon>delete_forever</i> Delete</button>
</div>
</div>
</div>
</div>

View File

@ -0,0 +1,25 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { SermonLargeComponent } from './sermon-large.component';
describe('SermonLargeComponent', () => {
let component: SermonLargeComponent;
let fixture: ComponentFixture<SermonLargeComponent>;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ SermonLargeComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(SermonLargeComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -0,0 +1,126 @@
import { SharePopupComponent } from './../popups/share-popup/share-popup.component';
import { Sermon } from './../../interfaces/sermon';
import { UpdateSermonPopupComponent } from './../popups/update-sermon-popup/update-sermon-popup.component';
import { SermonService } from './../../services/sermon.service';
import { YesNoPopupComponent } from './../popups/yes-no-popup/yes-no-popup.component';
import { MatDialog, MatDialogConfig, MatSnackBar } from '@angular/material';
import { Component, OnInit, Input, AfterContentInit } from '@angular/core';
import { trigger, state, style, transition, animate } from '@angular/animations';
import { SermonSmallComponent } from '../sermon-small/sermon-small.component';
import { AudioPlayerService } from '../../services/audio-player.service';
import { SERMON_DOWNLOAD_URL } from '../../constants/urls';
@Component({
selector: 'sermon-large-component',
templateUrl: './sermon-large-pic.component.html',
styleUrls: ['./sermon-large-pic.component.css'],
animations:[
trigger("inout",[
state("1", style({ opacity: '1' })),
state("0",style({ opacity: '0' })),
transition('* => 1',[
animate('500ms ease-in-out')
]),
transition(':enter',[
animate('0ms')
])
]),
trigger('toggleAnimation', [
state('open', style({
height: '*',
})),
state('closed', style({
height: '0px',
})),
transition('open <=> closed', animate('500ms ease-in-out'))
]),
trigger('toggleAnimationFade', [
state('open', style({
opacity: 0,
})),
state('closed', style({
opacity: 1,
})),
transition('open <=> closed', animate('500ms ease-in-out'))
])
]
})
export class SermonLargeComponent extends SermonSmallComponent implements OnInit, AfterContentInit {
public state: string = 'closed';
@Input()
public loggedIn: boolean;
constructor(private aplayer: AudioPlayerService
,private dialog: MatDialog
,private sermonService: SermonService
,private snackbar: MatSnackBar) {
super(aplayer);
}
ngOnInit() {
}
toggleState(){
this.state = this.state === 'open' ? 'closed' : 'open'
}
download(){
window.location.href = SERMON_DOWNLOAD_URL + this.id;
}
share(){
let opts = new MatDialogConfig;
opts.data = { prefix: 's', id: this.id, title: this.title, description: this.description };
let dialog = this.dialog.open(SharePopupComponent, opts);
}
edit(){
let config = new MatDialogConfig();
config.data = {
id: this.id,
title: this.title,
description: this.description,
date: this.date,
author: this.author
};
let dialog = this.dialog.open(UpdateSermonPopupComponent,config);
dialog.afterClosed().subscribe(s => {
s = s as Sermon;
if (s){
this.title = s.title;
this.description = s.description;
this.author = s.author;
this.date = s.date;
let snack = this.snackbar.open(s.title + " was updated!", "Ok");
snack.onAction().subscribe(()=>{
snack.dismiss();
});
setTimeout(()=>{snack.dismiss();},3000);
}
});
}
delete(){
let opts = new MatDialogConfig;
opts.data = { title:'Delete','message':'Are you sure you want to delete "' + this.title + '"?' };
let dialog = this.dialog.open(YesNoPopupComponent, opts);
dialog.afterClosed().subscribe(s => {
s = s as boolean;
if (s === true){
this.sermonService.deleteSermon(this.id).subscribe(
success => { },
error => { console.error(error); }
);
}
});
}
}

View File

@ -0,0 +1,31 @@
.inner-wrapper{
position:relative;
}
.title{
display: table;
font-size: 1.25em;
}
.title-text{
vertical-align: middle;
display: table-cell;
padding-left: 10px;
font-size: .95rem;
}
button{
position: absolute;
width: 100%;
height: 100%;
}
ofb-button{
position: absolute;
right: 0;
bottom: 0;
left: 0;
top: 0;
}

View File

@ -0,0 +1,9 @@
<div class="wrapper" [@inout]="startFadeIn">
<div class="inner-wrapper">
<div class="title">
<button mat-button (click)="play()"></button>
<date-component class="title-date" [date]="date"></date-component>
<span class="title-text">{{ title }}</span>
</div>
</div>
</div>

View File

@ -0,0 +1,49 @@
ul{
list-style: none;
}
li{
margin: 40px 0px 40px 0px; /* top right bottom left */
}
li:first-child{
margin-top: 0px;
}
li:last-child{
margin-bottom: 0px;
}
.side-bar{
position: fixed;
}
.width100{
width: 100%;
}
.mobile-search{
display: none;
}
.fab-buttons{
display: none;
}
#audio-player-filler{
height: 60px;
background-color: inherit;
}
@media(max-width:800px){
.mobile-search{
display: block;
}
.fab-buttons{
display: inline-block;
position: fixed;
bottom: 20px;
right: 20px;
}
}

View File

@ -0,0 +1,28 @@
.verse{
text-align: center;
font-style: italic;
}
.reference{
font-weight: bold;
}
.side-bar-header{
text-align: center;
background-color: gray;
}
.section-header{
font-size: 1.2em;
font-weight: bold;
border-bottom: 1px solid gray;
margin-bottom: 10px;
}
img{
border-radius: 5px;
width: 100%;
-webkit-box-shadow: 3px 3px 5px 0px rgba(50, 50, 50, 0.75);
-moz-box-shadow: 3px 3px 5px 0px rgba(50, 50, 50, 0.75);
box-shadow: 3px 3px 5px 0px rgba(50, 50, 50, 0.75);
}

View File

@ -0,0 +1,49 @@
<secondary-page-component [hideSideBarOnMobile]="true">
<div mainContent>
<div>
<p ofbFadeInOnScroll class="section-header">Service Times</p>
<p ofbFadeInOnScroll class="section-paragraph">
Join Us every Sunday and Wednesday for challenging messages from the Bible and fellowship with others!
Bring the whole family!
</p>
<br>
<div ofbFadeInOnScroll class="imageBorder">
<img class="image" src="assets/images/tiny/sign.png" alt="Church Image" />
</div>
<br><br>
<p ofbFadeInOnScroll class="section-header">Sunday School 10 AM</p>
<p ofbFadeInOnScroll class="section-paragraph">
During the Sunday morning service, kids are allowed to separate from the adults to attend a class that is appropriate for thier age group. We have a nursery and Sunday School classes for all age groups.
</p>
<br><br>
<p ofbFadeInOnScroll class="section-header">Sunday Morning 11 AM</p>
<p ofbFadeInOnScroll class="section-paragraph">
Sunday Morning church service immediately follows Sunday School service with a short break in between. Nursery and childrens church is available during the Sunday morning hour.
</p>
<br><br>
<p ofbFadeInOnScroll class="section-header">Sunday Evening 7 PM</p>
<p ofbFadeInOnScroll class="section-paragraph">
Sunday night is the last opportunity to hear a great message before the work week starts. We encourage you to come fellowship with us each sunday night at 7 P.M. Nursery services are available.
</p>
<br><br>
<p ofbFadeInOnScroll class="section-header">Wednesday Evening 7 PM</p>
<p ofbFadeInOnScroll class="section-paragraph">
Join us on Wednesday night for a Bible study that will challenge you in your walk with the Lord. Wednesday evening is a great time to come and hear a great message from the Bible to stay focused during the week. Nursery and childrens church are also available.
</p>
<br><br>
<p ofbFadeInOnScroll class="section-header"></p>
<div ofbFadeInOnScroll class="section-paragraph">
<p class="verse">Not forsaking the assembling of ourselves together, as the manner of some is; but exhorting one another: and so much the more, as ye see the day approaching.</p>
<p class="verse reference">Hebrews 10:25</p>
</div>
</div>
</div>
<div sideBar class="side-bar">
<upcoming-events-component [numberOfEventsToShow]="4" ></upcoming-events-component>
</div>
</secondary-page-component>

View File

@ -0,0 +1,34 @@
import { WindowRefService } from './../../services/window-ref.service';
import { Component, HostListener, OnInit } from '@angular/core';
@Component({
selector: 'services-component',
templateUrl: './services.component.html',
styleUrls: ['./services.component.css'],
})
export class ServicesComponent implements OnInit {
public columnCount: number = 2;
private smallColumnCount: number = 1;
private medColumnCount: number = 2;
private smallWidth: number = 500;
constructor(private windowRef: WindowRefService){ }
ngOnInit(){
let event = { target:this.windowRef.getWindow() };
this.onResize(event);
}
@HostListener('window:resize', ['$event'])
onResize(event) {
if (event.target.innerWidth < this.smallWidth){
this.columnCount = this.smallColumnCount;
} else {
this.columnCount = this.medColumnCount;
}
}
}

View File

@ -0,0 +1,15 @@
.event-header{
font-size: 1.2em;
font-weight: bold;
text-align: center;
border-bottom: 1px solid gray;
margin-bottom: 10px;
}
ul{
list-style: none;
}
li{
padding: 5px;
}

View File

@ -0,0 +1,13 @@
<div class="event-container" *ngIf="events.length > 0 || !hideWhenNoEvents">
<p class="event-header" >Upcoming Events</p>
<p *ngIf="loading">Loading...</p>
<ul class="events">
<li *ngFor="let event of events; let i = index;">
<event-component
[title]="event.title"
[startDate]="event.startDate"
[delayFadeIn]="(i+1)*200"
></event-component>
</li>
</ul>
</div>

View File

@ -0,0 +1,72 @@
.main-content-header{
font-weight: bold;
font-size: 1.2em;
}
a{
color: inherit;
}
a:visited{
color: inherit;
}
.action{
display: block;
font-weight: bold;
font-size: 20px;
margin: 20px;
}
.align-top{
vertical-align: top;
}
.side-bar-header{
text-align: center;
background-color: gray;
}
.section-header{
font-size: 1.2em;
font-weight: bold;
border-bottom: 1px solid gray;
margin-bottom: 10px;
}
.shadow{
border-radius: 5px;
width: 100%;
-webkit-box-shadow: 3px 3px 5px 0px rgba(50, 50, 50, 0.75);
-moz-box-shadow: 3px 3px 5px 0px rgba(50, 50, 50, 0.75);
box-shadow: 3px 3px 5px 0px rgba(50, 50, 50, 0.75);
}
.container-3-col{
width: 100%;
}
.container-3-col > button{
width: calc(33% - 12px);
background-color: lightgray;
color: black;
}
.container-3-col > button ~ button{
margin-left: 18px;
}
@media(max-width: 600px){
.container-3-col{
text-align: center;
}
.container-3-col > button{
width: 90%;
margin: 10px;
}
.container-3-col > button ~ button{
margin-left: 10px;
}
}

View File

@ -0,0 +1,78 @@
<secondary-page-component [hideSideBarOnMobile]="true" >
<div mainContent>
<div>
<p ofbFadeInOnScroll class="section-header">Who We Are</p>
<div ofbFadeInOnScroll class="section-paragraph">
Old Fashion Baptist Church is a small independent baptist church located in Butte, Montana. We meet regularly throughout the week
to spread the word of God as well as encourage and challenge believers in their Christian life.
<br><br>
<div class="container-3-col">
<button mat-raised-button routerLink="/services"><i ofbicon>access_time</i> Service Schedule</button><!--
--><button mat-raised-button routerLink="/location"><i ofbicon>directions</i> Directions</button><!--
--><button mat-raised-button routerLink="/contact"><i ofbicon>mail_outline</i> Contact Us</button>
</div>
</div>
<br><br>
<div ofbFadeInOnScroll class="imageBorder">
<img class="image shadow" src="assets/images/home-images/tiny/church.jpg" alt="Church Image" />
</div>
<br><br>
<p ofbFadeInOnScroll class="section-header">What to expect</p>
<p ofbFadeInOnScroll class="section-paragraph">
Sometimes it can be difficult to attend if you are not familiar with church and don't know what to expect.
One thing you can always expect is to be welcomed at Old Fashion Baptist. We count it a privalege to have
visitors attend our church services so the first thing you will find is a friendly church family.
<br><br>
Each service begins with a few songs along with prayer. Prayer and praise are a great way to help prepare
our hearts for the message that follows. You can expect the message to last aproximately 45 - 60
minutes and will be straight from the Bible. We want you to know that the message is from the Bible and will
turn to verses throughout the service. You are welcome to just listen or follow along in your Bible.
</p>
<br><br>
<p ofbFadeInOnScroll class="section-header">Nursery</p>
<p ofbFadeInOnScroll class="section-paragraph">
Nursery workers are available for every service. Our nursery is available so that you can attend the church service
without interruption.
</p>
<br><br>
<p ofbFadeInOnScroll class="section-header">Kids</p>
<p ofbFadeInOnScroll class="section-paragraph">
We also offer Sunday School classes for all age groups of kids. This is a great opportunity for
children to recieve a message that is appropriate for their age.
</p>
<br><br>
<p ofbFadeInOnScroll class="section-header">Jesus</p>
<p ofbFadeInOnScroll class="section-paragraph">
Jesus, God who became flesh and dwelt among us by being born of the virgin Mary is our only hope for eternal life.
Because of the sin and wickedness of mankind a wage was earned - that wage is death. Out of His great love
for us God sent his only begotten Son who lived on this earth as a man who "was in all points tempted like as we are, yet without sin" <a href="https://www.kingjamesbibleonline.org/Hebrews-4-15/">Hebrews 4:15</a>.
<br><br>
He was sinless - He layed down His life and became the sacrifice for all! <a routerLink="/salvation">Have you accepted Him?</a>
</p>
<br><br>
<p ofbFadeInOnScroll class="section-header">The Bible</p>
<p ofbFadeInOnScroll class="section-paragraph">
In the beginning was the Word, and the Word was with God, and the Word was God. <a href="https://www.kingjamesbibleonline.org/John-1-1/">John 1:1</a>
<br><br>
All scripture is given by inspiration of God, and is profitable for doctrine, for reproof, for correction, for instruction in righteousness:
That the man of God may be perfect, thoroughly furnished unto all good works. <a href="https://www.kingjamesbibleonline.org/2-Timothy-3-16_3-17/">2 Timothy 3:16 - 17</a>
<br><br>
But he answered and said, <span class="red">It is written, Man shall not live by bread alone, but by every word that proceedeth out of the mouth of God.</span> <a href="https://www.kingjamesbibleonline.org/Matthew-4-4/">Matthew 4:4</a>
<br><br>
The words of the LORD are pure words: as silver tried in a furnace of earth, purified seven times.
Thou shalt keep them, O LORD, thou shalt preserve them from this generation for ever. <a href="https://www.kingjamesbibleonline.org/Psalms-12-6_12-7/">Psalm 12:6 - 7</a>
<br><br>
<span class="red">Heaven and earth shall pass away, but my words shall not pass away.</span> <a href="https://www.kingjamesbibleonline.org/Matthew-24-35/">Matthew 24:35</a>
</p>
</div>
</div>
<div sideBar ofbFadeInOnScroll>
<recent-sermons-component [numberOfSermonsToShow]="4" ></recent-sermons-component>
</div>
</secondary-page-component>

View File

@ -0,0 +1,10 @@
import { Component } from '@angular/core';
@Component({
selector: 'whoweare-component',
templateUrl: './whoweare.component.html',
styleUrls: ['./whoweare.component.css'],
})
export class WhoWeAreComponent {
}

View File

@ -0,0 +1,29 @@
export const MONTHS = {
0: "JAN",
1: "FEB",
2: "MAR",
3: "APR",
4: "MAY",
5: "JUN",
6: "JUL",
7: "AUG",
8: "SEP",
9: "OCT",
10: "NOV",
11: "DEC"
};
export const MONTHS_FULL = {
0: "JANUARY",
1: "FEBRUARY",
2: "MARCH",
3: "APRIL",
4: "MAY",
5: "JUNE",
6: "JULY",
7: "AUGUST",
8: "SEPTEMBER",
9: "OCTOBER",
10: "NOVEMBER",
11: "DECEMBER"
};

View File

@ -0,0 +1,44 @@
import { Directive,
ElementRef,
Input,
HostListener,
HostBinding,
OnInit } from '@angular/core';
@Directive({
selector: '[ofbFadeInOnScroll]',
host:{
//'[class.animation-fade-in]':'true',
'[style.transition]':'"all .5s ease-in-out"'
}
})
export class FadeInOnScrollDirective implements OnInit {
@HostBinding('style.opacity')
opacity: number = 0;
constructor(private el: ElementRef) {
}
ngOnInit(): void{
this.checkScroll();
}
checkScroll(): void{
if (this.opacity === 1) return;
let viewPort = this.el.nativeElement.getBoundingClientRect();
let winHeight = window.innerHeight;
let dif = winHeight - viewPort.top;
let maxMargin = 100;
if (dif >= 50 && this.opacity != 1){
this.opacity = 1;
}
}
@HostListener('window:scroll', ['$event'])
onScroll(event){
this.checkScroll();
}
}

View File

@ -0,0 +1,27 @@
export const ICONS = {
access_time: '&#xE192;',
add: '&#xE145;',
close: '&#xE5CD;',
copyright: '&#xE90C;',
delete_forever: '&#xE92B;',
arrow_drop_down: '&#xE5C5;',
arrow_drop_up: '&#xE5C7;',
directions: '&#xE52E;',
edit: '&#xE3C9;',
event: '&#xE878;',
expand_more: '&#xE5CF;',
file_download: '&#xE2C4;',
headset: '&#xE310;',
help_outline: '&#xE8FD;',
info_outline: '&#xE88F;',
input: '&#xE890;',
mail_outline: '&#xE0E1;',
menu: '&#xE5D2;',
pause: '&#xE034;',
phone: '&#xE0CD;',
place: '&#xE55F;',
play_arrow: '&#xE037;',
search: '&#xE8B6;',
share: '&#xE80D;',
slow_motion_video: '&#xE068;',
};

View File

@ -0,0 +1,46 @@
import { Directive,
ElementRef,
OnInit,
Input,
OnChanges,
SimpleChanges } from '@angular/core';
import { ICONS } from './icon-code-map';
@Directive({
selector: '[ofbicon]',
host:{
'[class.material-icon]':'true'
}
})
export class IconDirective implements OnInit, OnChanges {
@Input()
iconName: string;
iconCode: string;
constructor(private el: ElementRef) {
}
ngOnInit(): void{
if (this.el.nativeElement.innerText != ''){
this.iconName = this.el.nativeElement.innerText;
this.setIcon();
}
}
ngOnChanges(changes: SimpleChanges): void{
this.setIcon();
}
setIcon(){
let iconCode = ICONS[this.iconName];
if (typeof iconCode !== 'undefined'){
this.iconCode = iconCode;
this.el.nativeElement.innerHTML = this.iconCode;
}
}
}

View File

@ -0,0 +1,6 @@
export enum AudioStates{
Loading,
Paused,
Playing,
Error
}

View File

@ -0,0 +1,27 @@
import { Pipe, PipeTransform } from '@angular/core';
/*
*
*/
@Pipe({name: 'duration'})
export class DurationPipe implements PipeTransform {
transform(value: number, duration: number): string {
if (duration != null && typeof duration === 'number'){
value = duration - value;
}
if (value === NaN || typeof value !== 'number')
return "00:00";
value = Math.ceil(value);
let minutes = Math.floor(value / 60);
let seconds = value - (minutes * 60);
let hours = Math.floor(minutes / 60);
minutes = minutes - (hours * 60);
let min = minutes < 10 ? "0" + minutes : minutes;
let sec = seconds < 10 ? "0" + seconds : seconds;
let hou = hours < 10 ? "0" + hours : hours;
let str = (hou == "00" ? "" : hou + ":") + min + ":" + sec;
return str;
}
}

View File

@ -0,0 +1,8 @@
import { OfbDatePipe } from './ofb-date.pipe';
describe('OfbDatePipe', () => {
it('create an instance', () => {
const pipe = new OfbDatePipe();
expect(pipe).toBeTruthy();
});
});

View File

@ -0,0 +1,31 @@
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
name: 'ofbDate'
})
export class OfbDatePipe implements PipeTransform {
transform(value: Date, time?: boolean): any {
if (typeof value === 'undefined' || value == null){
console.error("value for ofb-date-pipe was undefined or null");
return value;
}
var year = value.getFullYear();
var month = value.getMonth() + 1;
var day = value.getDate();
var m = month < 10 ? "0" + month : month;
var d = day < 10 ? "0" + day : day;
var datePart = year + "-" + m + "-" + d;
if (time){
var hour = value.getHours();
var minutes = value.getMinutes();
var m = minutes < 10 ? "0" + minutes : minutes;
var amPm = hour < 12 ? " AM" : " PM";
hour = hour > 12 ? hour - 12 : hour;
hour = hour == 0 ? 12 : hour;
return datePart + " | " + hour + ":" + m + amPm;
}
return datePart;
}
}

View File

@ -0,0 +1,8 @@
import { SafeUrlPipe } from './safe-url.pipe';
describe('SafeUrlPipe', () => {
it('create an instance', () => {
const pipe = new SafeUrlPipe();
expect(pipe).toBeTruthy();
});
});

View File

@ -0,0 +1,10 @@
import { Pipe, PipeTransform } from '@angular/core';
import { DomSanitizer} from '@angular/platform-browser';
@Pipe({ name: 'safeUrl' })
export class SafeUrlPipe implements PipeTransform {
constructor(private sanitizer: DomSanitizer) {}
transform(url) {
return this.sanitizer.bypassSecurityTrustResourceUrl(url);
}
}

View File

@ -0,0 +1,15 @@
import { TestBed, inject } from '@angular/core/testing';
import { GoogleAnalyticsService } from './google-analytics.service';
describe('GoogleAnalyticsService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
providers: [GoogleAnalyticsService]
});
});
it('should be created', inject([GoogleAnalyticsService], (service: GoogleAnalyticsService) => {
expect(service).toBeTruthy();
}));
});

View File

@ -0,0 +1,106 @@
declare var gtag;
import { Injectable } from '@angular/core';
@Injectable()
export class GoogleAnalyticsService {
private trackingId: string = 'UA-107601801-1';
private baseUrl: string = 'https://ofbbutte.com';
private lastSermonId: number = 0;
private lastSermonStartPos: number = 0;
constructor() { }
public pageView(title: string, path: string){
gtag('config', this.trackingId, {
'page_title': title,
'page_location': this.baseUrl + path,
'page_path': path
});
}
public searchSermon(searchTerm: string){
gtag('event', 'search', {
'search_term': searchTerm
});
}
public shareSermon(id: number, title: string, method: string){
gtag('event', 'share', {
'method': method,
'content_type': 'audio',
'content_id': id,
'content_title': title
});
}
public shareEvent(id: number, title: string, method: string){
gtag('event', 'share', {
'method': method,
'content_type': 'event',
'content_id': id,
'content_title': title
});
}
public shareSermonConversion(id: number, title: string){
gtag('event', 'share_conversion', {
'content_type': 'audio',
'content_id': id,
'content_title': title
});
}
public shareEventConversion(id: number, title: string){
gtag('event', 'share_conversion', {
'content_type': 'event',
'content_id': id,
'content_title': title
});
}
public loadingSermon(id: number, title: string){
gtag('event', 'audio_loading', {
'audio_id': id,
'audio_title': title
});
}
public playSermon(id: number, title: string, position: number){
this.lastSermonId = id;
this.lastSermonStartPos = position;
gtag('event', 'audio_play', {
'audio_id': id,
'audio_title': title,
'audio_start_position': position
});
}
public pauseSermon(id: number, title: string, position: number){
let duration = null;
let lastStartPos = null;
if (id == this.lastSermonId){
duration = position - this.lastSermonStartPos;
lastStartPos = this.lastSermonStartPos;
} else {
}
gtag('event', 'audio_pause', {
'audio_id': id,
'audio_title': title,
'audio_start_position': lastStartPos,
'audio_pause_position': position,
'audio_listen_duration': duration
});
}
public audioError(id: number, title: string, error: string){
gtag('event', 'audio_error', {
'audio_id': id,
'audio_title': title,
'audio_error_message': error
});
}
}

View File

@ -0,0 +1,12 @@
import { Injectable } from '@angular/core';
@Injectable()
export class WindowRefService {
constructor() { }
public getWindow(): any{
return window;
}
}

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

Some files were not shown because too many files have changed in this diff Show More