Full Code of paulsutherland/Polyonic for AI

master bfe318d726d2 cached
68 files
67.7 KB
19.8k tokens
34 symbols
1 requests
Download .txt
Repository: paulsutherland/Polyonic
Branch: master
Commit: bfe318d726d2
Files: 68
Total size: 67.7 KB

Directory structure:
gitextract_wfs5lkbz/

├── .editorconfig
├── .gitignore
├── README.md
├── angular.json
├── config.xml
├── e2e/
│   ├── protractor.conf.js
│   ├── src/
│   │   ├── app.e2e-spec.ts
│   │   └── app.po.ts
│   └── tsconfig.e2e.json
├── electron-builder.json
├── electron.js
├── ionic.config.json
├── package.json
├── resources/
│   ├── android/
│   │   └── xml/
│   │       └── network_security_config.xml
│   ├── electron/
│   │   ├── .gitkeep
│   │   ├── linux/
│   │   │   ├── DEBIAN/
│   │   │   │   └── control
│   │   │   └── app.desktop
│   │   ├── osx/
│   │   │   ├── dmg-icon.icns
│   │   │   └── icon.icns
│   │   ├── polyonic.psd
│   │   └── windows/
│   │       └── installer.nsi
│   ├── icon.png.md5
│   └── splash.png.md5
├── src/
│   ├── app/
│   │   ├── app-routing.module.ts
│   │   ├── app.component.html
│   │   ├── app.component.spec.ts
│   │   ├── app.component.ts
│   │   ├── app.module.ts
│   │   ├── data.service.spec.ts
│   │   ├── data.service.ts
│   │   ├── electron.service.spec.ts
│   │   ├── electron.service.ts
│   │   ├── events.service.spec.ts
│   │   ├── events.service.ts
│   │   ├── tab1/
│   │   │   ├── tab1.module.ts
│   │   │   ├── tab1.page.html
│   │   │   ├── tab1.page.scss
│   │   │   ├── tab1.page.spec.ts
│   │   │   └── tab1.page.ts
│   │   ├── tab2/
│   │   │   ├── tab2.module.ts
│   │   │   ├── tab2.page.html
│   │   │   ├── tab2.page.scss
│   │   │   ├── tab2.page.spec.ts
│   │   │   └── tab2.page.ts
│   │   ├── tab3/
│   │   │   ├── tab3.module.ts
│   │   │   ├── tab3.page.html
│   │   │   ├── tab3.page.scss
│   │   │   ├── tab3.page.spec.ts
│   │   │   └── tab3.page.ts
│   │   └── tabs/
│   │       ├── tabs.module.ts
│   │       ├── tabs.page.html
│   │       ├── tabs.page.scss
│   │       ├── tabs.page.spec.ts
│   │       ├── tabs.page.ts
│   │       └── tabs.router.module.ts
│   ├── environments/
│   │   ├── environment.prod.ts
│   │   └── environment.ts
│   ├── global.scss
│   ├── index.html
│   ├── karma.conf.js
│   ├── main.ts
│   ├── polyfills.ts
│   ├── test.ts
│   ├── theme/
│   │   └── variables.scss
│   ├── tsconfig.app.json
│   └── tsconfig.spec.json
├── tsconfig.json
└── tslint.json

================================================
FILE CONTENTS
================================================

================================================
FILE: .editorconfig
================================================
# editorconfig.org
root = true

[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true

[*.json]
indent_size = 2

[*.md]
trim_trailing_whitespace = false


================================================
FILE: .gitignore
================================================
node_modules
*.log
.DS_Store
Thumbs.db
*.swp
*.autogenerated
/build/*
!/build/.gitkeep
/releases/
/tmp/
/src/node_modules
/src/platforms
/src/plugins
/src/www/build/
/src/.tmp/
.vscode
/dist/*
.sourcemaps/
plugins/*
www/
release/
platforms/
app.db/


================================================
FILE: README.md
================================================
# Polyonic
## The ultimate "Universal Web App" 

[![JavaScript Style Guide](https://img.shields.io/badge/code%20style-standard-brightgreen.svg)](http://standardjs.com/)
[![devDependencies Status](https://david-dm.org/paulsutherland/Polyonic/dev-status.svg)](https://david-dm.org/paulsutherland/Polyonic?type=dev)
[![optionalDependencies Status](https://david-dm.org/paulsutherland/Polyonic/optional-status.svg)](https://david-dm.org/paulsutherland/Polyonic?type=optional)
[![MIT Licence](https://badges.frapsoft.com/os/mit/mit.svg?v=103)](https://opensource.org/licenses/mit-license.php)
[![Open Source Love](https://badges.frapsoft.com/os/v1/open-source.svg?v=103)](https://github.com/ellerbrock/open-source-badge/)

> Now using Ionic 5, Electron 11 and Angular 11 :tada:. See here for [Ionic 1](https://github.com/paulsutherland/Polyonic/tree/Ionic1) and [Ionic 3](https://github.com/paulsutherland/Polyonic/tree/Ionic3).

## Build once using web technologies and deploy everywhere!

Polyonic is an Electron Ionic application shell for creating Web Apps, Progressive Mobile Web Apps, Native Mobile Apps and Desktop Apps.

This project combines the [Electron Framework](http://electron.atom.io/) with the [Ionic Framework](https://ionicframework.com/) and provides a starter for building out an app that can run on either the desktop (macOS, Windows and Linux), a browser or mobile devices (iOS and Android).  You can use this application to build and run on one or all of these platforms.

## Motivation

When working in small teams creating web and mobile applications, it is difficult and time consuming for new team members to pick up the different technologies for each platform. I have also been asked a few questions that motivated me to try Polyonic out:

- Is there a way to run this mobile app on the desktop?
- Can we cache more in the browser for offline working?
- Can we have one Universal app that can run on the desktop, mobile, web and Office 365?

These questions made me think about the limitations of the browser for off-line editing and caching and was there a way to create a package that can be used to flesh out any app we require, whilst reducing the overhead of having to learn numerous libraries and frameworks for each platform.

And that is why I decided to try combining Electron and Ionic. Is it wise to have a universal app using a mobile framework? Probably not, but it is fun :wink:

## Quick start

The dependencies for this project are [Node.js](https://nodejs.org), [Ionic Framework](https://ionicframework.com/) and [Cordova](https://cordova.apache.org/).

You will need the latest Node 14 LTS and NPM 7 installed.

Make sure you have [node installed and running](https://nodejs.org/en/download/), then install Ionic and Cordova globally using npm.

```node
npm install -g ionic@latest cordova@latest
```

Clone the repo, change into the Polyonic directory, install the npm packages and run the Electron app

```node
git clone --depth 1 https://github.com/paulsutherland/Polyonic
cd Polyonic

npm install
npm run electron:dev
```

You now have Electron and Ionic running as a Desktop app.

## Running live reload for development

When developing, you will want to have the app live reload as you save your changes.

### Desktop

```node
npm run electron:dev
```

For debugging the main process you will need to have the Chrome Browser installed.

```node
npm run electron:dev:debug
```

Open Chrome and navigate to chrome://inspect/ and select the Electron remote target that is available to attach the debugger to.

If you require live reloading of the main process debugging session, then it is recommended that you install the Chrome plugin [Node.js V8 --inspector Manager (NiM)](https://chrome.google.com/webstore/detail/nodejs-v8-inspector-manag/gnhhdgbaldcilmgcpfddgdbkhjohddkj?hl=en). In the plugin settings, set the host to localhost, the port to 9229 and the app to auto.  This will allow you to live reload changes made to the main process (electron.js file).

```node
npm run electron:dev:debug-live
```

### iOS

#### Emulator

```node
npm run emulate:ios-dev
```

#### Device

```node
npm run device:ios-dev
```

### Android

#### Emulator

```node
npm run emulate:android-dev
```

#### Device

```node
npm run device:android-dev
```

## Building on Windows

For building on Windows you will need to install the Nullsoft Scriptable Install System.

You can [download NSIS here](http://nsis.sourceforge.net/Main_Page)

You will need to make sure the NSIS path is added as an environment variable:

```node
setx PATH "%PATH%;C:\Program Files (x86)\NSIS"
```

Or using [point and click](http://nsis.sourceforge.net/Main_Page).

## Porting existing Ionic Apps

It is possible to port your existing apps to run on the desktop, but you may need to make some platform adjustments to call out to an equivalent api for any mobile plugins your app uses.  The app includes and angular service for electron which makes is easy to call the Electron APIs from within the Ionic components.

For example you may want to check what platform you are running on before you make an api call, either calling out to an Ionic plugin, an Electron api or a browser api.  

The data service component ```data.service.ts``` has an example of setting up a PouchDB database depending upon what platform the app is running on.  

```javascript
import { ElectronService } from './electron.service'
import { Platform } from '@ionic/angular'

...

constructor(public electron: ElectronService, private platform: Platform) {}

...

public setup() {
    const ctx = this
    console.log('Setting up the application database')

    if (ctx.electron.isElectronApp) {
      return ctx.desktopDB()
    }
    if (ctx.platform.is('mobile')) {
      return ctx.mobileDB()
    } else {
      return ctx.webDB()
    }
}

```

## Encryption at rest

If your app requires your data to be encrypted at rest, the app includes an example of using the [cordova-sqlcipher-adapter](https://github.com/brodybits/cordova-sqlcipher-adapter) plugin for Ionic and the [polyonic-secure-pouch](https://github.com/paulsutherland/polyonic-secure-pouch) plugin for the desktop and browser.

The Cordova example encrypts the local sqlite database, whereas the secure pouch plugin encrypts and decrypts your data when it is saved or fetched from the browser database. Either way, at rest, your data is encrypted.

You will need to include a key or password/secret from the user, or an api app, to encrypt the data.  You can store this key in [Ionic Secure Storage](https://ionicframework.com/docs/native/secure-storage/) or for the desktop you can use [Keytar](https://github.com/atom/node-keytar).  

## NPM Script Commands

| Platform/Commands ||
|:-|:-|
| **Desktop** ||
| `npm run electron:dev` | For development using live reload and opens with developer tools |
| `npm run electron:local` | Build and run on the desktop, no livereload or developer tools |
| `npm run electron:linux` | Production build for linux platform.  (Requires Linux) |
| `npm run electron:mac` | Production build for macOS.  (Requires macOS) |
| `npm run electron:windows` | Production build for Windows.  (Requires Windows) |
| **iOS** ||
| `npm run emulate:ios-dev` | For iOS development on the simulator using live reload |
| `npm run emulate:ios` | For iOS development on the simulator |
| `npm run device:ios-dev` | For iOS development on an iOS device using live reload |
| `npm run device:ios` | For iOS development on an iOS device |
| `npm run release:ios` | Production build for iOS.  (Requires XCode on macOS) |
| **Android** ||
| `npm run emulate:android-dev` | For Android development on an emulator using live reload |
| `npm run emulate:android` | For Android development on an emulator |
| `npm run device:android-dev` | For Android development on an Android device using live reload |
| `npm run device:android` | For Android development on an Android device |
| **Web Apps and PWA Apps** ||
| `npm run ionic` | For web and progressive web app development using live reload |

## Publishing your apps

[How to publish an Android App](https://ionicframework.com/docs/publishing/play-store)

[How to publish an iOS App](https://ionicframework.com/docs/publishing/play-store)

[How to publish a macOS and/or Windows App](https://ionicframework.com/docs/publishing/desktop-app)

[How to publish a progressive web app](https://ionicframework.com/docs/publishing/progressive-web-app)

## Credits

This application was built using the [Electron Framework](http://electron.atom.io/) :heart: for creating desktop apps and [Ionic Framework](http://ionicframework.com/) :heart: for the UI and creating Native Mobile Applications, Progressive Mobile Web Applications and Web Applications.

The app was inspired by:

[Angular Electron Shell](https://github.com/maximegris/angular-electron) :punch:

[Simple Cryptor Pouch Plugin](https://www.npmjs.com/package/simple-cryptor-pouch) (forked to create the [polyonic-secure-pouch](https://github.com/paulsutherland/polyonic-secure-pouch) plugin). :pray:

## Polyonic for Enterprise

This project is a generic shell/seed project that lets you build your app for multiple platforms.  For Enterprise use, including:

- Azure AD multi-tenancy Integration
- Office 365 apps
- Realtime CouchDB integration
- End to end encryption
- Support services
- WebXR
- WebRTC

You can contact us at [polyonic.com](http://polyonic.com) :metal:

## License

Released under the MIT license.


================================================
FILE: angular.json
================================================
{
  "$schema": "./node_modules/@angular-devkit/core/src/workspace/workspace-schema.json",
  "version": 1,
  "defaultProject": "app",
  "newProjectRoot": "projects",
  "projects": {
    "app": {
      "root": "",
      "sourceRoot": "src",
      "projectType": "application",
      "prefix": "app",
      "schematics": {},
      "architect": {
        "build": {
          "builder": "@angular-devkit/build-angular:browser",
          "options": {
            "progress": false,
            "outputPath": "www",
            "index": "src/index.html",
            "main": "src/main.ts",
            "polyfills": "src/polyfills.ts",
            "tsConfig": "src/tsconfig.app.json",
            "allowedCommonJsDependencies": [
               "pouchdb-adapter-cordova-sqlite",
               "polyonic-secure-pouch",
               "pouchdb",
               "pouchdb-utils"
            ],
            "assets": [
              {
                "glob": "**/*",
                "input": "src/assets",
                "output": "assets"
              },
              {
                "glob": "**/*.svg",
                "input": "node_modules/ionicons/dist/ionicons/svg",
                "output": "./svg"
              }
            ],
            "styles": [
              {
                "input": "src/theme/variables.scss"
              },
              {
                "input": "src/global.scss"
              }
            ],
            "scripts": []
          },
          "configurations": {
            "production": {
              "fileReplacements": [
                {
                  "replace": "src/environments/environment.ts",
                  "with": "src/environments/environment.prod.ts"
                }
              ],
              "optimization": true,
              "outputHashing": "all",
              "sourceMap": false,
              "namedChunks": false,
              "aot": true,
              "extractLicenses": true,
              "vendorChunk": false,
              "buildOptimizer": true
            }
          }
        },
        "serve": {
          "builder": "@angular-devkit/build-angular:dev-server",
          "options": {
            "browserTarget": "app:build"
          },
          "configurations": {
            "production": {
              "browserTarget": "app:build:production"
            }
          }
        },
        "extract-i18n": {
          "builder": "@angular-devkit/build-angular:extract-i18n",
          "options": {
            "browserTarget": "app:build"
          }
        },
        "test": {
          "builder": "@angular-devkit/build-angular:karma",
          "options": {
            "main": "src/test.ts",
            "polyfills": "src/polyfills.ts",
            "tsConfig": "src/tsconfig.spec.json",
            "karmaConfig": "src/karma.conf.js",
            "styles": [
              "styles.css"
            ],
            "scripts": [],
            "assets": [
              {
                "glob": "favicon.ico",
                "input": "src/",
                "output": "/"
              },
              {
                "glob": "**/*",
                "input": "src/assets",
                "output": "/assets"
              }
            ]
          }
        },
        "lint": {
          "builder": "@angular-devkit/build-angular:tslint",
          "options": {
            "tsConfig": [
              "src/tsconfig.app.json",
              "src/tsconfig.spec.json"
            ],
            "exclude": [
              "**/node_modules/**"
            ]
          }
        },
        "ionic-cordova-build": {
          "builder": "@ionic/angular-toolkit:cordova-build",
          "options": {
            "browserTarget": "app:build"
          },
          "configurations": {
            "production": {
              "browserTarget": "app:build:production"
            }
          }
        },
        "ionic-cordova-serve": {
          "builder": "@ionic/angular-toolkit:cordova-serve",
          "options": {
            "cordovaBuildTarget": "app:ionic-cordova-build",
            "devServerTarget": "app:serve"
          },
          "configurations": {
            "production": {
              "cordovaBuildTarget": "app:ionic-cordova-build:production",
              "devServerTarget": "app:serve:production"
            }
          }
        }
      }
    },
    "app-e2e": {
      "root": "e2e/",
      "projectType": "application",
      "architect": {
        "e2e": {
          "builder": "@angular-devkit/build-angular:protractor",
          "options": {
            "protractorConfig": "e2e/protractor.conf.js",
            "devServerTarget": "app:serve"
          }
        },
        "lint": {
          "builder": "@angular-devkit/build-angular:tslint",
          "options": {
            "tsConfig": "e2e/tsconfig.e2e.json",
            "exclude": [
              "**/node_modules/**"
            ]
          }
        }
      }
    }
  },
  "cli": {
    "defaultCollection": "@ionic/angular-toolkit"
  },
  "schematics": {
    "@ionic/angular-toolkit:component": {
      "styleext": "scss"
    },
    "@ionic/angular-toolkit:page": {
      "styleext": "scss"
    }
  }
}


