diff --git a/.eslintrc.js b/.eslintrc.js
index a5b82de..ee6effd 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -1,3 +1,3 @@
module.exports = {
- "extends": "standard"
-};
\ No newline at end of file
+ extends: "standard"
+};
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
index c1f19fc..df095fe 100644
--- a/CODE_OF_CONDUCT.md
+++ b/CODE_OF_CONDUCT.md
@@ -8,19 +8,19 @@ In the interest of fostering an open and welcoming environment, we as contributo
Examples of behavior that contributes to creating a positive environment include:
-* Using welcoming and inclusive language
-* Being respectful of differing viewpoints and experiences
-* Gracefully accepting constructive criticism
-* Focusing on what is best for the community
-* Showing empathy towards other community members
+- Using welcoming and inclusive language
+- Being respectful of differing viewpoints and experiences
+- Gracefully accepting constructive criticism
+- Focusing on what is best for the community
+- Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
-* The use of sexualized language or imagery and unwelcome sexual attention or advances
-* Trolling, insulting/derogatory comments, and personal or political attacks
-* Public or private harassment
-* Publishing others' private information, such as a physical or electronic address, without explicit permission
-* Other conduct which could reasonably be considered inappropriate in a professional setting
+- The use of sexualized language or imagery and unwelcome sexual attention or advances
+- Trolling, insulting/derogatory comments, and personal or political attacks
+- Public or private harassment
+- Publishing others' private information, such as a physical or electronic address, without explicit permission
+- Other conduct which could reasonably be considered inappropriate in a professional setting
## Our Responsibilities
diff --git a/LICENSE.md b/LICENSE.md
index b823bcc..5bf2230 100644
--- a/LICENSE.md
+++ b/LICENSE.md
@@ -1,8 +1,6 @@
-CC0 1.0 Universal
-==================
+# CC0 1.0 Universal
-Statement of Purpose
----------------------
+## Statement of Purpose
The laws of most jurisdictions throughout the world automatically confer exclusive Copyright and Related Rights (defined below) upon the creator and subsequent owner(s) (each and all, an "owner") of an original work of authorship and/or a database (each, a "Work").
@@ -11,7 +9,9 @@ Certain owners wish to permanently relinquish those rights to a Work for the pur
For these and/or other purposes and motivations, and without any expectation of additional consideration or compensation, the person associating CC0 with a Work (the "Affirmer"), to the extent that he or she is an owner of Copyright and Related Rights in the Work, voluntarily elects to apply CC0 to the Work and publicly distribute the Work under its terms, with knowledge of his or her Copyright and Related Rights in the Work and the meaning and intended legal effect of CC0 on those rights.
1. Copyright and Related Rights.
---------------------------------
+
+---
+
A Work made available under CC0 may be protected by copyright and related or neighboring rights ("Copyright and Related Rights"). Copyright and Related Rights include, but are not limited to, the following:
i. the right to reproduce, adapt, distribute, perform, display, communicate, and translate a Work;
@@ -23,15 +23,20 @@ vi. database rights (such as those arising under Directive 96/9/EC of the Europe
vii. other similar, equivalent or corresponding rights throughout the world based on applicable law or treaty, and any national implementations thereof.
2. Waiver.
------------
+
+---
+
To the greatest extent permitted by, but not in contravention of, applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and unconditionally waives, abandons, and surrenders all of Affirmer's Copyright and Related Rights and associated claims and causes of action, whether now known or unknown (including existing as well as future claims and causes of action), in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each member of the public at large and to the detriment of Affirmer's heirs and successors, fully intending that such Waiver shall not be subject to revocation, rescission, cancellation, termination, or any other legal or equitable action to disrupt the quiet enjoyment of the Work by the public as contemplated by Affirmer's express Statement of Purpose.
3. Public License Fallback.
-----------------------------
+
+---
+
Should any part of the Waiver for any reason be judged legally invalid or ineffective under applicable law, then the Waiver shall be preserved to the maximum extent permitted taking into account Affirmer's express Statement of Purpose. In addition, to the extent the Waiver is so judged Affirmer hereby grants to each affected person a royalty-free, non transferable, non sublicensable, non exclusive, irrevocable and unconditional license to exercise Affirmer's Copyright and Related Rights in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "License"). The License shall be deemed effective as of the date CC0 was applied by Affirmer to the Work. Should any part of the License for any reason be judged legally invalid or ineffective under applicable law, such partial invalidity or ineffectiveness shall not invalidate the remainder of the License, and in such case Affirmer hereby affirms that he or she will not (i) exercise any of his or her remaining Copyright and Related Rights in the Work or (ii) assert any associated claims and causes of action with respect to the Work, in either case contrary to Affirmer's express Statement of Purpose.
4. Limitations and Disclaimers.
---------------------------------
+
+---
a. No trademark or patent rights held by Affirmer are waived, abandoned, surrendered, licensed or otherwise affected by this document.
b. Affirmer offers the Work as-is and makes no representations or warranties of any kind concerning the Work, express, implied, statutory or otherwise, including without limitation warranties of title, merchantability, fitness for a particular purpose, non infringement, or the absence of latent or other defects, accuracy, or the present or absence of errors, whether or not discoverable, all to the greatest extent permissible under applicable law.
diff --git a/README.md b/README.md
index 21da3d5..deb63eb 100644
--- a/README.md
+++ b/README.md
@@ -1,36 +1,59 @@
# H2
-Inspired by the Helium app, **H2** is a minimalist browser to watch embeded videos and more in picture-in-picture mode.
+
+Inspired by the Helium app, **H2** is a minimalist browser to watch embeded videos and more in picture-in-picture mode.
+
# Features
+
:tv: Works with any Youtube URL.
:scroll: Works with PDFs links, Google Docs URLs.
:computer: Works on all platforms, thanks to electron ❤️
-:clock9:[WIP] After [#22](https://github.com/poush/H2/pull/22) a basic image editor, many other features coming soon.
+:art: Image editor supports contrast, brightness, hue, sepia and save as PNG.
+
+:clock9:[WIP] Many other features coming soon.
+
# Example
+

+
## Installation
-* [Windows](docs/windows.md)
-* [Mac](docs/mac.md)
-* [Linux](docs/linux.md)
+
+- [Windows](docs/windows.md)
+- [Mac](docs/mac.md)
+- [Linux](docs/linux.md)
+
## Usage
-* Copy any youtube, PDF or google docs URL and press `Ctrl/Command + Shift + V`
-* [Keyboards Shortcuts](docs/shortcuts.md)
+
+- Copy any youtube, PDF or google docs URL and press `Ctrl/Command + Shift + V`
+- [Keyboards Shortcuts](docs/shortcuts.md)
+
## Contributing
+
```bash
# Clone this repository
+
git clone https://github.com/poush/h2
+
# Go into the repository
+
cd h2
+
# Install dependencies
+
npm install
+
# Run the app
+
npm start
```
+
## Bug
+
[Known Bugs](https://github.com/poush/H2/issues?q=is%3Aopen+is%3Aissue+label%3Abug)
-## Credits
+
## License
+
[CC0-1.0](https://github.com/poush/H2/blob/master/LICENSE.md)
diff --git a/ServiceProviders/MediaProviders/baseMediaProvider.js b/ServiceProviders/MediaProviders/baseMediaProvider.js
index b4fe110..ef73f3c 100644
--- a/ServiceProviders/MediaProviders/baseMediaProvider.js
+++ b/ServiceProviders/MediaProviders/baseMediaProvider.js
@@ -1,50 +1,46 @@
class baseMediaProvider {
-
- constructor() {
- this.version = '0.1'
- this.name = 'base'
- this.response = {
- eventName: 'default',
- type: 'link',
- content: ''
- }
- }
-
- set text(link) {
- this.extractContents(link)
- }
-
- get content() {
- if(this.response.content != '' || this.response.link != undefined){
- // console.log(this.response)
- return this
- }
-
- throw "InvalidContentByContentExtraction"
- }
-
- matcher(link) {
-
- }
-
- //method
- extractContents(link) {
- if (link != undefined || link != '') {
- this.response.content = link
- }
-
- return true
- }
-
- postWinLoad(win){
- return true
- }
-
- preWinLoad(win) {
- //must return true
- return true
- }
-
+ constructor() {
+ this.version = "0.1";
+ this.name = "base";
+ this.response = {
+ eventName: "default",
+ type: "link",
+ content: ""
+ };
+ }
+
+ set text(link) {
+ this.extractContents(link);
+ }
+
+ get content() {
+ if (this.response.content != "" || this.response.link != undefined) {
+ // console.log(this.response)
+ return this;
+ }
+
+ throw "InvalidContentByContentExtraction";
+ }
+
+ matcher(link) {}
+
+ //method
+ extractContents(link) {
+ if (link != undefined || link != "") {
+ this.response.content = link;
+ }
+
+ return true;
+ }
+
+ postWinLoad(win) {
+ return true;
+ }
+
+ preWinLoad(win) {
+ //must return true
+ return true;
+ }
}
-module.exports = baseMediaProvider
\ No newline at end of file
+module.exports = baseMediaProvider;
diff --git a/ServiceProviders/MediaProviders/docs.js b/ServiceProviders/MediaProviders/docs.js
index 6eff392..b3dd892 100644
--- a/ServiceProviders/MediaProviders/docs.js
+++ b/ServiceProviders/MediaProviders/docs.js
@@ -1,36 +1,32 @@
-const baseMediaProvider = require('./baseMediaProvider')
+const baseMediaProvider = require("./baseMediaProvider");
class docsProvider extends baseMediaProvider {
-
- constructor(){
- super()
- this.name = 'googleDocs'
- this.response.eventName = 'googleDocs'
- this.response.type = 'iframe'
- }
-
- matcher(link){
- const regex = /https:\/\/docs.google.com\/document\/d\/[0-9A-Za-z]+/gm;
- var match = regex.exec(link)
- if(match)
- return match;
-
- return false;
- }
-
- extractContents(link) {
- if (link != undefined || link != '') {
- this.response.content = link
- }
-
- return true
-
- }
-
- postWinLoad(win){
- return true
- }
-
+ constructor() {
+ super();
+ this.name = "googleDocs";
+ this.response.eventName = "googleDocs";
+ this.response.type = "iframe";
+ }
+
+ matcher(link) {
+ const regex = /https:\/\/docs.google.com\/document\/d\/[0-9A-Za-z]+/gm;
+ var match = regex.exec(link);
+ if (match) return match;
+
+ return false;
+ }
+
+ extractContents(link) {
+ if (link != undefined || link != "") {
+ this.response.content = link;
+ }
+
+ return true;
+ }
+
+ postWinLoad(win) {
+ return true;
+ }
}
-module.exports = docsProvider
\ No newline at end of file
+module.exports = docsProvider;
diff --git a/ServiceProviders/MediaProviders/pdf.js b/ServiceProviders/MediaProviders/pdf.js
index ee7bfda..6599925 100644
--- a/ServiceProviders/MediaProviders/pdf.js
+++ b/ServiceProviders/MediaProviders/pdf.js
@@ -1,36 +1,31 @@
-const baseMediaProvider = require('./baseMediaProvider')
+const baseMediaProvider = require("./baseMediaProvider");
class pdfProvider extends baseMediaProvider {
-
- constructor(){
- super()
- this.name = 'pdf'
- this.response.type = 'link'
- }
-
- matcher(link){
- const regex = /(https?:\/\/\S*?\.pdf)/gm;
- var match = regex.exec(link)
- if(match)
- return match;
-
- return false;
- }
-
- extractContents(link) {
-
- if (link != undefined || link != '') {
- this.response.content = link
- }
-
- return true
-
- }
-
- postWinLoad(win){
- return true
- }
-
+ constructor() {
+ super();
+ this.name = "pdf";
+ this.response.type = "link";
+ }
+
+ matcher(link) {
+ const regex = /(https?:\/\/\S*?\.pdf)/gm;
+ var match = regex.exec(link);
+ if (match) return match;
+
+ return false;
+ }
+
+ extractContents(link) {
+ if (link != undefined || link != "") {
+ this.response.content = link;
+ }
+
+ return true;
+ }
+
+ postWinLoad(win) {
+ return true;
+ }
}
-module.exports = pdfProvider
\ No newline at end of file
+module.exports = pdfProvider;
diff --git a/ServiceProviders/MediaProviders/youtube.js b/ServiceProviders/MediaProviders/youtube.js
index fe644d5..b4e6efe 100644
--- a/ServiceProviders/MediaProviders/youtube.js
+++ b/ServiceProviders/MediaProviders/youtube.js
@@ -1,37 +1,33 @@
-const baseMediaProvider = require('./baseMediaProvider')
+const baseMediaProvider = require("./baseMediaProvider");
class youtubeProvider extends baseMediaProvider {
-
- constructor(){
- super()
- this.name = 'youtube'
- this.response.type = 'iframe'
- this.response.eventName = 'youtube'
- }
-
- matcher(link){
-
- if (link != undefined || link != '') {
- var regExp = /^.*(youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=|\&v=|\?v=)([^#\&\?]*).*/;
- var match = link.match(regExp);
- if (match && match[2].length == 11) {
- return match[2]
- }
- }
-
- return false;
-
- }
-
- extractContents(link) {
- let match = this.matcher(link)
- this.response.content = match
- // let web = 'https://www.youtube.com/embed/' + match + '?autoplay=1&enablejsapi=1'
- // this.response.content = ``
-
- return true
- }
-
+ constructor() {
+ super();
+ this.name = "youtube";
+ this.response.type = "iframe";
+ this.response.eventName = "youtube";
+ }
+
+ matcher(link) {
+ if (link != undefined || link != "") {
+ var regExp = /^.*(youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=|\&v=|\?v=)([^#\&\?]*).*/;
+ var match = link.match(regExp);
+ if (match && match[2].length == 11) {
+ return match[2];
+ }
+ }
+
+ return false;
+ }
+
+ extractContents(link) {
+ let match = this.matcher(link);
+ this.response.content = match;
+ // let web = 'https://www.youtube.com/embed/' + match + '?autoplay=1&enablejsapi=1'
+ // this.response.content = ``
+
+ return true;
+ }
}
-module.exports = youtubeProvider
\ No newline at end of file
+module.exports = youtubeProvider;
diff --git a/ServiceProviders/mediaProviderApplier.js b/ServiceProviders/mediaProviderApplier.js
index 4276bc8..712f4c6 100644
--- a/ServiceProviders/mediaProviderApplier.js
+++ b/ServiceProviders/mediaProviderApplier.js
@@ -1,22 +1,20 @@
-const baseMediaProvider = require('./MediaProviders/baseMediaProvider')
+const baseMediaProvider = require("./MediaProviders/baseMediaProvider");
-module.exports = function (toApply, win){
+module.exports = function(toApply, win) {
+ if (toApply instanceof baseMediaProvider) {
+ if (toApply.response.type === "iframe") {
+ win.loadFile("./index.html");
- if(toApply instanceof baseMediaProvider){
-
- if(toApply.response.type === 'iframe'){
- win.loadFile('./index.html')
-
- win.webContents.once('dom-ready', () => {
- win.webContents.send(toApply.response.eventName, toApply.response.content)
- })
- }
-
- else {
- toApply.preWinLoad(win)
- win.loadURL(toApply.response.content)
- toApply.postWinLoad(win)
- }
-
- }
-}
\ No newline at end of file
+ win.webContents.once("dom-ready", () => {
+ win.webContents.send(
+ toApply.response.eventName,
+ toApply.response.content
+ );
+ });
+ } else {
+ toApply.preWinLoad(win);
+ win.loadURL(toApply.response.content);
+ toApply.postWinLoad(win);
+ }
+ }
+};
diff --git a/ServiceProviders/providers.js b/ServiceProviders/providers.js
index 0f611de..f17b69f 100644
--- a/ServiceProviders/providers.js
+++ b/ServiceProviders/providers.js
@@ -1,43 +1,38 @@
-const youtubeProvider = require('./MediaProviders/youtube')
-const pdfProvider = require('./MediaProviders/pdf')
-const docsProvider = require('./MediaProviders/docs')
-const applyMedia = require('./mediaProviderApplier')
-const {clipboard} = require('electron')
-
+const youtubeProvider = require("./MediaProviders/youtube");
+const pdfProvider = require("./MediaProviders/pdf");
+const docsProvider = require("./MediaProviders/docs");
+const applyMedia = require("./mediaProviderApplier");
+const { clipboard } = require("electron");
let matchers = {
-
- 'youtube' : new youtubeProvider(),
- 'pdf': new pdfProvider(),
- 'docs': new docsProvider(),
-
-}
+ youtube: new youtubeProvider(),
+ pdf: new pdfProvider(),
+ docs: new docsProvider()
+};
module.exports = {
-
- run(win) {
-
- let text = clipboard.readText('selection');
-
- let provider = null
- for(let key in matchers){
- if(matchers[key].matcher(text) !== false){
- provider = key
- break;
- }
- }
-
- if(provider == null){
- win.webContents.send('invalidUrl', 'ping')
- return
- }
-
- matchers[provider].text = text;
-
- if(arguments[1]) {
- matchers[provider].text = arguments[1];
- }
-
- applyMedia(matchers[provider].content, win)
- }
-}
\ No newline at end of file
+ run(win) {
+ let text = clipboard.readText("selection");
+
+ let provider = null;
+ for (let key in matchers) {
+ if (matchers[key].matcher(text) !== false) {
+ provider = key;
+ break;
+ }
+ }
+
+ if (provider == null) {
+ win.webContents.send("invalidUrl", "ping");
+ return;
+ }
+
+ matchers[provider].text = text;
+
+ if (arguments[1]) {
+ matchers[provider].text = arguments[1];
+ }
+
+ applyMedia(matchers[provider].content, win);
+ }
+};
diff --git a/assets/.DS_Store b/assets/.DS_Store
new file mode 100644
index 0000000..4e58249
Binary files /dev/null and b/assets/.DS_Store differ
diff --git a/assets/css/main.css b/assets/css/main.css
new file mode 100644
index 0000000..16b9193
--- /dev/null
+++ b/assets/css/main.css
@@ -0,0 +1,396 @@
+:root {
+ --contrast: 100%;
+ --brightness: 100%;
+ --hue-rotate: 0deg;
+ --sepia: 0%;
+ --accent: #4a90e2;
+ --dark-accent: #3f7ac0;
+ --gray: #e6e7f0;
+ --dark-gray: #4e5056;
+}
+
+* {
+ padding: 0;
+ margin: 0;
+}
+
+body {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ height: 100vh;
+ width: 100vw;
+ background-image: linear-gradient(0deg, #ebfcff 0%, #ffffff 89%);
+ font-family: Roboto, sans-serif;
+}
+
+.drop-it-hot {
+ background-color: white;
+ width: 100%;
+ height: 100%;
+ position: relative;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ box-shadow: 0 8px 15px 0 rgba(0, 0, 0, 0.1);
+ overflow: hidden;
+ align-items: flex-start;
+}
+
+.drop-it-hot:after {
+ content: "";
+ position: absolute;
+ width: calc(100% - 20px);
+ height: calc(100% - 20px);
+ z-index: 0;
+}
+
+.info {
+ display: flex;
+ align-items: center;
+ width: 100%;
+ align-items: center;
+ justify-content: center;
+}
+
+.info p {
+ padding: 15px;
+ text-align: center;
+}
+
+.info.heading {
+ flex-direction: column;
+ margin-top: 5vh;
+ max-width: 400px;
+ align-self: flex-start;
+}
+
+.info.drag {
+ margin-top: 82vh;
+ max-width: 400px;
+ position: absolute;
+}
+
+.choose-files {
+ align-self: flex-end;
+ z-index: 50;
+ display: none;
+}
+
+.drop-it-hot .circle {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ border-radius: 50%;
+ position: absolute;
+ color: var(--accent);
+ background-color: white;
+ border: 3px solid var(--gray);
+ width: 100px;
+ height: 100px;
+ margin-top: 45vh;
+}
+
+.circle {
+ transition: transform 150ms ease-in;
+ z-index: 10;
+}
+.circle svg {
+ width: 40px;
+ height: 40px;
+}
+.circle:before {
+ content: "";
+ background-color: var(--accent);
+ width: 130px;
+ height: 130px;
+ border-radius: 50%;
+ position: absolute;
+ opacity: 0;
+ transition: transform 250ms ease-in, opacity 200ms ease-in;
+ z-index: 0;
+}
+.circle:after {
+ content: "";
+ position: absolute;
+ width: 100px;
+ height: 100px;
+ border-radius: 50%;
+}
+.circle:hover {
+ transform: scale(1.2);
+ opacity: 0.9;
+}
+.circle:hover:before {
+ transform: scale(8);
+ opacity: 1;
+}
+.circle:hover:after {
+ border: 3px solid white;
+}
+.circle:hover svg {
+ color: white;
+ z-index: 1;
+}
+
+.highlight:before {
+ transform: scale(8);
+ opacity: 1;
+}
+.highlight:after {
+ border: 3px solid white;
+}
+.highlight svg {
+ color: white;
+ z-index: 1;
+}
+
+#gallery {
+ position: absolute;
+ height: 90%;
+ top: 0;
+ left: 0;
+ z-index: 20;
+ width: 100%;
+}
+
+canvas {
+ min-width: 300px;
+ width: 100%;
+ height: auto;
+}
+
+.download a img {
+ max-width: 25px;
+ margin-top: 5px;
+}
+
+#gallery img {
+ display: none;
+ background-repeat: no-repeat;
+ background-size: cover;
+ object-fit: cover;
+ filter: contrast(var(--contrast)) brightness(var(--brightness))
+ sepia(var(--sepia)) hue-rotate(var(--hue-rotate));
+}
+
+.editor {
+ display: flex;
+ flex-wrap: wrap;
+ justify-content: space-between;
+ width: 100%;
+ padding: 20px;
+ margin: auto;
+ box-sizing: border-box;
+ background-color: white;
+ z-index: 100;
+ bottom: 0px;
+ position: fixed;
+ align-items: flex-start;
+ transform: translateY(50px);
+ opacity: 0;
+}
+
+.is-visible {
+ transition: transform 500ms ease-in-out, opacity 500ms ease-in-out;
+ opacity: 1;
+ transform: translateY(0);
+}
+
+.range-wrapper {
+ display: flex;
+ flex-direction: column;
+ width: 20%;
+}
+
+.range {
+ -webkit-appearance: none;
+ height: 2px;
+ border-radius: 5px;
+ background: var(--gray);
+ outline: none;
+ padding: 0;
+ margin: 10px 0;
+}
+
+::-moz-range-track {
+ background: var(--gray);
+ border: 0;
+}
+
+input::-moz-focus-inner,
+input::-moz-focus-outer {
+ border: 0;
+}
+
+input[type="range"]::-webkit-slider-thumb {
+ -webkit-appearance: none;
+ height: 12px;
+ width: 12px;
+ border-radius: 8px;
+ background-color: var(--accent);
+ cursor: pointer;
+}
+
+input[type="range"]::-moz-range-thumb {
+ -webkit-appearance: none;
+ height: 12px;
+ width: 12px;
+ border-radius: 8px;
+ background-color: var(--accent);
+ cursor: pointer;
+}
+
+input[type="range"]::-ms-thumb {
+ -webkit-appearance: none;
+ height: 12px;
+ width: 12px;
+ border-radius: 8px;
+ background-color: var(--accent);
+ cursor: pointer;
+}
+.loading {
+ position: fixed;
+ z-index: 99;
+ height: 2em;
+ width: 2em;
+ overflow: show;
+ margin: auto;
+ top: 0;
+ left: 0;
+ bottom: 0;
+ right: 0;
+}
+
+/* Transparent Overlay */
+.loading:before {
+ content: "";
+ display: block;
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background: radial-gradient(rgba(20, 20, 20, 0.8), rgba(0, 0, 0, 0.8));
+
+ background: -webkit-radial-gradient(
+ rgba(20, 20, 20, 0.8),
+ rgba(0, 0, 0, 0.8)
+ );
+}
+
+/* :not(:required) hides these rules from IE9 and below */
+.loading:not(:required) {
+ /* hide "loading..." text */
+ font: 0/0 a;
+ color: transparent;
+ text-shadow: none;
+ background-color: transparent;
+ border: 0;
+}
+
+.loading:not(:required):after {
+ content: "";
+ display: block;
+ font-size: 10px;
+ width: 1em;
+ height: 1em;
+ margin-top: -0.5em;
+ -webkit-animation: spinner 1500ms infinite linear;
+ -moz-animation: spinner 1500ms infinite linear;
+ -ms-animation: spinner 1500ms infinite linear;
+ -o-animation: spinner 1500ms infinite linear;
+ animation: spinner 1500ms infinite linear;
+ border-radius: 0.5em;
+ -webkit-box-shadow: rgba(255, 255, 255, 0.75) 1.5em 0 0 0,
+ rgba(255, 255, 255, 0.75) 1.1em 1.1em 0 0,
+ rgba(255, 255, 255, 0.75) 0 1.5em 0 0,
+ rgba(255, 255, 255, 0.75) -1.1em 1.1em 0 0,
+ rgba(255, 255, 255, 0.75) -1.5em 0 0 0,
+ rgba(255, 255, 255, 0.75) -1.1em -1.1em 0 0,
+ rgba(255, 255, 255, 0.75) 0 -1.5em 0 0,
+ rgba(255, 255, 255, 0.75) 1.1em -1.1em 0 0;
+ box-shadow: rgba(255, 255, 255, 0.75) 1.5em 0 0 0,
+ rgba(255, 255, 255, 0.75) 1.1em 1.1em 0 0,
+ rgba(255, 255, 255, 0.75) 0 1.5em 0 0,
+ rgba(255, 255, 255, 0.75) -1.1em 1.1em 0 0,
+ rgba(255, 255, 255, 0.75) -1.5em 0 0 0,
+ rgba(255, 255, 255, 0.75) -1.1em -1.1em 0 0,
+ rgba(255, 255, 255, 0.75) 0 -1.5em 0 0,
+ rgba(255, 255, 255, 0.75) 1.1em -1.1em 0 0;
+}
+
+/* Animation */
+
+@-webkit-keyframes spinner {
+ 0% {
+ -webkit-transform: rotate(0deg);
+ -moz-transform: rotate(0deg);
+ -ms-transform: rotate(0deg);
+ -o-transform: rotate(0deg);
+ transform: rotate(0deg);
+ }
+ 100% {
+ -webkit-transform: rotate(360deg);
+ -moz-transform: rotate(360deg);
+ -ms-transform: rotate(360deg);
+ -o-transform: rotate(360deg);
+ transform: rotate(360deg);
+ }
+}
+@-moz-keyframes spinner {
+ 0% {
+ -webkit-transform: rotate(0deg);
+ -moz-transform: rotate(0deg);
+ -ms-transform: rotate(0deg);
+ -o-transform: rotate(0deg);
+ transform: rotate(0deg);
+ }
+ 100% {
+ -webkit-transform: rotate(360deg);
+ -moz-transform: rotate(360deg);
+ -ms-transform: rotate(360deg);
+ -o-transform: rotate(360deg);
+ transform: rotate(360deg);
+ }
+}
+@-o-keyframes spinner {
+ 0% {
+ -webkit-transform: rotate(0deg);
+ -moz-transform: rotate(0deg);
+ -ms-transform: rotate(0deg);
+ -o-transform: rotate(0deg);
+ transform: rotate(0deg);
+ }
+ 100% {
+ -webkit-transform: rotate(360deg);
+ -moz-transform: rotate(360deg);
+ -ms-transform: rotate(360deg);
+ -o-transform: rotate(360deg);
+ transform: rotate(360deg);
+ }
+}
+@keyframes spinner {
+ 0% {
+ -webkit-transform: rotate(0deg);
+ -moz-transform: rotate(0deg);
+ -ms-transform: rotate(0deg);
+ -o-transform: rotate(0deg);
+ transform: rotate(0deg);
+ }
+ 100% {
+ -webkit-transform: rotate(360deg);
+ -moz-transform: rotate(360deg);
+ -ms-transform: rotate(360deg);
+ -o-transform: rotate(360deg);
+ transform: rotate(360deg);
+ }
+}
+#h2-icon {
+ height: 22px;
+ width: 23px;
+}
+#h2-icon .white {
+ fill: #ffffff;
+}
diff --git a/assets/js/dragAndDrop.js b/assets/js/dragAndDrop.js
new file mode 100644
index 0000000..adceef5
--- /dev/null
+++ b/assets/js/dragAndDrop.js
@@ -0,0 +1,131 @@
+const dragEvents = ["dragenter", "dragover", "dragleave", "drop"];
+const dragHighlight = ["dragenter", "dragover"];
+const dragUnighlight = ["dragleave", "drop"];
+const inputRange = document.querySelectorAll(".editor input");
+
+const dropArea = document.getElementById("drop-area");
+const iconElement = document.querySelector(".circle");
+const editorElement = document.querySelector(".editor");
+
+const preventDefaults = e => {
+ e.preventDefault();
+ e.stopPropagation();
+};
+
+const highlight = () => {
+ iconElement.classList.add("highlight");
+};
+
+const unhighlight = () => {
+ iconElement.classList.remove("highlight");
+};
+
+// Preview file
+const previewFile = file => {
+ document.getElementById("gallery").innerHTML = "";
+ document.getElementById("heading").setAttribute("style", "opacity:0;");
+ document.getElementById("circle").setAttribute("style", "opacity:0;");
+ document.getElementById("drag").setAttribute("style", "opacity:0;");
+ let reader = new FileReader();
+ reader.readAsDataURL(file);
+ reader.onloadend = function() {
+ let img = document.createElement("img");
+ img.src = reader.result;
+ img.setAttribute("id", "uploadedImage");
+ document.getElementById("gallery").appendChild(img);
+ editorElement.classList.add("is-visible");
+ img.onload = drawCanvas;
+ };
+
+ function drawCanvas() {
+ // Create an out of view canvas
+ var canvas = document.querySelector("canvas"),
+ ctx = canvas.getContext("2d");
+ // Set canvas dimensions to original image
+ canvas.width = this.width;
+ canvas.height = this.height;
+ ctx.drawImage(this, 0, 0);
+ saveImage();
+ }
+};
+
+// Setup to handle multiple files
+// For now, keep as single file upload until I can do testing
+const handleFiles = files => {
+ files = [...files];
+ files.forEach(previewFile);
+};
+// Drag and drop file(s) - Note: Keep as one file input for now.
+const handleDrop = e => {
+ let dt = e.dataTransfer;
+ let files = dt.files;
+ const filetype = files[0].type.split("/")[0];
+ if (filetype !== "image") {
+ alert("Sorry, you can only drag and drop images.");
+ } else {
+ handleFiles(files);
+ }
+};
+
+// Editor controls
+function handleUpdate() {
+ const suffix = this.dataset.unit;
+ document.documentElement.style.setProperty(
+ `--${this.name}`,
+ this.value + suffix
+ );
+}
+
+// Save image
+function saveImage() {
+ var canvas = document.querySelector("canvas");
+
+ // Set download link to save image
+ var link = document.getElementById("downloadImage");
+ link.setAttribute("download", "h2-image.png");
+ link.setAttribute(
+ "href",
+ canvas.toDataURL("image/png").replace("image/png", "image/octet-stream")
+ );
+}
+
+document.getElementById("downloadImage").onclick = function() {
+ location.reload();
+};
+
+inputRange.forEach(input => input.addEventListener("change", handleUpdate));
+inputRange.forEach(input => input.addEventListener("mousemove", handleUpdate));
+
+inputRange.forEach(input => input.addEventListener("change", updateCanvas));
+function updateCanvas() {
+ // Create an out of view canvas
+ const img = document.getElementById("uploadedImage");
+ const canvas = document.querySelector("canvas"),
+ ctx = canvas.getContext("2d");
+ // Get filter values
+ let filterValues = document.documentElement.style.cssText;
+ // Replace values to canvas filter CSS
+ filterValues = filterValues
+ .replace(/--/g, "")
+ .replace(/:/g, "(")
+ .replace(/;/g, ")");
+ // Apply filters
+ ctx.filter = filterValues;
+ // Update canvas and save image href
+ ctx.drawImage(img, 0, 0);
+ saveImage();
+}
+
+dragEvents.forEach(eventName => {
+ dropArea.addEventListener(eventName, preventDefaults, false);
+});
+
+dragHighlight.forEach(eventName => {
+ dropArea.addEventListener(eventName, highlight, false);
+});
+
+dragUnighlight.forEach(eventName => {
+ dropArea.addEventListener(eventName, unhighlight, false);
+});
+
+dropArea.addEventListener("drop", handleDrop, false);
diff --git a/assets/js/waitForLoader.js b/assets/js/waitForLoader.js
new file mode 100644
index 0000000..89c9489
--- /dev/null
+++ b/assets/js/waitForLoader.js
@@ -0,0 +1,29 @@
+const waitForLoader = (DOMSelector, MAX_TIME = 10000) => {
+ let timeout = 0;
+
+ const waitForContainerElement = (resolve, reject) => {
+ const container = document.querySelector(DOMSelector);
+ timeout += 30;
+ if (timeout >= MAX_TIME) reject("Element not found");
+ if (!container || container.length === 0) {
+ setTimeout(waitForContainerElement.bind(this, resolve, reject), 30);
+ } else {
+ resolve(container);
+ }
+ };
+ return new Promise((resolve, reject) => {
+ waitForContainerElement(resolve, reject);
+ });
+};
+let loader = document.createElement("div");
+loader.setAttribute("id", "loading");
+loader.setAttribute("class", "loading");
+loader.innerHTML = "Loading…";
+document.body.appendChild(loader);
+waitForLoader("#circle")
+ .then(res => {
+ document.getElementById("loading").remove();
+ })
+ .catch(err => {
+ alert("Something went wrong, you may need to reinstall H2.");
+ });
diff --git a/docs/linux.md b/docs/linux.md
index 32e2e96..2f30cf5 100644
--- a/docs/linux.md
+++ b/docs/linux.md
@@ -6,9 +6,9 @@ Follow the steps from below
### Shell script installation
-* For first time install clone the repo https://github.com/poush/h2.git
-* Run the shell script `cd h2/install`
-* `sh ./linux.sh`
+- For first time install clone the repo https://github.com/poush/h2.git
+- Run the shell script `cd h2/install`
+- `sh ./linux.sh`
Once started we will be default window like below
@@ -18,15 +18,14 @@ Paste any youtube url on opened window using `cmd + shift + v`

-
Move the window to any place!
-

### Problems on Ubuntu 18.04
On Ubuntu 18.04 this error message can be displayed
+
```
error while loading shared libraries: libgconf-2.so.4: cannot open shared object file: No such file or directory
npm ERR! file sh
diff --git a/docs/mac.md b/docs/mac.md
index af1c088..663845f 100644
--- a/docs/mac.md
+++ b/docs/mac.md
@@ -6,9 +6,9 @@ Follow the steps from below
### Shell script installation
-* For first time install clone the repo https://github.com/poush/h2.git
-* Run the shell script `cd h2/install`
-* `sh ./mac.sh`
+- For first time install clone the repo https://github.com/poush/h2.git
+- Run the shell script `cd h2/install`
+- `sh ./mac.sh`
Once started we will be default window like below
@@ -18,11 +18,6 @@ Paste any youtube url on opened window using `cmd + shift + v`

-
Move the window to any place!
-

-
-
-
diff --git a/docs/windows.md b/docs/windows.md
index dc894d4..61f956a 100644
--- a/docs/windows.md
+++ b/docs/windows.md
@@ -1,49 +1,62 @@
# Installation instructions for Windows systems
+
These instructions have been tested on Windows 10 1803 x64
# Install Node.js
- - Go to https://nodejs.org
- - Download the "Current" version
- - If the homepage doesn't show a download link get it from the "Downloads" section (https://nodejs.org/en/download/current/)
- - Run the .msi package and complete the install
- - Verify the installation by opening Powershell and typing `node --version` and hitting Enter. The version number of your node installation is shown in the next line
-
+
+- Go to https://nodejs.org
+- Download the "Current" version
+ - If the homepage doesn't show a download link get it from the "Downloads" section (https://nodejs.org/en/download/current/)
+- Run the .msi package and complete the install
+- Verify the installation by opening Powershell and typing `node --version` and hitting Enter. The version number of your node installation is shown in the next line
+
# Install h2
-## Cloning from Github
- - Clone this repository
+
+## Cloning from Github
+
+- Clone this repository
+
```
git clone https://github.com/poush/h2`
```
- - Open Powershell
- - Change to the folder containing the cloned version
- - Install dependencies - this might take a few minutes
+
+- Open Powershell
+- Change to the folder containing the cloned version
+- Install dependencies - this might take a few minutes
+
```
npm install
```
## Using the zip file from Github
- - Save the zip to your disk
- - Unzip it to the desired target location
- - Open Powershell
- - Change to the folder containing the cloned version (__h2-master__)
- - Install dependencies - this might take a few minutes
+
+- Save the zip to your disk
+- Unzip it to the desired target location
+- Open Powershell
+- Change to the folder containing the cloned version (**h2-master**)
+- Install dependencies - this might take a few minutes
+
```
npm install --dev
```
# Run h2
+
After the installation is complete stay in the h2 directory and type
+
```
nmp start
```
# Play a video
- - Once the program has started you see the H2 window on top containing the text
+
+- Once the program has started you see the H2 window on top containing the text
+
```
Hello People!
Start by pasting any youtube url. For bugs, you can create issues at github.com/poush/h2
```
- - Open a video on youtube using your browser, copy its URL
- - Paste the URL into the H2 window using `++V` and the video automatically starts.
-
+
+- Open a video on youtube using your browser, copy its URL
+- Paste the URL into the H2 window using `++V` and the video automatically starts.
diff --git a/icons/H2Icon.svg b/icons/H2Icon.svg
new file mode 100644
index 0000000..2870bef
--- /dev/null
+++ b/icons/H2Icon.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/img/download.png b/img/download.png
new file mode 100644
index 0000000..4c52034
Binary files /dev/null and b/img/download.png differ
diff --git a/index.html b/index.html
index 8850aa7..afbfd99 100644
--- a/index.html
+++ b/index.html
@@ -1,21 +1,64 @@
-
-
- H2
-
-
-
-
-
Hello People!
-
Start by pasting any youtube url. For bugs, you can create issues at github.com/poush/h2
+
+
+
+ H2
+
+
+
+
+
+
+
+
+
Hello People!
+
Start by pasting any youtube url. For bugs, you can create issues at github.com/poush/h2