================================================
FILE: config.xml
================================================
<?xml version='1.0' encoding='utf-8'?>
<widget id="io.polyonic.starter" version="0.0.1" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0">
    <name>Polyonic</name>
    <description>An awesome Polyonic Ionic/Cordova app.</description>
    <author email="hi@polyonic" href="http://polyonic.com/">Polyonic Framework Team</author>
    <content src="index.html" />
    <access origin="*" />
    <allow-intent href="http://*/*" />
    <allow-intent href="https://*/*" />
    <allow-intent href="tel:*" />
    <allow-intent href="sms:*" />
    <allow-intent href="mailto:*" />
    <allow-intent href="geo:*" />
    <preference name="ScrollEnabled" value="false" />
    <preference name="android-minSdkVersion" value="22" />
    <preference name="android-targetSdkVersion" value="29" />
    <preference name="AndroidXEnabled" value="true" />
    <preference name="BackupWebStorage" value="none" />
    <preference name="SplashMaintainAspectRatio" value="true" />
    <preference name="FadeSplashScreenDuration" value="300" />
    <preference name="SplashShowOnlyFirstTime" value="false" />
    <preference name="SplashScreen" value="screen" />
    <preference name="SplashScreenDelay" value="3000" />
    <preference name="WKWebViewOnly" value="true" />
    <platform name="android">
        <resource-file src="resources/android/xml/network_security_config.xml" target="app/src/main/res/xml/network_security_config.xml" />
        <edit-config file="app/src/main/AndroidManifest.xml" mode="merge" target="/manifest/application" xmlns:android="http://schemas.android.com/apk/res/android">
            <application android:networkSecurityConfig="@xml/network_security_config" />
        </edit-config>
        <allow-intent href="market:*" />
        <icon density="ldpi" src="resources/android/icon/drawable-ldpi-icon.png" />
        <icon density="mdpi" src="resources/android/icon/drawable-mdpi-icon.png" />
        <icon density="hdpi" src="resources/android/icon/drawable-hdpi-icon.png" />
        <icon density="xhdpi" src="resources/android/icon/drawable-xhdpi-icon.png" />
        <icon density="xxhdpi" src="resources/android/icon/drawable-xxhdpi-icon.png" />
        <icon density="xxxhdpi" src="resources/android/icon/drawable-xxxhdpi-icon.png" />
        <splash density="land-ldpi" src="resources/android/splash/drawable-land-ldpi-screen.png" />
        <splash density="land-mdpi" src="resources/android/splash/drawable-land-mdpi-screen.png" />
        <splash density="land-hdpi" src="resources/android/splash/drawable-land-hdpi-screen.png" />
        <splash density="land-xhdpi" src="resources/android/splash/drawable-land-xhdpi-screen.png" />
        <splash density="land-xxhdpi" src="resources/android/splash/drawable-land-xxhdpi-screen.png" />
        <splash density="land-xxxhdpi" src="resources/android/splash/drawable-land-xxxhdpi-screen.png" />
        <splash density="port-ldpi" src="resources/android/splash/drawable-port-ldpi-screen.png" />
        <splash density="port-mdpi" src="resources/android/splash/drawable-port-mdpi-screen.png" />
        <splash density="port-hdpi" src="resources/android/splash/drawable-port-hdpi-screen.png" />
        <splash density="port-xhdpi" src="resources/android/splash/drawable-port-xhdpi-screen.png" />
        <splash density="port-xxhdpi" src="resources/android/splash/drawable-port-xxhdpi-screen.png" />
        <splash density="port-xxxhdpi" src="resources/android/splash/drawable-port-xxxhdpi-screen.png" />
    </platform>
    <platform name="ios">
        <allow-intent href="itms:*" />
        <allow-intent href="itms-apps:*" />
        <icon height="57" src="resources/ios/icon/icon.png" width="57" />
        <icon height="114" src="resources/ios/icon/icon@2x.png" width="114" />
        <icon height="40" src="resources/ios/icon/icon-40.png" width="40" />
        <icon height="80" src="resources/ios/icon/icon-40@2x.png" width="80" />
        <icon height="120" src="resources/ios/icon/icon-40@3x.png" width="120" />
        <icon height="50" src="resources/ios/icon/icon-50.png" width="50" />
        <icon height="100" src="resources/ios/icon/icon-50@2x.png" width="100" />
        <icon height="60" src="resources/ios/icon/icon-60.png" width="60" />
        <icon height="120" src="resources/ios/icon/icon-60@2x.png" width="120" />
        <icon height="180" src="resources/ios/icon/icon-60@3x.png" width="180" />
        <icon height="72" src="resources/ios/icon/icon-72.png" width="72" />
        <icon height="144" src="resources/ios/icon/icon-72@2x.png" width="144" />
        <icon height="76" src="resources/ios/icon/icon-76.png" width="76" />
        <icon height="152" src="resources/ios/icon/icon-76@2x.png" width="152" />
        <icon height="167" src="resources/ios/icon/icon-83.5@2x.png" width="167" />
        <icon height="29" src="resources/ios/icon/icon-small.png" width="29" />
        <icon height="58" src="resources/ios/icon/icon-small@2x.png" width="58" />
        <icon height="87" src="resources/ios/icon/icon-small@3x.png" width="87" />
        <icon height="1024" src="resources/ios/icon/icon-1024.png" width="1024" />
        <splash height="1136" src="resources/ios/splash/Default-568h@2x~iphone.png" width="640" />
        <splash height="1334" src="resources/ios/splash/Default-667h.png" width="750" />
        <splash height="2208" src="resources/ios/splash/Default-736h.png" width="1242" />
        <splash height="1242" src="resources/ios/splash/Default-Landscape-736h.png" width="2208" />
        <splash height="1536" src="resources/ios/splash/Default-Landscape@2x~ipad.png" width="2048" />
        <splash height="2048" src="resources/ios/splash/Default-Landscape@~ipadpro.png" width="2732" />
        <splash height="768" src="resources/ios/splash/Default-Landscape~ipad.png" width="1024" />
        <splash height="2048" src="resources/ios/splash/Default-Portrait@2x~ipad.png" width="1536" />
        <splash height="2732" src="resources/ios/splash/Default-Portrait@~ipadpro.png" width="2048" />
        <splash height="1024" src="resources/ios/splash/Default-Portrait~ipad.png" width="768" />
        <splash height="960" src="resources/ios/splash/Default@2x~iphone.png" width="640" />
        <splash height="480" src="resources/ios/splash/Default~iphone.png" width="320" />
        <splash height="2732" src="resources/ios/splash/Default@2x~universal~anyany.png" width="2732" />
        <icon height="20" src="resources/ios/icon/icon-20.png" width="20" />
        <icon height="40" src="resources/ios/icon/icon-20@2x.png" width="40" />
        <icon height="60" src="resources/ios/icon/icon-20@3x.png" width="60" />
        <icon height="29" src="resources/ios/icon/icon-29.png" width="29" />
        <icon height="58" src="resources/ios/icon/icon-29@2x.png" width="58" />
        <icon height="87" src="resources/ios/icon/icon-29@3x.png" width="87" />
        <icon height="48" src="resources/ios/icon/icon-24@2x.png" width="48" />
        <icon height="55" src="resources/ios/icon/icon-27.5@2x.png" width="55" />
        <icon height="88" src="resources/ios/icon/icon-44@2x.png" width="88" />
        <icon height="172" src="resources/ios/icon/icon-86@2x.png" width="172" />
        <icon height="196" src="resources/ios/icon/icon-98@2x.png" width="196" />
        <splash height="2436" src="resources/ios/splash/Default-2436h.png" width="1125" />
        <splash height="1125" src="resources/ios/splash/Default-Landscape-2436h.png" width="2436" />
    </platform>
    <plugin name="cordova-plugin-whitelist" spec="1.3.3" />
    <plugin name="cordova-plugin-statusbar" spec="2.4.2" />
    <plugin name="cordova-plugin-device" spec="2.0.2" />
    <plugin name="cordova-plugin-splashscreen" spec="5.0.2" />
    <plugin name="cordova-plugin-ionic-webview" spec="^2.0.0" />
    <plugin name="cordova-plugin-ionic-keyboard" spec="^2.0.5" />
    <plugin name="cordova-sqlcipher-adapter" spec="0.1.12-rc2" />
</widget>


================================================
FILE: e2e/protractor.conf.js
================================================
// Protractor configuration file, see link for more information
// https://github.com/angular/protractor/blob/master/lib/config.ts

const { SpecReporter } = require('jasmine-spec-reporter');

exports.config = {
  allScriptsTimeout: 11000,
  specs: [
    './src/**/*.e2e-spec.ts'
  ],
  capabilities: {
    'browserName': 'chrome'
  },
  directConnect: true,
  baseUrl: 'http://localhost:4200/',
  framework: 'jasmine',
  jasmineNodeOpts: {
    showColors: true,
    defaultTimeoutInterval: 30000,
    print: function() {}
  },
  onPrepare() {
    require('ts-node').register({
      project: 'e2e/tsconfig.e2e.json'
    });
    jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
  }
};


================================================
FILE: e2e/src/app.e2e-spec.ts
================================================
import { AppPage } from './app.po';

describe('new App', () => {
  let page: AppPage;

  beforeEach(() => {
    page = new AppPage();
  });

  it('should display welcome message', () => {
    page.navigateTo();
    expect(page.getParagraphText()).toContain('The world is your oyster.');
  });
});


================================================
FILE: e2e/src/app.po.ts
================================================
import { browser, by, element } from 'protractor';

export class AppPage {
  navigateTo() {
    return browser.get('/');
  }

  getParagraphText() {
    return element(by.deepCss('app-root ion-content')).getText();
  }
}


================================================
FILE: e2e/tsconfig.e2e.json
================================================
{
  "extends": "../tsconfig.json",
  "compilerOptions": {
    "outDir": "../out-tsc/e2e",
    "baseUrl": "./",
    "module": "commonjs",
    "target": "es5"
  }
}


================================================
FILE: electron-builder.json
================================================
{
  "productName": "Polyonic",
  "directories": {
    "buildResources": "./resources/electron",
    "output": "release/"
  },
    "files": [
        "**/*",
        "!*.ts",
        "!*.code-workspace",
        "!LICENSE.md",
        "!package.json",
        "!package-lock.json",
        "!config/",
        "!platforms/",
        "!plugins/",
        "!src/",
        "!e2e/",
        "!hooks/",
        "!.angular-cli.json",
        "!_config.yml",
        "!karma.conf.js",
        "!tsconfig.json",
        "!tslint.json",
        "!app.db/",
        "!release/",
        "!resources/"
    ],
  "win": {
    "icon": "resources/electron/windows/icon.ico",
    "target": [
      "portable"
    ]
  },
  "squirrelWindows": {
    "msi":"true"
  },
  "msi" : {
    "warningsAsErrors": false
  },
  "nsis" : {
    "oneClick": false,
    "allowToChangeInstallationDirectory": true
  },
  "appx": {
    "publisherDisplayName":"Polyonic"
  },
  "mac": {
    "icon": "resources/electron/osx/icon.icns",
    "target": [
      "dmg"
    ]
  },
  "linux": {
    "icon": "dist",
    "target": [
      "AppImage"
    ]
  }
}


================================================
FILE: electron.js
================================================
const electron = require('electron')
const app = electron.app
const BrowserWindow = electron.BrowserWindow
const path = require('path')

// process.env.ELECTRON_DISABLE_SECURITY_WARNINGS = true

let win
const args = process.argv.slice(1)
const serve = args.some(val => val === '--serve')

function createWindow () {
  debugger
  win = new BrowserWindow({
    width: 1800,
    height: 1200,
    center: true,
    icon: path.join(__dirname, './resources/electron/icons/64x64.png'),
    webPreferences: {
      nodeIntegration: true,
      enableRemoteModule: true,
      contextIsolation: false
    }
  })

  if (serve) {
    win.loadURL('http://localhost:4200')
  } else {
    win.loadURL(`file://${__dirname}/www/index.html`)
  }

  win.webContents.on('did-fail-load', () => {
    win.loadURL(`file://${__dirname}/www/index.html`)
  })

  console.log(`Node Environment: ${process.env.NODE_ENV}`)
  if (process.env.NODE_ENV === 'development') {
    win.toggleDevTools()
  }

  // Emitted when the window is closed.
  win.on('closed', () => {
    // Dereference the window object, usually you would store window
    // in an array if your app supports multi windows, this is the time
    // when you should delete the corresponding element.
    win = null
  })
}
try {
  // This method will be called when Electron has finished
  // initialization and is ready to create browser windows.
  // Some APIs can only be used after this event occurs.
  app.on('ready', createWindow)

  // Quit when all windows are closed.
  app.on('window-all-closed', () => {
    // On OS X it is common for applications and their menu bar
    // to stay active until the user quits explicitly with Cmd + Q
    if (process.platform !== 'darwin') {
      app.quit()
    }
  })

  app.on('activate', () => {
    // On OS X it's common to re-create a window in the app when the
    // dock icon is clicked and there are no other windows open.
    if (win === null) {
      createWindow()
    }
  })
} catch (e) {
  // Catch Error
  // throw e;
}


================================================
FILE: ionic.config.json
================================================
{
  "name": "ionic5",
  "integrations": {
    "cordova": {}
  },
  "type": "angular"
}


================================================
FILE: package.json
================================================
{
  "name": "polyonic",
  "productName": "Polyonic App",
  "description": "An Ionic project",
  "version": "3.0.6",
  "author": {
    "name": "Your Name",
    "email": "email@your-email.com",
    "url": "http://yourwebsite.com"
  },
  "repository": "https://github.com/paulsutherland/polyonic",
  "copyright": "© 2018, Your Name",
  "private": true,
  "main": "electron.js",
  "scripts": {
    "build:electron": "ng build --base-href ./",
    "build:electron:dev": "npm run build:electron -- -c dev",
    "build:electron:prod": "npm run build:electron -- -c production",
    "electron:dev": "export NODE_ENV=development || set NODE_ENV=development && npm-run-all -p ng:serve electron:serve",
    "electron:dev:debug": "npm-run-all -p ng:serve electron:serve:debug",
    "electron:dev:debug-live": "npm-run-all -p ng:serve electron:serve:debug-live",
    "electron:serve": "wait-on http-get://localhost:4200/ && run-electron . --serve",
    "electron:serve:debug": "wait-on http-get://localhost:4200/ && run-electron --inspect-brk=9229 . --serve",
    "electron:serve:debug-live": "wait-on http-get://localhost:4200/ && nodemon --watch electron.js --exec run-electron --inspect-brk=9229 . --serve",
    "electron:local": "npm run build:electron:prod && run-electron .",
    "electron:linux": "npm run build:electron:prod && npx electron-builder build --linux",
    "electron:windows": "npm run build:electron:prod && npx electron-builder build --windows",
    "electron:mac": "npm run build:electron:prod && npx electron-builder build --mac",
    "ionic": "npm run ng:serve:web",
    "emulate:ios-dev": "ionic cordova emulate ios --l",
    "run:ios-dev": "ionic cordova run ios --l --device",
    "emulate:ios": "ionic cordova emulate ios",
    "run:ios": "ionic cordova run ios --device",
    "release:ios": "ionic cordova build ios --prod --release",
    "emulate:android-dev": "ionic cordova emulate android --l",
    "run:android-dev": "ionic cordova run android --device --l",
    "emulate:android": "ionic cordova emulate android",
    "run:android": "ionic cordova run android --device",
    "release:android": "ionic cordova build android --prod --release ",
    "ng": "ng",
    "ng:serve": "ng serve",
    "ng:serve:web": "ng serve -o",
    "test": "ng test",
    "e2e": "ng e2e",
    "xcode": "open -a Xcode platforms/ios/"
  },
  "dependencies": {
    "@angular/common": "11.2.6",
    "@angular/core": "11.2.6",
    "@angular/forms": "11.2.6",
    "@angular/platform-browser": "11.2.6",
    "@angular/platform-browser-dynamic": "11.2.6",
    "@angular/router": "11.2.6",
    "@ionic-native/core": "5.31.1",
    "@ionic-native/splash-screen": "5.31.1",
    "@ionic-native/status-bar": "5.31.1",
    "@ionic/angular": "5.6.1",
    "cordova-android": "9.0.0",
    "cordova-ios": "6.2.0",
    "cordova-plugin-device": "2.0.3",
    "cordova-plugin-ionic-keyboard": "2.2.0",
    "cordova-plugin-ionic-webview": "^5.0.0",
    "cordova-plugin-splashscreen": "^6.0.0",
    "cordova-plugin-statusbar": "2.4.3",
    "cordova-plugin-whitelist": "1.3.4",
    "cordova-sqlcipher-adapter": "^0.5.3",
    "core-js": "3.9.1",
    "polyonic-secure-pouch": "1.0.7",
    "pouchdb": "7.2.2",
    "pouchdb-adapter-cordova-sqlite": "2.0.8",
    "run-electron": "1.0.0",
    "rxjs": "6.6.6",
    "zone.js": "0.11.4"
  },
  "devDependencies": {
    "@angular-devkit/build-angular": "^0.1102.5",
    "@angular/cli": "~11.2.5",
    "@angular/compiler": "11.2.6",
    "@angular/compiler-cli": "11.2.6",
    "@angular/language-service": "11.2.6",
    "@ionic/angular-toolkit": "^3.1.1",
    "@types/jasmine": "3.6.7",
    "@types/jasminewd2": "2.0.8",
    "@types/node": "14.14.35",
    "codelyzer": "6.0.1",
    "electron": "11.3.0",
    "electron-builder": "22.10.5",
    "husky": "5.1.3",
    "jasmine-core": "3.7.1",
    "jasmine-spec-reporter": "6.0.0",
    "karma": "6.2.0",
    "karma-chrome-launcher": "3.1.0",
    "karma-coverage-istanbul-reporter": "3.0.3",
    "karma-jasmine": "4.0.1",
    "karma-jasmine-html-reporter": "1.5.4",
    "nodemon": "^2.0.7",
    "npm-run-all": "4.1.5",
    "protractor": "7.0.0",
    "ts-node": "9.1.1",
    "tslint": "6.1.3",
    "typescript": "4.1.5",
    "wait-on": "5.3.0"
  },
  "cordova": {
    "plugins": {
      "cordova-plugin-whitelist": {},
      "cordova-plugin-statusbar": {},
      "cordova-plugin-device": {},
      "cordova-plugin-splashscreen": {},
      "cordova-plugin-ionic-webview": {},
      "cordova-plugin-ionic-keyboard": {},
      "cordova-sqlcipher-adapter": {},
      "cordova-plugin-androidx-adapter": {}
    },
    "platforms": []
  }
}

================================================
FILE: resources/android/xml/network_security_config.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <base-config cleartextTrafficPermitted="true">
        <trust-anchors>
            <certificates src="system" />
        </trust-anchors>
    </base-config>
    <domain-config cleartextTrafficPermitted="true">
        <domain includeSubdomains="true">localhost</domain>
    </domain-config>
</network-security-config>


================================================
FILE: resources/electron/.gitkeep
================================================


================================================
FILE: resources/electron/linux/DEBIAN/control
================================================
Package: {{name}}
Version: {{version}}
Maintainer: {{author}}
Priority: optional
Architecture: amd64
Installed-Size: {{size}}
Description: {{description}}


================================================
FILE: resources/electron/linux/app.desktop
================================================
[Desktop Entry]
Version=1.0
Type=Application
Encoding=UTF-8
Name={{productName}}
Comment={{description}}
Exec=/opt/{{name}}/{{name}}
Path=/opt/{{name}}/
Icon=/opt/{{name}}/icon.png
Terminal=false
Categories=Application;


================================================
FILE: resources/electron/windows/installer.nsi
================================================
; NSIS packaging/install script
; Docs: http://nsis.sourceforge.net/Docs/Contents.html

!include LogicLib.nsh
!include nsDialogs.nsh

; --------------------------------
; Variables
; --------------------------------

!define dest "{{dest}}"
!define src "{{src}}"
!define name "{{name}}"
!define productName "{{productName}}"
!define author "{{author}}"
!define version "{{version}}"
!define icon "{{icon}}"
!define setupIcon "{{setupIcon}}"
!define banner "{{banner}}"

!define exec "{{productName}}.exe"

!define regkey "Software\${productName}"
!define uninstkey "Software\Microsoft\Windows\CurrentVersion\Uninstall\${productName}"

!define uninstaller "uninstall.exe"

; --------------------------------
; Installation
; --------------------------------

Unicode true
SetCompressor /SOLID lzma

Name "${productName}"
Icon "${setupIcon}"
OutFile "${dest}"
InstallDir "$PROGRAMFILES\${productName}"
InstallDirRegKey HKLM "${regkey}" ""

RequestExecutionLevel admin
CRCCheck on
SilentInstall normal

XPStyle on
ShowInstDetails nevershow
AutoCloseWindow false
WindowIcon off

Caption "${productName} Setup"
; Don't add sub-captions to title bar
SubCaption 3 " "
SubCaption 4 " "

Page custom welcome
Page instfiles

Var Image
Var ImageHandle

Function .onInit

    ; Extract banner image for welcome page
    InitPluginsDir
    ReserveFile "${banner}"
    File /oname=$PLUGINSDIR\banner.bmp "${banner}"

FunctionEnd

; Custom welcome page
Function welcome

    nsDialogs::Create 1018

    ${NSD_CreateLabel} 185 1u 210 100% "Welcome to ${productName} version ${version} installer.$\r$\n$\r$\nClick install to begin."

    ${NSD_CreateBitmap} 0 0 170 210 ""
    Pop $Image
    ${NSD_SetImage} $Image $PLUGINSDIR\banner.bmp $ImageHandle

    nsDialogs::Show

    ${NSD_FreeImage} $ImageHandle

FunctionEnd

; Installation declarations
Section "Install"

    WriteRegStr HKLM "${regkey}" "Install_Dir" "$INSTDIR"
    WriteRegStr HKLM "${uninstkey}" "DisplayName" "${productName}"
    WriteRegStr HKLM "${uninstkey}" "DisplayIcon" '"$INSTDIR\icon.ico"'
    WriteRegStr HKLM "${uninstkey}" "UninstallString" '"$INSTDIR\${uninstaller}"'
    WriteRegStr HKLM "${uninstkey}" "Publisher" "${author}"
    WriteRegStr HKLM "${uninstkey}" "DisplayVersion" "${version}"

    ; Remove all application files copied by previous installation
    RMDir /r "$INSTDIR"

    SetOutPath $INSTDIR

    ; Include all files from /build directory
    File /r "${src}\*"

    ; Create start menu shortcut
    SetShellVarContext all
    CreateShortCut "$SMPROGRAMS\${productName}.lnk" "$INSTDIR\${exec}" "" "$INSTDIR\icon.ico"
    ; Create desktop shortcut
    CreateShortCut "$DESKTOP\${productName}.lnk" "$INSTDIR\${exec}" "" "$INSTDIR\icon.ico"

    WriteUninstaller "${uninstaller}"

SectionEnd

; --------------------------------
; Uninstaller
; --------------------------------

ShowUninstDetails nevershow

UninstallCaption "Uninstall ${productName}"
UninstallText "Don't like ${productName} anymore? Hit uninstall button."
UninstallIcon "${icon}"

UninstPage custom un.confirm un.confirmOnLeave
UninstPage instfiles

Var RemoveAppDataCheckbox
Var RemoveAppDataCheckbox_State

; Custom uninstall confirm page
Function un.confirm

    nsDialogs::Create 1018

    ${NSD_CreateLabel} 1u 1u 100% 24u "If you really want to remove ${productName} from your computer press uninstall button."

    ${NSD_CreateCheckbox} 1u 35u 100% 10u "Remove also my ${productName} personal data"
    Pop $RemoveAppDataCheckbox

    nsDialogs::Show

FunctionEnd

Function un.confirmOnLeave

    ; Save checkbox state on page leave
    ${NSD_GetState} $RemoveAppDataCheckbox $RemoveAppDataCheckbox_State

FunctionEnd

; Uninstall declarations
Section "Uninstall"

    DeleteRegKey HKLM "${uninstkey}"
    DeleteRegKey HKLM "${regkey}"

    SetShellVarContext all
    Delete "$SMPROGRAMS\${productName}.lnk"
    ; Remove desktop shortcut
    Delete "$DESKTOP\${productName}.lnk"
    ; Remove whole directory from Program Files
    RMDir /r "$INSTDIR"

    ; Remove also appData directory generated by your app if user checked this option
    ${If} $RemoveAppDataCheckbox_State == ${BST_CHECKED}
        RMDir /r "$APPDATA\${productName}"
    ${EndIf}

SectionEnd


================================================
FILE: resources/icon.png.md5
================================================
e495003092b862d39b09a0f1b31f732b

================================================
FILE: resources/splash.png.md5
================================================
f010d2a2420ae8b5b5fa398e77da6397

================================================
FILE: src/app/app-routing.module.ts
================================================
import { NgModule } from '@angular/core'
import { Routes, RouterModule } from '@angular/router'

const routes: Routes = [
  { path: '', loadChildren: './tabs/tabs.module#TabsPageModule' }
]
@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule {}


================================================
FILE: src/app/app.component.html
================================================
<ion-app>
  <ion-router-outlet></ion-router-outlet>
</ion-app>


================================================
FILE: src/app/app.component.spec.ts
================================================
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'
import { TestBed, async } from '@angular/core/testing'

import { Platform } from '@ionic/angular'
import { SplashScreen } from '@ionic-native/splash-screen/ngx'
import { StatusBar } from '@ionic-native/status-bar/ngx'

import { AppComponent } from './app.component'

describe('AppComponent', () => {

  let statusBarSpy, splashScreenSpy, platformReadySpy, platformSpy

  beforeEach(async(() => {
    statusBarSpy = jasmine.createSpyObj('StatusBar', ['styleDefault'])
    splashScreenSpy = jasmine.createSpyObj('SplashScreen', ['hide'])
    platformReadySpy = Promise.resolve()
    platformSpy = jasmine.createSpyObj('Platform', { ready: platformReadySpy })

    TestBed.configureTestingModule({
      declarations: [AppComponent],
      schemas: [CUSTOM_ELEMENTS_SCHEMA],
      providers: [
        { provide: StatusBar, useValue: statusBarSpy },
        { provide: SplashScreen, useValue: splashScreenSpy },
        { provide: Platform, useValue: platformSpy },
      ],
    }).compileComponents()
  }))

  it('should create the app', () => {
    const fixture = TestBed.createComponent(AppComponent)
    const app = fixture.debugElement.componentInstance
    expect(app).toBeTruthy()
  })

  it('should initialize the app', async () => {
    TestBed.createComponent(AppComponent)
    expect(platformSpy.ready).toHaveBeenCalled()
    await platformReadySpy
    expect(statusBarSpy.styleDefault).toHaveBeenCalled()
    expect(splashScreenSpy.hide).toHaveBeenCalled()
  })

  // TODO: add more tests!

})


================================================
FILE: src/app/app.component.ts
================================================
import { Component } from '@angular/core'

import { Platform } from '@ionic/angular'
import { SplashScreen } from '@ionic-native/splash-screen/ngx'
import { StatusBar } from '@ionic-native/status-bar/ngx'
import { DataService } from './data.service'

@Component({
  selector: 'app-root',
  templateUrl: 'app.component.html'
})
export class AppComponent {
  constructor(
    private platform: Platform,
    private splashScreen: SplashScreen,
    private statusBar: StatusBar,
    private data: DataService
  ) {
    this.initializeApp()
  }

  initializeApp() {
    this.platform.ready().then(() => {
      if (this.platform.is('mobile')) {
        this.statusBar.styleLightContent()
        this.splashScreen.hide()
      }

      this.data.setup()
      .then(info => {
        console.log('Database setup complete')
      })
      .catch(error => console.log('Error setting up the Database: ', error))
    })
  }
}


================================================
FILE: src/app/app.module.ts
================================================
import { NgModule } from '@angular/core'
import { BrowserModule } from '@angular/platform-browser'
import { RouteReuseStrategy } from '@angular/router'

import { IonicModule, IonicRouteStrategy } from '@ionic/angular'
import { SplashScreen } from '@ionic-native/splash-screen/ngx'
import { StatusBar } from '@ionic-native/status-bar/ngx'

import { AppRoutingModule } from './app-routing.module'
import { AppComponent } from './app.component'

@NgModule({
  declarations: [AppComponent],
  entryComponents: [],
  imports: [BrowserModule, IonicModule.forRoot(), AppRoutingModule],
  providers: [
    StatusBar,
    SplashScreen,
    { provide: RouteReuseStrategy, useClass: IonicRouteStrategy }
  ],
  bootstrap: [AppComponent]
})
export class AppModule {}


================================================
FILE: src/app/data.service.spec.ts
================================================
import { TestBed, inject } from '@angular/core/testing'

import { DataService } from './data.service'

describe('DataService', () => {
  beforeEach(() => {
    TestBed.configureTestingModule({
      providers: [DataService]
    })
  })

  it('should be created', inject([DataService], (service: DataService) => {
    expect(service).toBeTruthy()
  }))
})


================================================
FILE: src/app/data.service.ts
================================================
import { Injectable } from '@angular/core'
import { Platform } from '@ionic/angular'
import { environment } from '../environments/environment'
import { ElectronService } from './electron.service'
import { EventService } from './events.service'
import * as PouchDB from 'pouchdb/dist/pouchdb'
import SecurePouch from 'polyonic-secure-pouch'
import cordovaSqlitePlugin from 'pouchdb-adapter-cordova-sqlite'

@Injectable({
  providedIn: 'root'
})
export class DataService {

  public db: any
  public dbInfo: any

  constructor(
    public electron: ElectronService,
    private events: EventService,
    private platform: Platform
  ) {}

  public setup() {
    const ctx = this
    console.log('Setting up the application database')

    if (ctx.platform.is('mobile')) {
      return ctx.mobileDB()
    } else {
      return ctx.webDB()
    }
  }

  private mobileDB() {
    const ctx = this
    console.log('This app is running on a mobile device')
    return new Promise<void>((resolve, reject) => {
      ctx.platform.ready()
      .then(() => {
        // Go for either an encrypted db or encrypted data
        // There is a greater performance hit on the encrypted data option
        PouchDB.plugin(cordovaSqlitePlugin)
        return ctx.db = new PouchDB('app.db', {
          adapter: 'cordova-sqlite',
          key: 'secret', // <<<<<<<<<<<<< Replace with your secret key
          iosDatabaseLocation: 'Documents'
        })
        // PouchDB.plugin(SecurePouch)
        // ctx.db = new PouchDB('app.db')
        // ctx.db.securePouch('secret') // <<<<<<<<<<<<< Replace with your secret key
      })
      .then(res => {
        return ctx.db.info()
      })
      .then(info => {
        ctx.events.publish('database:available', info)
        resolve()
      })
      .catch(error => {
        console.log('Error waiting for platform to load', error)
        reject(error)
      })
    })
  }

  private webDB() {
    const ctx = this
    console.log('This app is running in a web browser')
    return new Promise<void>((resolve, reject) => {
      ctx.platform.ready()
      .then(() => {
        PouchDB.plugin(SecurePouch)
        return ctx.db = new PouchDB('app.db')

      })
      .then(res => {
        ctx.db.encrypt('password') // <<<<<<<<<<<<< Replace with your secret key
        return ctx.db.info()
      })
      .then(info => {
        ctx.events.publish('database:available', info)
        resolve()
      })
      .catch(error => {
        console.log('Error waiting for platform to load', error)
        reject(error)
      })
    })
  }
}


================================================
FILE: src/app/electron.service.spec.ts
================================================
import { TestBed } from '@angular/core/testing';

import { ElectronService } from './electron.service';

describe('ElectronService', () => {
  let service: ElectronService;

  beforeEach(() => {
    TestBed.configureTestingModule({});
    service = TestBed.inject(ElectronService);
  });

  it('should be created', () => {
    expect(service).toBeTruthy();
  });
});


================================================
FILE: src/app/electron.service.ts
================================================
import { Injectable } from '@angular/core'
import { ipcRenderer, webFrame, remote } from 'electron'
import * as childProcess from 'child_process'
import * as fs from 'fs'

@Injectable({
  providedIn: 'root'
})
export class ElectronService {
  ipcRenderer: typeof ipcRenderer
  webFrame: typeof webFrame
  remote: typeof remote
  childProcess: typeof childProcess
  fs: typeof fs
  
  get isElectron(): boolean {
    return !!(typeof navigator === 'object' && typeof navigator.userAgent === 'string' && navigator.userAgent.indexOf('Electron') >= 0)
  }

  get isElectronApp(): boolean {
    // For compatibility with anyone who used the ngx-electron package
    return this.isElectron
  }

  constructor() {
    if (this.isElectron) {
      this.ipcRenderer = window.require('electron').ipcRenderer
      this.webFrame = window.require('electron').webFrame
      this.remote = window.require('electron').remote
      this.childProcess = window.require('child_process')
      this.fs = window.require('fs')
    }
  }
}

================================================
FILE: src/app/events.service.spec.ts
================================================
import { TestBed } from '@angular/core/testing';

import { EventsService } from './events.service';

describe('EventsService', () => {
  beforeEach(() => TestBed.configureTestingModule({}));

  it('should be created', () => {
    const service: EventsService = TestBed.get(EventsService);
    expect(service).toBeTruthy();
  });
});


================================================
FILE: src/app/events.service.ts
================================================
import { Injectable } from '@angular/core'
import { Subject, Subscription } from 'rxjs'

@Injectable({
  providedIn: 'root'
})
export class EventService {

  private channels: { [key: string]: Subject<any> } = {}

  subscribe(topic: string, observer: (_: any) => void): Subscription {
    const ctx = this

    if (!ctx.channels[topic]) { ctx.channels[topic] = new Subject<any>() }

    return ctx.channels[topic].subscribe(observer)
  }

  publish(topic: string, data: any): void {
    const ctx = this

    const subject = ctx.channels[topic]
    if (!subject) { return } // Or you can create a new subject for future subscribers

    subject.next(data)
  }

  destroy(topic: string): null {
    const ctx = this
    const subject = ctx.channels[topic]
    if (!subject) {
      return
    }

    subject.complete()
    delete ctx.channels[topic]
  }

}



================================================
FILE: src/app/tab1/tab1.module.ts
================================================
import { IonicModule } from '@ionic/angular';
import { RouterModule } from '@angular/router';
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { Tab1Page } from './tab1.page';

@NgModule({
  imports: [
    IonicModule,
    CommonModule,
    FormsModule,
    RouterModule.forChild([{ path: '', component: Tab1Page }])
  ],
  declarations: [Tab1Page]
})
export class Tab1PageModule {}


================================================
FILE: src/app/tab1/tab1.page.html
================================================
<ion-header>
  <ion-toolbar>
    <ion-title>
      Tab One
    </ion-title>
  </ion-toolbar>
</ion-header>

<ion-content>
  <ion-card class="welcome-card">
    <ion-img src="assets/shapes.svg"></ion-img>
    <ion-card-header>
      <ion-card-subtitle>Get Started</ion-card-subtitle>
      <ion-card-title>Welcome to Polyonic</ion-card-title>
    </ion-card-header>
    <ion-card-content>
      <p>Now that your app has been created, you'll want to start building out features and components. Check out some of the resources below for next steps.</p>
    </ion-card-content>
  </ion-card>
  
  <ion-card>
    <ion-card-header>
      <ion-card-subtitle>System</ion-card-subtitle>
      <ion-card-title>Data</ion-card-title>
    </ion-card-header>
    <ion-card-content>
      <!-- All of the Node.js APIs are available in this renderer process. -->
      <span *ngIf="electron.remote && electron.remote.process && electron.isElectronApp">
        <pre>
          Node v{{electron.remote.process.versions.node}}
          Chromium v{{electron.remote.process.versions.chrome}}
          Electron v{{electron.remote.process.versions.electron}}
        </pre>
      </span>
      We are using PouchDB for our Database<pre>{{dbInfo | json}}</pre>
    </ion-card-content>
  </ion-card>
</ion-content>


================================================
FILE: src/app/tab1/tab1.page.scss
================================================
.welcome-card ion-img {
  max-height: 35vh;
  overflow: hidden;
}


================================================
FILE: src/app/tab1/tab1.page.spec.ts
================================================
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { async, ComponentFixture, TestBed } from '@angular/core/testing';

import { Tab1Page } from './tab1.page';

describe('Tab1Page', () => {
  let component: Tab1Page;
  let fixture: ComponentFixture<Tab1Page>;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [Tab1Page],
      schemas: [CUSTOM_ELEMENTS_SCHEMA],
    }).compileComponents();
  }));

  beforeEach(() => {
    fixture = TestBed.createComponent(Tab1Page);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('should create', () => {
    expect(component).toBeTruthy();
  });
});


================================================
FILE: src/app/tab1/tab1.page.ts
================================================
import { Component, OnInit } from '@angular/core'
import { ElectronService } from '../electron.service'
import { DataService } from '../data.service'
import { EventService } from '../events.service'

@Component({
  selector: 'app-tab1',
  templateUrl: 'tab1.page.html',
  styleUrls: ['tab1.page.scss']
})
export class Tab1Page  implements OnInit {

  public db: any
  public dbInfo: Object
  public electron: any

  constructor(
    public electronService: ElectronService,
    private data: DataService,
    private events: EventService
  ) {
    this.events.subscribe('database:available', (info) => {
      console.log('Database is now available')
      this.db = this.data.db
      this.dbInfo = info
    })
  }

  ngOnInit () {
    const ctx = this

    ctx.electron = ctx.electronService

    if (ctx.electron.isElectronApp) {
      ctx.db = ctx.data.db
      ctx.data.db.info()
      .then(info => ctx.dbInfo = info)
      .catch(err => console.log(err))
    }
  }

}


================================================
FILE: src/app/tab2/tab2.module.ts
================================================
import { IonicModule } from '@ionic/angular';
import { RouterModule } from '@angular/router';
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { Tab2Page } from './tab2.page';

@NgModule({
  imports: [
    IonicModule,
    CommonModule,
    FormsModule,
    RouterModule.forChild([{ path: '', component: Tab2Page }])
  ],
  declarations: [Tab2Page]
})
export class Tab2PageModule {}


================================================
FILE: src/app/tab2/tab2.page.html
================================================
<ion-header>
  <ion-toolbar>
    <ion-title>
      Tab Two
    </ion-title>
  </ion-toolbar>
</ion-header>

<ion-content></ion-content>


================================================
FILE: src/app/tab2/tab2.page.scss
================================================



================================================
FILE: src/app/tab2/tab2.page.spec.ts
================================================
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { async, ComponentFixture, TestBed } from '@angular/core/testing';

import { Tab2Page } from './tab2.page';

describe('Tab2Page', () => {
  let component: Tab2Page;
  let fixture: ComponentFixture<Tab2Page>;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [Tab2Page],
      schemas: [CUSTOM_ELEMENTS_SCHEMA],
    }).compileComponents();
  }));

  beforeEach(() => {
    fixture = TestBed.createComponent(Tab2Page);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('should create', () => {
    expect(component).toBeTruthy();
  });
});


================================================
FILE: src/app/tab2/tab2.page.ts
================================================
import { Component } from '@angular/core';

@Component({
  selector: 'app-tab2',
  templateUrl: 'tab2.page.html',
  styleUrls: ['tab2.page.scss']
})
export class Tab2Page {}


================================================
FILE: src/app/tab3/tab3.module.ts
================================================
import { IonicModule } from '@ionic/angular';
import { RouterModule } from '@angular/router';
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { Tab3Page } from './tab3.page';

@NgModule({
  imports: [
    IonicModule,
    CommonModule,
    FormsModule,
    RouterModule.forChild([{ path: '', component: Tab3Page }])
  ],
  declarations: [Tab3Page]
})
export class Tab3PageModule {}


================================================
FILE: src/app/tab3/tab3.page.html
================================================
<ion-header>
  <ion-toolbar>
    <ion-title>
      Tab Three
    </ion-title>
  </ion-toolbar>
</ion-header>

<ion-content></ion-content>


================================================
FILE: src/app/tab3/tab3.page.scss
================================================



================================================
FILE: src/app/tab3/tab3.page.spec.ts
================================================
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { async, ComponentFixture, TestBed } from '@angular/core/testing';

import { Tab3Page } from './tab3.page';

describe('Tab3Page', () => {
  let component: Tab3Page;
  let fixture: ComponentFixture<Tab3Page>;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [Tab3Page],
      schemas: [CUSTOM_ELEMENTS_SCHEMA],
    }).compileComponents();
  }));

  beforeEach(() => {
    fixture = TestBed.createComponent(Tab3Page);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('should create', () => {
    expect(component).toBeTruthy();
  });
});


================================================
FILE: src/app/tab3/tab3.page.ts
================================================
import { Component } from '@angular/core';

@Component({
  selector: 'app-tab3',
  templateUrl: 'tab3.page.html',
  styleUrls: ['tab3.page.scss']
})
export class Tab3Page {}


================================================
FILE: src/app/tabs/tabs.module.ts
================================================
import { IonicModule } from '@ionic/angular';
import { RouterModule } from '@angular/router';
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';

import { TabsPageRoutingModule } from './tabs.router.module';

import { TabsPage } from './tabs.page';

@NgModule({
  imports: [
    IonicModule,
    CommonModule,
    FormsModule,
    TabsPageRoutingModule
  ],
  declarations: [TabsPage]
})
export class TabsPageModule {}


================================================
FILE: src/app/tabs/tabs.page.html
================================================
<ion-tabs>

  <ion-tab-bar slot="bottom">
    <ion-tab-button tab="tab1">
      <ion-icon name="flash"></ion-icon>
      <ion-label>Tab One</ion-label>
    </ion-tab-button>

    <ion-tab-button tab="tab2">
      <ion-icon name="apps"></ion-icon>
      <ion-label>Tab Two</ion-label>
    </ion-tab-button>

    <ion-tab-button tab="tab3">
      <ion-icon name="send"></ion-icon>
      <ion-label>Tab Three</ion-label>
    </ion-tab-button>
  </ion-tab-bar>

</ion-tabs>


================================================
FILE: src/app/tabs/tabs.page.scss
================================================



================================================
FILE: src/app/tabs/tabs.page.spec.ts
================================================
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { async, ComponentFixture, TestBed } from '@angular/core/testing';

import { TabsPage } from './tabs.page';

describe('TabsPage', () => {
  let component: TabsPage;
  let fixture: ComponentFixture<TabsPage>;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [TabsPage],
      schemas: [CUSTOM_ELEMENTS_SCHEMA],
    }).compileComponents();
  }));

  beforeEach(() => {
    fixture = TestBed.createComponent(TabsPage);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('should create', () => {
    expect(component).toBeTruthy();
  });
});


================================================
FILE: src/app/tabs/tabs.page.ts
================================================
import { Component } from '@angular/core';

@Component({
  selector: 'app-tabs',
  templateUrl: 'tabs.page.html',
  styleUrls: ['tabs.page.scss']
})
export class TabsPage {}


================================================
FILE: src/app/tabs/tabs.router.module.ts
================================================
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';

import { TabsPage } from './tabs.page';

const routes: Routes = [
  {
    path: 'tabs',
    component: TabsPage,
    children: [
      {
        path: 'tab1',
        children: [
          {
            path: '',
            loadChildren: '../tab1/tab1.module#Tab1PageModule'
          }
        ]
      },
      {
        path: 'tab2',
        children: [
          {
            path: '',
            loadChildren: '../tab2/tab2.module#Tab2PageModule'
          }
        ]
      },
      {
        path: 'tab3',
        children: [
          {
            path: '',
            loadChildren: '../tab3/tab3.module#Tab3PageModule'
          }
        ]
      },
      {
        path: '',
        redirectTo: '/tabs/tab1',
        pathMatch: 'full'
      }
    ]
  },
  {
    path: '',
    redirectTo: '/tabs/tab1',
    pathMatch: 'full'
  }
];

@NgModule({
  imports: [RouterModule.forChild(routes)],
  exports: [RouterModule]
})
export class TabsPageRoutingModule {}


================================================
FILE: src/environments/environment.prod.ts
================================================
export const environment = {
  production: true
};


================================================
FILE: src/environments/environment.ts
================================================
// The file contents for the current environment will overwrite these during build.
// The build system defaults to the dev environment which uses `environment.ts`, but if you do
// `ng build --env=prod` then `environment.prod.ts` will be used instead.
// The list of which env maps to which file can be found in `.angular-cli.json`.
export const environment = {
  production: false
};

/*
 * In development mode, to ignore zone related error stack frames such as
 * `zone.run`, `zoneDelegate.invokeTask` for easier debugging, you can
 * import the following file, but please comment it out in production mode
 * because it will have performance impact when throw error
 */
// import 'zone.js/dist/zone-error';  // Included with Angular CLI.


================================================
FILE: src/global.scss
================================================
// http://ionicframework.com/docs/theming/
@import "~@ionic/angular/css/core.css";
@import "~@ionic/angular/css/normalize.css";
@import "~@ionic/angular/css/structure.css";
@import "~@ionic/angular/css/typography.css";

@import "~@ionic/angular/css/padding.css";
@import "~@ionic/angular/css/float-elements.css";
@import "~@ionic/angular/css/text-alignment.css";
@import "~@ionic/angular/css/text-transformation.css";
@import "~@ionic/angular/css/flex-utils.css";



================================================
FILE: src/index.html
================================================
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Ionic App</title>

  <base href="/" />

  <meta name="viewport" content="viewport-fit=cover, width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
  <meta name="format-detection" content="telephone=no">
  <meta name="msapplication-tap-highlight" content="no">

  <link rel="icon" type="image/png" href="assets/icon/favicon.png">

  <!-- add to homescreen for ios -->
  <meta name="apple-mobile-web-app-capable" content="yes">
  <meta name="apple-mobile-web-app-status-bar-style" content="black">

</head>
<body>
  <app-root></app-root>
</body>
</html>


================================================
FILE: src/karma.conf.js
================================================
// Karma configuration file, see link for more information
// https://karma-runner.github.io/1.0/config/configuration-file.html

module.exports = function (config) {
  config.set({
    basePath: '',
    frameworks: ['jasmine', '@angular-devkit/build-angular'],
    plugins: [
      require('karma-jasmine'),
      require('karma-chrome-launcher'),
      require('karma-jasmine-html-reporter'),
      require('karma-coverage-istanbul-reporter'),
      require('@angular-devkit/build-angular/plugins/karma')
    ],
    client: {
      clearContext: false // leave Jasmine Spec Runner output visible in browser
    },
    coverageIstanbulReporter: {
      dir: require('path').join(__dirname, '../coverage'),
      reports: ['html', 'lcovonly'],
      fixWebpackSourcePaths: true
    },
    reporters: ['progress', 'kjhtml'],
    port: 9876,
    colors: true,
    logLevel: config.LOG_INFO,
    autoWatch: true,
    browsers: ['Chrome'],
    singleRun: false
  });
};


================================================
FILE: src/main.ts
================================================
import { enableProdMode } from '@angular/core'
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'

import { AppModule } from './app/app.module'
import { environment } from './environments/environment'

if (environment.production) {
  enableProdMode()
}

platformBrowserDynamic().bootstrapModule(AppModule)
  .catch(err => console.log(err))


================================================
FILE: src/polyfills.ts
================================================
/**
 * This file includes polyfills needed by Angular and is loaded before the app.
 * You can add your own extra polyfills to this file.
 *
 * This file is divided into 2 sections:
 *   1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
 *   2. Application imports. Files imported after ZoneJS that should be loaded before your main
 *      file.
 *
 * The current setup is for so-called "evergreen" browsers; the last versions of browsers that
 * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
 * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
 *
 * Learn more in https://angular.io/guide/browser-support
 */

/***************************************************************************************************
 * BROWSER POLYFILLS
 */

/** IE9, IE10 and IE11 requires all of the following polyfills. **/
// import 'core-js/es6/symbol';
// import 'core-js/es6/object';
// import 'core-js/es6/function';
// import 'core-js/es6/parse-int';
// import 'core-js/es6/parse-float';
// import 'core-js/es6/number';
// import 'core-js/es6/math';
// import 'core-js/es6/string';
// import 'core-js/es6/date';
// import 'core-js/es6/regexp';
// import 'core-js/es6/map';
// import 'core-js/es6/weak-map';
// import 'core-js/es6/set';

/**
 * If your app need to indexed by Google Search, your app require polyfills 'core-js/es6/array'
 * Google bot use ES5.
 * FYI: Googlebot uses a renderer following the similar spec to Chrome 41.
 * https://developers.google.com/search/docs/guides/rendering
 **/
// import 'core-js/es6/array';

/** IE10 and IE11 requires the following for NgClass support on SVG elements */
// import 'classlist.js';  // Run `npm install --save classlist.js`.

/** IE10 and IE11 requires the following for the Reflect API. */
// import 'core-js/es6/reflect';


/** Evergreen browsers require these. **/
// Used for reflect-metadata in JIT. If you use AOT (and only Angular decorators), you can remove.
// import 'core-js/es7/reflect';


/**
 * Web Animations `@angular/platform-browser/animations`
 * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari.
 * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0).
 **/
// import 'web-animations-js';  // Run `npm install --save web-animations-js`.

/**
 * By default, zone.js will patch all possible macroTask and DomEvents
 * user can disable parts of macroTask/DomEvents patch by setting following flags
 */

 // (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
 // (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
 // (window as any).__zone_symbol__BLACK_LISTED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames

 /*
 * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
 * with the following flag, it will bypass `zone.js` patch for IE/Edge
 */
// (window as any).__Zone_enable_cross_context_check = true;

/***************************************************************************************************
 * Zone JS is required by default for Angular itself.
 */
import 'zone.js/dist/zone';  // Included with Angular CLI.



/***************************************************************************************************
 * APPLICATION IMPORTS
 */

(window as any).global = window;
(window as any).process = {};
(window as any).process.nextTick = setTimeout;


================================================
FILE: src/test.ts
================================================
// This file is required by karma.conf.js and loads recursively all the .spec and framework files

import 'zone.js/dist/zone-testing'
import { getTestBed } from '@angular/core/testing'
import {
  BrowserDynamicTestingModule,
  platformBrowserDynamicTesting
} from '@angular/platform-browser-dynamic/testing'

declare const require: any

// First, initialize the Angular testing environment.
getTestBed().initTestEnvironment(
  BrowserDynamicTestingModule,
  platformBrowserDynamicTesting()
)
// Then we find all the tests.
const context = require.context('./', true, /\.spec\.ts$/)
// And load the modules.
context.keys().map(context)


================================================
FILE: src/theme/variables.scss
================================================
// Ionic Variables and Theming. For more info, please see:
// http://ionicframework.com/docs/theming/

/** Ionic CSS Variables **/
:root {

  /** primary **/
  --ion-color-primary: #3880ff;
  --ion-color-primary-rgb: 56,128,255;
  --ion-color-primary-contrast: #ffffff;
  --ion-color-primary-contrast-rgb: 255,255,255;
  --ion-color-primary-shade: #3171e0;
  --ion-color-primary-tint: #4c8dff;

  /** secondary **/
  --ion-color-secondary: #0cd1e8;
  --ion-color-secondary-rgb: 12,209,232;
  --ion-color-secondary-contrast: #ffffff;
  --ion-color-secondary-contrast-rgb: 255,255,255;
  --ion-color-secondary-shade: #0bb8cc;
  --ion-color-secondary-tint: #24d6ea;

  /** tertiary **/
  --ion-color-tertiary: #7044ff;
  --ion-color-tertiary-rgb: 112,68,255;
  --ion-color-tertiary-contrast: #ffffff;
  --ion-color-tertiary-contrast-rgb: 255,255,255;
  --ion-color-tertiary-shade: #633ce0;
  --ion-color-tertiary-tint: #7e57ff;

  /** success **/
  --ion-color-success: #10dc60;
  --ion-color-success-rgb: 16,220,96;
  --ion-color-success-contrast: #ffffff;
  --ion-color-success-contrast-rgb: 255,255,255;
  --ion-color-success-shade: #0ec254;
  --ion-color-success-tint: #28e070;

  /** warning **/
  --ion-color-warning: #ffce00;
  --ion-color-warning-rgb: 255,206,0;
  --ion-color-warning-contrast: #ffffff;
  --ion-color-warning-contrast-rgb: 255,255,255;
  --ion-color-warning-shade: #e0b500;
  --ion-color-warning-tint: #ffd31a;

  /** danger **/
  --ion-color-danger: #f04141;
  --ion-color-danger-rgb: 245,61,61;
  --ion-color-danger-contrast: #ffffff;
  --ion-color-danger-contrast-rgb: 255,255,255;
  --ion-color-danger-shade: #d33939;
  --ion-color-danger-tint: #f25454;

  /** dark **/
  --ion-color-dark: #222428;
  --ion-color-dark-rgb: 34,34,34;
  --ion-color-dark-contrast: #ffffff;
  --ion-color-dark-contrast-rgb: 255,255,255;
  --ion-color-dark-shade: #1e2023;
  --ion-color-dark-tint: #383a3e;

  /** medium **/
  --ion-color-medium: #989aa2;
  --ion-color-medium-rgb: 152,154,162;
  --ion-color-medium-contrast: #ffffff;
  --ion-color-medium-contrast-rgb: 255,255,255;
  --ion-color-medium-shade: #86888f;
  --ion-color-medium-tint: #a2a4ab;

  /** light **/
  --ion-color-light: #f4f5f8;
  --ion-color-light-rgb: 244,244,244;
  --ion-color-light-contrast: #000000;
  --ion-color-light-contrast-rgb: 0,0,0;
  --ion-color-light-shade: #d7d8da;
  --ion-color-light-tint: #f5f6f9;

}


================================================
FILE: src/tsconfig.app.json
================================================
{
  "extends": "../tsconfig.json",
  "compilerOptions": {
    "outDir": "../out-tsc/app",
    "baseUrl": "./",
    "module": "es2015"
  },
  "exclude": [
    "test.ts",
    "**/*.spec.ts",
    "environments/*.ts"
  ]
}


================================================
FILE: src/tsconfig.spec.json
================================================
{
  "extends": "../tsconfig.json",
  "compilerOptions": {
    "outDir": "../out-tsc/spec",
    "baseUrl": "./",
    "module": "commonjs",
    "types": [
      "jasmine",
      "node"
    ]
  },
  "files": [
    "test.ts"
  ],
  "include": [
    "polyfills.ts",
    "**/*.spec.ts",
    "**/*.d.ts"
  ]
}


================================================
FILE: tsconfig.json
================================================
{
  "compileOnSave": false,
  "compilerOptions": {
    "outDir": "./dist/out-tsc",
    "sourceMap": true,
    "declaration": false,
    "moduleResolution": "node",
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "target": "es5",
    "lib": [
      "es2017",
      "dom"
    ]
  }
}


================================================
FILE: tslint.json
================================================
{
  "rulesDirectory": [
    "node_modules/codelyzer"
  ],
  "rules": {
    "arrow-return-shorthand": true,
    "callable-types": true,
    "class-name": true,
    "comment-format": [
      true,
      "check-space"
    ],
    "curly": true,
    "deprecation": {
      "severity": "warn"
    },
    "eofline": true,
    "forin": true,
    "import-spacing": true,
    "indent": [
      true,
      "spaces"
    ],
    "interface-over-type-literal": true,
    "label-position": true,
    "max-line-length": [
      true,
      140
    ],
    "member-access": false,
    "member-ordering": [
      true,
      {
        "order": [
          "static-field",
          "instance-field",
          "static-method",
          "instance-method"
        ]
      }
    ],
    "no-arg": true,
    "no-bitwise": true,
    "no-console": [
      true,
      "debug",
      "info",
      "time",
      "timeEnd",
      "trace"
    ],
    "no-construct": true,
    "no-debugger": true,
    "no-duplicate-super": true,
    "no-empty": false,
    "no-empty-interface": true,
    "no-eval": true,
    "no-inferrable-types": [
      true,
      "ignore-params"
    ],
    "no-misused-new": true,
    "no-non-null-assertion": true,
    "no-shadowed-variable": true,
    "no-string-literal": false,
    "no-string-throw": true,
    "no-switch-case-fall-through": true,
    "no-trailing-whitespace": true,
    "no-unnecessary-initializer": true,
    "no-unused-expression": true,
    "no-use-before-declare": true,
    "no-var-keyword": true,
    "object-literal-sort-keys": false,
    "one-line": [
      true,
      "check-open-brace",
      "check-catch",
      "check-else",
      "check-whitespace"
    ],
    "prefer-const": true,
    "quotemark": [
      true,
      "single"
    ],
    "radix": true,
    "semicolon": [
      false,
      "always"
    ],
    "triple-equals": [
      true,
      "allow-null-check"
    ],
    "typedef-whitespace": [
      true,
      {
        "call-signature": "nospace",
        "index-signature": "nospace",
        "parameter": "nospace",
        "property-declaration": "nospace",
        "variable-declaration": "nospace"
      }
    ],
    "unified-signatures": true,
    "variable-name": false,
    "whitespace": [
      true,
      "check-branch",
      "check-decl",
      "check-operator",
      "check-separator",
      "check-type"
    ],
    "directive-selector": [
      true,
      "attribute",
      "app",
      "camelCase"
    ],
    "component-selector": [
      true,
      "element",
      "app",
      "page",
      "kebab-case"
    ],
    "no-output-on-prefix": true,
    "use-input-property-decorator": true,
    "use-output-property-decorator": true,
    "use-host-property-decorator": true,
    "no-input-rename": true,
    "no-output-rename": true,
    "use-life-cycle-interface": true,
    "use-pipe-transform-interface": true,
    "directive-class-suffix": true
  }
}
Download .txt
gitextract_wfs5lkbz/

├── .editorconfig
├── .gitignore
├── README.md
├── angular.json
├── config.xml
├── e2e/
│   ├── protractor.conf.js
│   ├── src/
│   │   ├── app.e2e-spec.ts
│   │   └── app.po.ts
│   └── tsconfig.e2e.json
├── electron-builder.json
├── electron.js
├── ionic.config.json
├── package.json
├── resources/
│   ├── android/
│   │   └── xml/
│   │       └── network_security_config.xml
│   ├── electron/
│   │   ├── .gitkeep
│   │   ├── linux/
│   │   │   ├── DEBIAN/
│   │   │   │   └── control
│   │   │   └── app.desktop
│   │   ├── osx/
│   │   │   ├── dmg-icon.icns
│   │   │   └── icon.icns
│   │   ├── polyonic.psd
│   │   └── windows/
│   │       └── installer.nsi
│   ├── icon.png.md5
│   └── splash.png.md5
├── src/
│   ├── app/
│   │   ├── app-routing.module.ts
│   │   ├── app.component.html
│   │   ├── app.component.spec.ts
│   │   ├── app.component.ts
│   │   ├── app.module.ts
│   │   ├── data.service.spec.ts
│   │   ├── data.service.ts
│   │   ├── electron.service.spec.ts
│   │   ├── electron.service.ts
│   │   ├── events.service.spec.ts
│   │   ├── events.service.ts
│   │   ├── tab1/
│   │   │   ├── tab1.module.ts
│   │   │   ├── tab1.page.html
│   │   │   ├── tab1.page.scss
│   │   │   ├── tab1.page.spec.ts
│   │   │   └── tab1.page.ts
│   │   ├── tab2/
│   │   │   ├── tab2.module.ts
│   │   │   ├── tab2.page.html
│   │   │   ├── tab2.page.scss
│   │   │   ├── tab2.page.spec.ts
│   │   │   └── tab2.page.ts
│   │   ├── tab3/
│   │   │   ├── tab3.module.ts
│   │   │   ├── tab3.page.html
│   │   │   ├── tab3.page.scss
│   │   │   ├── tab3.page.spec.ts
│   │   │   └── tab3.page.ts
│   │   └── tabs/
│   │       ├── tabs.module.ts
│   │       ├── tabs.page.html
│   │       ├── tabs.page.scss
│   │       ├── tabs.page.spec.ts
│   │       ├── tabs.page.ts
│   │       └── tabs.router.module.ts
│   ├── environments/
│   │   ├── environment.prod.ts
│   │   └── environment.ts
│   ├── global.scss
│   ├── index.html
│   ├── karma.conf.js
│   ├── main.ts
│   ├── polyfills.ts
│   ├── test.ts
│   ├── theme/
│   │   └── variables.scss
│   ├── tsconfig.app.json
│   └── tsconfig.spec.json
├── tsconfig.json
└── tslint.json
Download .txt
SYMBOL INDEX (34 symbols across 18 files)

FILE: e2e/protractor.conf.js
  method onPrepare (line 22) | onPrepare() {

FILE: e2e/src/app.po.ts
  class AppPage (line 3) | class AppPage {
    method navigateTo (line 4) | navigateTo() {
    method getParagraphText (line 8) | getParagraphText() {

FILE: electron.js
  function createWindow (line 12) | function createWindow () {

FILE: src/app/app-routing.module.ts
  class AppRoutingModule (line 11) | class AppRoutingModule {}

FILE: src/app/app.component.ts
  class AppComponent (line 12) | class AppComponent {
    method constructor (line 13) | constructor(
    method initializeApp (line 22) | initializeApp() {

FILE: src/app/app.module.ts
  class AppModule (line 23) | class AppModule {}

FILE: src/app/data.service.ts
  class DataService (line 13) | class DataService {
    method constructor (line 18) | constructor(
    method setup (line 24) | public setup() {
    method mobileDB (line 35) | private mobileDB() {
    method webDB (line 67) | private webDB() {

FILE: src/app/electron.service.ts
  class ElectronService (line 9) | class ElectronService {
    method isElectron (line 16) | get isElectron(): boolean {
    method isElectronApp (line 20) | get isElectronApp(): boolean {
    method constructor (line 25) | constructor() {

FILE: src/app/events.service.ts
  class EventService (line 7) | class EventService {
    method subscribe (line 11) | subscribe(topic: string, observer: (_: any) => void): Subscription {
    method publish (line 19) | publish(topic: string, data: any): void {
    method destroy (line 28) | destroy(topic: string): null {

FILE: src/app/tab1/tab1.module.ts
  class Tab1PageModule (line 17) | class Tab1PageModule {}

FILE: src/app/tab1/tab1.page.ts
  class Tab1Page (line 11) | class Tab1Page  implements OnInit {
    method constructor (line 17) | constructor(
    method ngOnInit (line 29) | ngOnInit () {

FILE: src/app/tab2/tab2.module.ts
  class Tab2PageModule (line 17) | class Tab2PageModule {}

FILE: src/app/tab2/tab2.page.ts
  class Tab2Page (line 8) | class Tab2Page {}

FILE: src/app/tab3/tab3.module.ts
  class Tab3PageModule (line 17) | class Tab3PageModule {}

FILE: src/app/tab3/tab3.page.ts
  class Tab3Page (line 8) | class Tab3Page {}

FILE: src/app/tabs/tabs.module.ts
  class TabsPageModule (line 20) | class TabsPageModule {}

FILE: src/app/tabs/tabs.page.ts
  class TabsPage (line 8) | class TabsPage {}

FILE: src/app/tabs/tabs.router.module.ts
  class TabsPageRoutingModule (line 56) | class TabsPageRoutingModule {}
Condensed preview — 68 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (77K chars).
[
  {
    "path": ".editorconfig",
    "chars": 233,
    "preview": "# editorconfig.org\nroot = true\n\n[*]\nindent_style = space\nindent_size = 2\nend_of_line = lf\ncharset = utf-8\ntrim_trailing_"
  },
  {
    "path": ".gitignore",
    "chars": 249,
    "preview": "node_modules\n*.log\n.DS_Store\nThumbs.db\n*.swp\n*.autogenerated\n/build/*\n!/build/.gitkeep\n/releases/\n/tmp/\n/src/node_module"
  },
  {
    "path": "README.md",
    "chars": 9489,
    "preview": "# Polyonic\n## The ultimate \"Universal Web App\" \n\n[![JavaScript Style Guide](https://img.shields.io/badge/code%20style-st"
  },
  {
    "path": "angular.json",
    "chars": 5198,
    "preview": "{\n  \"$schema\": \"./node_modules/@angular-devkit/core/src/workspace/workspace-schema.json\",\n  \"version\": 1,\n  \"defaultProj"
  },
  {
    "path": "config.xml",
    "chars": 7965,
    "preview": "<?xml version='1.0' encoding='utf-8'?>\n<widget id=\"io.polyonic.starter\" version=\"0.0.1\" xmlns=\"http://www.w3.org/ns/widg"
  },
  {
    "path": "e2e/protractor.conf.js",
    "chars": 722,
    "preview": "// Protractor configuration file, see link for more information\n// https://github.com/angular/protractor/blob/master/lib"
  },
  {
    "path": "e2e/src/app.e2e-spec.ts",
    "chars": 297,
    "preview": "import { AppPage } from './app.po';\n\ndescribe('new App', () => {\n  let page: AppPage;\n\n  beforeEach(() => {\n    page = n"
  },
  {
    "path": "e2e/src/app.po.ts",
    "chars": 221,
    "preview": "import { browser, by, element } from 'protractor';\n\nexport class AppPage {\n  navigateTo() {\n    return browser.get('/');"
  },
  {
    "path": "e2e/tsconfig.e2e.json",
    "chars": 163,
    "preview": "{\n  \"extends\": \"../tsconfig.json\",\n  \"compilerOptions\": {\n    \"outDir\": \"../out-tsc/e2e\",\n    \"baseUrl\": \"./\",\n    \"modu"
  },
  {
    "path": "electron-builder.json",
    "chars": 1115,
    "preview": "{\n  \"productName\": \"Polyonic\",\n  \"directories\": {\n    \"buildResources\": \"./resources/electron\",\n    \"output\": \"release/\""
  },
  {
    "path": "electron.js",
    "chars": 2020,
    "preview": "const electron = require('electron')\nconst app = electron.app\nconst BrowserWindow = electron.BrowserWindow\nconst path = "
  },
  {
    "path": "ionic.config.json",
    "chars": 87,
    "preview": "{\n  \"name\": \"ionic5\",\n  \"integrations\": {\n    \"cordova\": {}\n  },\n  \"type\": \"angular\"\n}\n"
  },
  {
    "path": "package.json",
    "chars": 4589,
    "preview": "{\n  \"name\": \"polyonic\",\n  \"productName\": \"Polyonic App\",\n  \"description\": \"An Ionic project\",\n  \"version\": \"3.0.6\",\n  \"a"
  },
  {
    "path": "resources/android/xml/network_security_config.xml",
    "chars": 387,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<network-security-config>\n    <base-config cleartextTrafficPermitted=\"true\">\n    "
  },
  {
    "path": "resources/electron/.gitkeep",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "resources/electron/linux/DEBIAN/control",
    "chars": 155,
    "preview": "Package: {{name}}\nVersion: {{version}}\nMaintainer: {{author}}\nPriority: optional\nArchitecture: amd64\nInstalled-Size: {{s"
  },
  {
    "path": "resources/electron/linux/app.desktop",
    "chars": 220,
    "preview": "[Desktop Entry]\nVersion=1.0\nType=Application\nEncoding=UTF-8\nName={{productName}}\nComment={{description}}\nExec=/opt/{{nam"
  },
  {
    "path": "resources/electron/windows/installer.nsi",
    "chars": 4216,
    "preview": "; NSIS packaging/install script\n; Docs: http://nsis.sourceforge.net/Docs/Contents.html\n\n!include LogicLib.nsh\n!include n"
  },
  {
    "path": "resources/icon.png.md5",
    "chars": 32,
    "preview": "e495003092b862d39b09a0f1b31f732b"
  },
  {
    "path": "resources/splash.png.md5",
    "chars": 32,
    "preview": "f010d2a2420ae8b5b5fa398e77da6397"
  },
  {
    "path": "src/app/app-routing.module.ts",
    "chars": 307,
    "preview": "import { NgModule } from '@angular/core'\nimport { Routes, RouterModule } from '@angular/router'\n\nconst routes: Routes = "
  },
  {
    "path": "src/app/app.component.html",
    "chars": 63,
    "preview": "<ion-app>\n  <ion-router-outlet></ion-router-outlet>\n</ion-app>\n"
  },
  {
    "path": "src/app/app.component.spec.ts",
    "chars": 1559,
    "preview": "import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'\nimport { TestBed, async } from '@angular/core/testing'\n\nimport { "
  },
  {
    "path": "src/app/app.component.ts",
    "chars": 918,
    "preview": "import { Component } from '@angular/core'\n\nimport { Platform } from '@ionic/angular'\nimport { SplashScreen } from '@ioni"
  },
  {
    "path": "src/app/app.module.ts",
    "chars": 755,
    "preview": "import { NgModule } from '@angular/core'\nimport { BrowserModule } from '@angular/platform-browser'\nimport { RouteReuseSt"
  },
  {
    "path": "src/app/data.service.spec.ts",
    "chars": 355,
    "preview": "import { TestBed, inject } from '@angular/core/testing'\n\nimport { DataService } from './data.service'\n\ndescribe('DataSer"
  },
  {
    "path": "src/app/data.service.ts",
    "chars": 2572,
    "preview": "import { Injectable } from '@angular/core'\nimport { Platform } from '@ionic/angular'\nimport { environment } from '../env"
  },
  {
    "path": "src/app/electron.service.spec.ts",
    "chars": 367,
    "preview": "import { TestBed } from '@angular/core/testing';\n\nimport { ElectronService } from './electron.service';\n\ndescribe('Elect"
  },
  {
    "path": "src/app/electron.service.ts",
    "chars": 1016,
    "preview": "import { Injectable } from '@angular/core'\nimport { ipcRenderer, webFrame, remote } from 'electron'\nimport * as childPro"
  },
  {
    "path": "src/app/events.service.spec.ts",
    "chars": 333,
    "preview": "import { TestBed } from '@angular/core/testing';\n\nimport { EventsService } from './events.service';\n\ndescribe('EventsSer"
  },
  {
    "path": "src/app/events.service.ts",
    "chars": 857,
    "preview": "import { Injectable } from '@angular/core'\nimport { Subject, Subscription } from 'rxjs'\n\n@Injectable({\n  providedIn: 'ro"
  },
  {
    "path": "src/app/tab1/tab1.module.ts",
    "chars": 477,
    "preview": "import { IonicModule } from '@ionic/angular';\nimport { RouterModule } from '@angular/router';\nimport { NgModule } from '"
  },
  {
    "path": "src/app/tab1/tab1.page.html",
    "chars": 1293,
    "preview": "<ion-header>\n  <ion-toolbar>\n    <ion-title>\n      Tab One\n    </ion-title>\n  </ion-toolbar>\n</ion-header>\n\n<ion-content"
  },
  {
    "path": "src/app/tab1/tab1.page.scss",
    "chars": 66,
    "preview": ".welcome-card ion-img {\n  max-height: 35vh;\n  overflow: hidden;\n}\n"
  },
  {
    "path": "src/app/tab1/tab1.page.spec.ts",
    "chars": 670,
    "preview": "import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';\nimport { async, ComponentFixture, TestBed } from '@angular/core/"
  },
  {
    "path": "src/app/tab1/tab1.page.ts",
    "chars": 975,
    "preview": "import { Component, OnInit } from '@angular/core'\nimport { ElectronService } from '../electron.service'\nimport { DataSer"
  },
  {
    "path": "src/app/tab2/tab2.module.ts",
    "chars": 477,
    "preview": "import { IonicModule } from '@ionic/angular';\nimport { RouterModule } from '@angular/router';\nimport { NgModule } from '"
  },
  {
    "path": "src/app/tab2/tab2.page.html",
    "chars": 136,
    "preview": "<ion-header>\n  <ion-toolbar>\n    <ion-title>\n      Tab Two\n    </ion-title>\n  </ion-toolbar>\n</ion-header>\n\n<ion-content"
  },
  {
    "path": "src/app/tab2/tab2.page.scss",
    "chars": 1,
    "preview": "\n"
  },
  {
    "path": "src/app/tab2/tab2.page.spec.ts",
    "chars": 670,
    "preview": "import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';\nimport { async, ComponentFixture, TestBed } from '@angular/core/"
  },
  {
    "path": "src/app/tab2/tab2.page.ts",
    "chars": 174,
    "preview": "import { Component } from '@angular/core';\n\n@Component({\n  selector: 'app-tab2',\n  templateUrl: 'tab2.page.html',\n  styl"
  },
  {
    "path": "src/app/tab3/tab3.module.ts",
    "chars": 477,
    "preview": "import { IonicModule } from '@ionic/angular';\nimport { RouterModule } from '@angular/router';\nimport { NgModule } from '"
  },
  {
    "path": "src/app/tab3/tab3.page.html",
    "chars": 138,
    "preview": "<ion-header>\n  <ion-toolbar>\n    <ion-title>\n      Tab Three\n    </ion-title>\n  </ion-toolbar>\n</ion-header>\n\n<ion-conte"
  },
  {
    "path": "src/app/tab3/tab3.page.scss",
    "chars": 1,
    "preview": "\n"
  },
  {
    "path": "src/app/tab3/tab3.page.spec.ts",
    "chars": 670,
    "preview": "import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';\nimport { async, ComponentFixture, TestBed } from '@angular/core/"
  },
  {
    "path": "src/app/tab3/tab3.page.ts",
    "chars": 174,
    "preview": "import { Component } from '@angular/core';\n\n@Component({\n  selector: 'app-tab3',\n  templateUrl: 'tab3.page.html',\n  styl"
  },
  {
    "path": "src/app/tabs/tabs.module.ts",
    "chars": 504,
    "preview": "import { IonicModule } from '@ionic/angular';\nimport { RouterModule } from '@angular/router';\nimport { NgModule } from '"
  },
  {
    "path": "src/app/tabs/tabs.page.html",
    "chars": 470,
    "preview": "<ion-tabs>\n\n  <ion-tab-bar slot=\"bottom\">\n    <ion-tab-button tab=\"tab1\">\n      <ion-icon name=\"flash\"></ion-icon>\n     "
  },
  {
    "path": "src/app/tabs/tabs.page.scss",
    "chars": 1,
    "preview": "\n"
  },
  {
    "path": "src/app/tabs/tabs.page.spec.ts",
    "chars": 670,
    "preview": "import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';\nimport { async, ComponentFixture, TestBed } from '@angular/core/"
  },
  {
    "path": "src/app/tabs/tabs.page.ts",
    "chars": 174,
    "preview": "import { Component } from '@angular/core';\n\n@Component({\n  selector: 'app-tabs',\n  templateUrl: 'tabs.page.html',\n  styl"
  },
  {
    "path": "src/app/tabs/tabs.router.module.ts",
    "chars": 1068,
    "preview": "import { NgModule } from '@angular/core';\nimport { RouterModule, Routes } from '@angular/router';\n\nimport { TabsPage } f"
  },
  {
    "path": "src/environments/environment.prod.ts",
    "chars": 51,
    "preview": "export const environment = {\n  production: true\n};\n"
  },
  {
    "path": "src/environments/environment.ts",
    "chars": 742,
    "preview": "// The file contents for the current environment will overwrite these during build.\n// The build system defaults to the "
  },
  {
    "path": "src/global.scss",
    "chars": 465,
    "preview": "// http://ionicframework.com/docs/theming/\n@import \"~@ionic/angular/css/core.css\";\n@import \"~@ionic/angular/css/normaliz"
  },
  {
    "path": "src/index.html",
    "chars": 668,
    "preview": "<!doctype html>\n<html lang=\"en\">\n<head>\n  <meta charset=\"utf-8\">\n  <title>Ionic App</title>\n\n  <base href=\"/\" />\n\n  <met"
  },
  {
    "path": "src/karma.conf.js",
    "chars": 965,
    "preview": "// Karma configuration file, see link for more information\n// https://karma-runner.github.io/1.0/config/configuration-fi"
  },
  {
    "path": "src/main.ts",
    "chars": 364,
    "preview": "import { enableProdMode } from '@angular/core'\nimport { platformBrowserDynamic } from '@angular/platform-browser-dynamic"
  },
  {
    "path": "src/polyfills.ts",
    "chars": 3554,
    "preview": "/**\n * This file includes polyfills needed by Angular and is loaded before the app.\n * You can add your own extra polyfi"
  },
  {
    "path": "src/test.ts",
    "chars": 635,
    "preview": "// This file is required by karma.conf.js and loads recursively all the .spec and framework files\n\nimport 'zone.js/dist/"
  },
  {
    "path": "src/theme/variables.scss",
    "chars": 2400,
    "preview": "// Ionic Variables and Theming. For more info, please see:\n// http://ionicframework.com/docs/theming/\n\n/** Ionic CSS Var"
  },
  {
    "path": "src/tsconfig.app.json",
    "chars": 219,
    "preview": "{\n  \"extends\": \"../tsconfig.json\",\n  \"compilerOptions\": {\n    \"outDir\": \"../out-tsc/app\",\n    \"baseUrl\": \"./\",\n    \"modu"
  },
  {
    "path": "src/tsconfig.spec.json",
    "chars": 303,
    "preview": "{\n  \"extends\": \"../tsconfig.json\",\n  \"compilerOptions\": {\n    \"outDir\": \"../out-tsc/spec\",\n    \"baseUrl\": \"./\",\n    \"mod"
  },
  {
    "path": "tsconfig.json",
    "chars": 309,
    "preview": "{\n  \"compileOnSave\": false,\n  \"compilerOptions\": {\n    \"outDir\": \"./dist/out-tsc\",\n    \"sourceMap\": true,\n    \"declarati"
  },
  {
    "path": "tslint.json",
    "chars": 2916,
    "preview": "{\n  \"rulesDirectory\": [\n    \"node_modules/codelyzer\"\n  ],\n  \"rules\": {\n    \"arrow-return-shorthand\": true,\n    \"callable"
  }
]

// ... and 3 more files (download for full content)

About this extraction

This page contains the full source code of the paulsutherland/Polyonic GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 68 files (67.7 KB), approximately 19.8k tokens, and a symbol index with 34 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!