├── .gitignore
├── CNAME
├── backgrounds
├── 01.jpg
├── 02.jpg
├── 03.jpg
├── 04.jpg
├── 05.jpg
├── 06.jpg
├── 07.jpg
├── 08.jpg
├── 09.jpg
├── 10.jpg
├── 11.jpg
├── 12.jpg
├── 13.jpg
├── 14.jpg
├── 15.jpg
├── 16.jpg
├── 17.jpg
├── 18.jpg
├── 19.jpg
├── 20.jpg
├── 21.jpg
├── 22.jpg
├── 23.jpg
├── 24.jpg
├── 25.jpg
├── 26.jpg
├── 27.jpg
├── 28.jpg
├── 29.jpg
├── 30.jpg
├── 31.jpg
├── 32.jpg
├── 33.jpg
├── 34.jpg
├── 39.jpg
├── 40.jpg
├── 41.jpg
├── 42.jpg
└── 43.jpg
├── .jshintrc
├── .editorconfig
├── LICENSE
├── scripts
├── generate-conf-apache.sh
└── generate-dir-index.js
├── cache.appcache
├── package.json
├── .jscsrc
├── README.md
├── index.html
├── index.css
├── images.js
└── index.js
/.gitignore:
--------------------------------------------------------------------------------
1 | images.js
--------------------------------------------------------------------------------
/CNAME:
--------------------------------------------------------------------------------
1 | tab.putaindecode.io
2 |
--------------------------------------------------------------------------------
/backgrounds/01.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MoOx/tab/HEAD/backgrounds/01.jpg
--------------------------------------------------------------------------------
/backgrounds/02.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MoOx/tab/HEAD/backgrounds/02.jpg
--------------------------------------------------------------------------------
/backgrounds/03.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MoOx/tab/HEAD/backgrounds/03.jpg
--------------------------------------------------------------------------------
/backgrounds/04.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MoOx/tab/HEAD/backgrounds/04.jpg
--------------------------------------------------------------------------------
/backgrounds/05.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MoOx/tab/HEAD/backgrounds/05.jpg
--------------------------------------------------------------------------------
/backgrounds/06.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MoOx/tab/HEAD/backgrounds/06.jpg
--------------------------------------------------------------------------------
/backgrounds/07.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MoOx/tab/HEAD/backgrounds/07.jpg
--------------------------------------------------------------------------------
/backgrounds/08.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MoOx/tab/HEAD/backgrounds/08.jpg
--------------------------------------------------------------------------------
/backgrounds/09.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MoOx/tab/HEAD/backgrounds/09.jpg
--------------------------------------------------------------------------------
/backgrounds/10.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MoOx/tab/HEAD/backgrounds/10.jpg
--------------------------------------------------------------------------------
/backgrounds/11.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MoOx/tab/HEAD/backgrounds/11.jpg
--------------------------------------------------------------------------------
/backgrounds/12.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MoOx/tab/HEAD/backgrounds/12.jpg
--------------------------------------------------------------------------------
/backgrounds/13.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MoOx/tab/HEAD/backgrounds/13.jpg
--------------------------------------------------------------------------------
/backgrounds/14.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MoOx/tab/HEAD/backgrounds/14.jpg
--------------------------------------------------------------------------------
/backgrounds/15.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MoOx/tab/HEAD/backgrounds/15.jpg
--------------------------------------------------------------------------------
/backgrounds/16.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MoOx/tab/HEAD/backgrounds/16.jpg
--------------------------------------------------------------------------------
/backgrounds/17.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MoOx/tab/HEAD/backgrounds/17.jpg
--------------------------------------------------------------------------------
/backgrounds/18.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MoOx/tab/HEAD/backgrounds/18.jpg
--------------------------------------------------------------------------------
/backgrounds/19.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MoOx/tab/HEAD/backgrounds/19.jpg
--------------------------------------------------------------------------------
/backgrounds/20.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MoOx/tab/HEAD/backgrounds/20.jpg
--------------------------------------------------------------------------------
/backgrounds/21.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MoOx/tab/HEAD/backgrounds/21.jpg
--------------------------------------------------------------------------------
/backgrounds/22.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MoOx/tab/HEAD/backgrounds/22.jpg
--------------------------------------------------------------------------------
/backgrounds/23.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MoOx/tab/HEAD/backgrounds/23.jpg
--------------------------------------------------------------------------------
/backgrounds/24.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MoOx/tab/HEAD/backgrounds/24.jpg
--------------------------------------------------------------------------------
/backgrounds/25.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MoOx/tab/HEAD/backgrounds/25.jpg
--------------------------------------------------------------------------------
/backgrounds/26.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MoOx/tab/HEAD/backgrounds/26.jpg
--------------------------------------------------------------------------------
/backgrounds/27.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MoOx/tab/HEAD/backgrounds/27.jpg
--------------------------------------------------------------------------------
/backgrounds/28.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MoOx/tab/HEAD/backgrounds/28.jpg
--------------------------------------------------------------------------------
/backgrounds/29.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MoOx/tab/HEAD/backgrounds/29.jpg
--------------------------------------------------------------------------------
/backgrounds/30.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MoOx/tab/HEAD/backgrounds/30.jpg
--------------------------------------------------------------------------------
/backgrounds/31.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MoOx/tab/HEAD/backgrounds/31.jpg
--------------------------------------------------------------------------------
/backgrounds/32.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MoOx/tab/HEAD/backgrounds/32.jpg
--------------------------------------------------------------------------------
/backgrounds/33.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MoOx/tab/HEAD/backgrounds/33.jpg
--------------------------------------------------------------------------------
/backgrounds/34.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MoOx/tab/HEAD/backgrounds/34.jpg
--------------------------------------------------------------------------------
/backgrounds/39.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MoOx/tab/HEAD/backgrounds/39.jpg
--------------------------------------------------------------------------------
/backgrounds/40.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MoOx/tab/HEAD/backgrounds/40.jpg
--------------------------------------------------------------------------------
/backgrounds/41.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MoOx/tab/HEAD/backgrounds/41.jpg
--------------------------------------------------------------------------------
/backgrounds/42.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MoOx/tab/HEAD/backgrounds/42.jpg
--------------------------------------------------------------------------------
/backgrounds/43.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MoOx/tab/HEAD/backgrounds/43.jpg
--------------------------------------------------------------------------------
/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "newcap": false,
3 | "undef": true,
4 | "unused": true,
5 | "asi": true,
6 | "esnext": true,
7 | "node": true,
8 | "browser": true
9 | }
10 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # editorconfig.org
2 | root = true
3 |
4 | [*]
5 | end_of_line = lf
6 | charset = utf-8
7 | trim_trailing_whitespace = true
8 | insert_final_newline = true
9 | indent_style = space
10 | indent_size = 2
11 |
12 | [*.md]
13 | trim_trailing_whitespace = false
14 |
15 | [Makefile]
16 | indent_style = tab
17 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014 Putain de Code !
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of
6 | this software and associated documentation files (the "Software"), to deal in
7 | the Software without restriction, including without limitation the rights to
8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9 | the Software, and to permit persons to whom the Software is furnished to do so,
10 | subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/scripts/generate-conf-apache.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | APACHE_CONF="/etc/apache2/httpd.conf"
4 |
5 | PUTAINDETAB_APACHE_CONF=$PUTAINDETAB_PATH/index.apache.conf
6 | echo "Updating configuring for Apache in $PUTAINDETAB_APACHE_CONF..."
7 |
8 | sudo -v
9 |
10 | echo "# putaindetab $PUTAINDETAB_PATH
11 | Alias /tab \"$PUTAINDETAB_PATH\"
12 |
13 | Options Indexes FollowSymLinks MultiViews
14 | AllowOverride All
15 | Require all granted
16 |
17 | "| sudo tee "$PUTAINDETAB_APACHE_CONF"
18 |
19 | # Add Apache configuration only if not already here
20 | if cat $APACHE_CONF | grep "$PUTAINDETAB_PATH" > /dev/null
21 | then
22 | echo "Apache configuration already included"
23 | else
24 | echo "Including configuring to Apache..."
25 | echo "" | sudo tee -a $APACHE_CONF
26 | echo "# putaindetab $PUTAINDETAB_PATH" | sudo tee -a $APACHE_CONF
27 | echo "Include \"$PUTAINDETAB_APACHE_CONF\"" | sudo tee -a $APACHE_CONF
28 | fi
29 |
30 | # restart Apache
31 | if type apachectl > /dev/null
32 | then
33 | echo "Restarting Apache..."
34 | apachectl configtest
35 | sudo apachectl restart
36 | else
37 | echo "Please restart Apache"
38 | fi
39 |
--------------------------------------------------------------------------------
/cache.appcache:
--------------------------------------------------------------------------------
1 | CACHE MANIFEST
2 | # 2015-01-15T07:00:00
3 |
4 | # good link to understand appcache
5 | # http://www.html5rocks.com/en/tutorials/appcache/beginner/
6 |
7 | index.html
8 | index.css
9 | index.js
10 |
11 | images.js
12 | backgrounds/01.jpg
13 | backgrounds/02.jpg
14 | backgrounds/03.jpg
15 | backgrounds/04.jpg
16 | backgrounds/05.jpg
17 | backgrounds/06.jpg
18 | backgrounds/07.jpg
19 | backgrounds/08.jpg
20 | backgrounds/09.jpg
21 | backgrounds/10.jpg
22 | backgrounds/11.jpg
23 | backgrounds/12.jpg
24 | backgrounds/13.jpg
25 | backgrounds/14.jpg
26 | backgrounds/15.jpg
27 | backgrounds/16.jpg
28 | backgrounds/17.jpg
29 | backgrounds/18.jpg
30 | backgrounds/19.jpg
31 | backgrounds/20.jpg
32 | backgrounds/21.jpg
33 | backgrounds/22.jpg
34 | backgrounds/23.jpg
35 | backgrounds/24.jpg
36 | backgrounds/25.jpg
37 | backgrounds/26.jpg
38 | backgrounds/27.jpg
39 | backgrounds/28.jpg
40 | backgrounds/29.jpg
41 | backgrounds/30.jpg
42 | backgrounds/31.jpg
43 | backgrounds/32.jpg
44 | backgrounds/33.jpg
45 | backgrounds/34.jpg
46 | backgrounds/39.jpg
47 | backgrounds/40.jpg
48 | backgrounds/41.jpg
49 | backgrounds/42.jpg
50 | backgrounds/43.jpg
51 |
52 |
53 | # prevent non cached ressources to not load on cached page (even online)
54 | # http://alistapart.com/article/application-cache-is-a-douchebag#section7
55 | NETWORK:
56 | *
57 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "putainde-tab",
3 | "version": "0.1.0",
4 | "description": "New tab replacement",
5 | "main": "index.js",
6 | "scripts": {
7 | "generate-conf-apache": "scripts/generate-conf-apache.sh",
8 | "generate-dir-index": "node scripts/generate-dir-index.js",
9 | "generate-localhost": "npm run generate-dir-index && npm run generate-conf-apache",
10 | "copy-app": "cp index.html index.js index.css \"$PUTAINDETAB_PATH\"",
11 | "hide-app": "chflags hidden \"$PUTAINDETAB_PATH/index.html\" \"$PUTAINDETAB_PATH/index.js\" \"$PUTAINDETAB_PATH/index.css\" \"$PUTAINDETAB_PATH/images.js\" \"$PUTAINDETAB_PATH/index.apache.conf\"",
12 | "unhide-app": "chflags nohidden \"$PUTAINDETAB_PATH/index.html\" \"$PUTAINDETAB_PATH/index.js\" \"$PUTAINDETAB_PATH/index.css\" \"$PUTAINDETAB_PATH/images.js\" \"$PUTAINDETAB_PATH/index.apache.conf\""
13 | },
14 | "repository": {
15 | "type": "git",
16 | "url": "https://github.com/putaindecode/tab.git"
17 | },
18 | "keywords": [
19 | "browser",
20 | "tab"
21 | ],
22 | "author": "Maxime Thirouin",
23 | "license": "MIT",
24 | "bugs": {
25 | "url": "https://github.com/putaindecode/tab/issues"
26 | },
27 | "homepage": "https://github.com/putaindecode/tab",
28 | "devDependencies": {
29 | "glob": "^4.3.1",
30 | "ignore": "^2.2.15"
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/scripts/generate-dir-index.js:
--------------------------------------------------------------------------------
1 | var fs = require("fs")
2 | var path = require("path")
3 | var glob = require("glob")
4 |
5 | var backgrounds = []
6 |
7 | // using an array of glob to directly get the appropriate extensions is very slow
8 | // so instead getting an ent
9 | // ire folder, then manually filter
10 | var allowedExt = [".jpg", ".JPG", ".jpeg", ".JPEG", ".png", ".PNG"]
11 | var dir = process.env.PUTAINDETAB_PATH || "backgrounds"
12 | console.log("Generating index for '" + dir + "'...")
13 | glob(dir + "/**/*",
14 | function(err, files) {
15 | if (err) {throw err}
16 |
17 | // for test
18 | // files = files.slice(0, 10)
19 |
20 | var totalLength = files.length
21 |
22 | // get only images extensions
23 | files = files.filter(function(file) {
24 | return allowedExt.indexOf(path.extname(file)) > -1
25 | })
26 |
27 | console.log(files.length + " files found (on " + totalLength + " - " + (totalLength - files.length) + " skipped)...")
28 |
29 | // .putaindetabignore
30 | var ignoreFile
31 | try {
32 | ignoreFile = fs.readFileSync(dir + "/.putaindetabignore", {encoding: "utf8"})
33 | }
34 | catch (e) {
35 | console.log("No .putaindetabignore (or not readable) in " + dir)
36 | }
37 | if (ignoreFile) {
38 | var ignorePatterns = ignoreFile.split(require("os").EOL)
39 | if (!ignorePatterns.length) {
40 | console.log("No pattern found in .putaindetabignore")
41 | }
42 | else {
43 | console.log("Skipping files by patterns: " + ignorePatterns)
44 | var ignore = require('ignore')
45 | var putaindetabignore = ignore().addPattern(ignorePatterns)
46 | var nonIgnoredLength = files.length
47 | files = files.filter(putaindetabignore.createFilter())
48 | console.log(files.length + " files kept (on " + nonIgnoredLength + " - " + (nonIgnoredLength - files.length) + " ignored)...")
49 | }
50 | }
51 |
52 | // build images.js
53 | files.forEach(function(file) {
54 | var dirs = path.dirname(file).split(path.sep)
55 | var title = path.basename(file)
56 | title = title.replace(/\..+$/, "")
57 | if (dirs.length > 1) {
58 | title = dirs[dirs.length - 1] + " (" + title + ")"
59 | }
60 | backgrounds.push({
61 | //color: null, // @todo extract a color from the image
62 | url: file.replace(dir + "/", ""),
63 | source: null, // @todo extract author of the jpg if possible
64 | sourceUrl: "file://" + file,
65 | title: title
66 | })
67 | })
68 |
69 | // write images.js
70 | fs.writeFileSync(dir + "/images.js", "window.putaindeTab = " + JSON.stringify({
71 | backgrounds: backgrounds
72 | }, null, 2))
73 | console.log(dir + "/images.js successfully created.")
74 | }
75 | )
76 |
--------------------------------------------------------------------------------
/.jscsrc:
--------------------------------------------------------------------------------
1 | {
2 | "excludeFiles": [
3 | "node_modules/**"
4 | ],
5 | "fileExtensions": [
6 | ".js"
7 | ],
8 | "requireCurlyBraces": [
9 | "if",
10 | "else",
11 | "for",
12 | "while",
13 | "do",
14 | "try",
15 | "catch"
16 | ],
17 | "requireSpaceAfterKeywords": [
18 | "if",
19 | "else",
20 | "for",
21 | "while",
22 | "do",
23 | "switch",
24 | "return",
25 | "try",
26 | "catch"
27 | ],
28 | "requireSpaceBeforeBlockStatements": true,
29 | "requireParenthesesAroundIIFE": true,
30 | "requireSpacesInConditionalExpression": {
31 | "afterTest": true,
32 | "beforeConsequent": true,
33 | "afterConsequent": true,
34 | "beforeAlternate": true
35 | },
36 | "requireSpacesInFunctionExpression": {
37 | "beforeOpeningCurlyBrace": true
38 | },
39 | "disallowSpacesInFunctionExpression": {
40 | "beforeOpeningRoundBrace": true
41 | },
42 | "disallowMultipleVarDecl": true,
43 | "requireBlocksOnNewline": 1,
44 | "disallowPaddingNewlinesInBlocks": true,
45 | "disallowEmptyBlocks": true,
46 | "disallowSpacesInsideObjectBrackets": true,
47 | "disallowSpacesInsideArrayBrackets": true,
48 | "disallowSpacesInsideParentheses": true,
49 | "disallowQuotedKeysInObjects": "allButReserved",
50 | "disallowSpaceAfterObjectKeys": true,
51 | "requireCommaBeforeLineBreak": true,
52 | "requireOperatorBeforeLineBreak": [
53 | "?",
54 | "+",
55 | "-",
56 | "/",
57 | "*",
58 | "=",
59 | "==",
60 | "===",
61 | "!=",
62 | "!==",
63 | ">",
64 | ">=",
65 | "<",
66 | "<="
67 | ],
68 | "disallowSpaceAfterPrefixUnaryOperators": [
69 | "++",
70 | "--",
71 | "+",
72 | "-",
73 | "~",
74 | "!"
75 | ],
76 | "disallowSpaceBeforePostfixUnaryOperators": [
77 | "++",
78 | "--"
79 | ],
80 | "requireSpaceBeforeBinaryOperators": [
81 | "+",
82 | "-",
83 | "/",
84 | "*",
85 | "=",
86 | "==",
87 | "===",
88 | "!=",
89 | "!=="
90 | ],
91 | "requireSpaceAfterBinaryOperators": [
92 | "+",
93 | "-",
94 | "/",
95 | "*",
96 | "=",
97 | "==",
98 | "===",
99 | "!=",
100 | "!=="
101 | ],
102 | "disallowImplicitTypeConversion": [
103 | "numeric",
104 | "boolean",
105 | "binary",
106 | "string"
107 | ],
108 | "requireCamelCaseOrUpperCaseIdentifiers": "ignoreProperties",
109 | "disallowKeywords": [
110 | "with"
111 | ],
112 | "disallowMultipleLineStrings": true,
113 | "validateQuoteMarks": "\"",
114 | "validateIndentation": 2,
115 | "disallowMixedSpacesAndTabs": true,
116 | "disallowTrailingWhitespace": true,
117 | "requireKeywordsOnNewLine": [
118 | "else"
119 | ],
120 | "requireLineFeedAtFileEnd": true,
121 | "requireCapitalizedConstructors": true,
122 | "safeContextKeyword": "that",
123 | "requireDotNotation": true,
124 | "validateJSDoc": {
125 | "checkParamNames": true,
126 | "checkRedundantParams": true,
127 | "requireParamTypes": true
128 | },
129 | "requireSpaceAfterLineComment": true
130 | }
131 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # tab
2 |
3 | > Get a nice picture each time you open a new blank tab.
4 |
5 | _Note: before [reporting a bug](https://github.com/putaindecode/tab/issues/new), please checkout [opened issues](https://github.com/putaindecode/tab/issues/) first._
6 |
7 | ## Usage
8 |
9 | ### Online
10 |
11 | Set [putaindecode.io/tab/](http://putaindecode.io/tab/) as a url for your new tab/window. _Do not remove the trailing slash or you will get a redirection (with the slash) which will create a lag every time you will open a new tab._
12 |
13 | ##### Firefox
14 |
15 | Go to `about:config?filter=browser.newtab.url` and set `http://putaindecode.io/tab/` or add in your `user.js`
16 |
17 | ```js
18 | user_pref("browser.newtab.url", "http://putaindecode.io/tab/");
19 | ```
20 |
21 | ### From a local folder
22 |
23 | This project include a command to run this app locally for a given folder
24 |
25 | ```bash
26 | $ git clone https://github.com/putaindecode/tab.git
27 | $ cd tab
28 | $ npm install
29 | $ PUTAINDETAB_PATH="/Users/Shared/Photos" npm run generate-localhost
30 | $ npm run copy-app
31 | $ npm run hide-app
32 | ```
33 |
34 | Notes:
35 |
36 | - `generate-localhost` will create an `images.js` & `index.apache.conf` in `$PUTAINDETAB_PATH` by executing `generate-dir-index` then `generate-conf-apache`,
37 | - `copy-app` will copy the app files (`index.html|js|css`) in `$PUTAINDETAB_PATH`,
38 | - `hide-app` will copy just hide those file by using `chflags`. Nice to avoid noise in your folder (note that `unhide-app` make the reverse).
39 |
40 |
41 | Now go into [localhost/tab](http://localhost/tab/) & enjoy.
42 |
43 | #### Ignore files
44 |
45 | You can place in your `$PUTAINDETAB_PATH` folder a `.putaindetabignore` file that will behave like commons ignore file (pattern/wildcard supported).
46 | Here is an example
47 |
48 | ```
49 | To sort*
50 | Road Trip/2008 Trip with boring people
51 | ```
52 |
53 | #### Browser setup
54 |
55 | ##### Firefox
56 |
57 | In your user.js
58 |
59 | ```js
60 | // set your new tab url
61 | user_pref("browser.newtab.url", "http://localhost/tab/");
62 |
63 | // enable file:// link on my homepage
64 | user_pref("capability.policy.policynames", "localfilelinks");
65 | user_pref("capability.policy.localfilelinks.sites", "http://localhost/");
66 | user_pref("capability.policy.localfilelinks.checkloaduri.enabled", "allAccess");
67 | ```
68 |
69 | [Source](http://kb.mozillazine.org/Firefox_:_Issues_:_Links_to_Local_Pages_Don%27t_Work)
70 |
71 |
72 | ### Options
73 |
74 | You can pass some user script & styles to the url like this by appending some parameters.
75 |
76 | Checkout this a real online example: [putaindecode.io/tab/?scripts=https://rawgit.com/MoOx/0c43795ea80dc48faa28/raw/9c9e8d899b14616fcf18e53f7e98f22aad03d6a9/custom.js&styles=https://rawgit.com/MoOx/0c43795ea80dc48faa28/raw/11d12f02ee26f6e310e842e8832d339f2a2f0c9f/custom.css](http://putaindecode.io/tab/?scripts=https://rawgit.com/MoOx/0c43795ea80dc48faa28/raw/9c9e8d899b14616fcf18e53f7e98f22aad03d6a9/custom.js&styles=https://rawgit.com/MoOx/0c43795ea80dc48faa28/raw/11d12f02ee26f6e310e842e8832d339f2a2f0c9f/custom.css)
77 |
78 | #### `scripts`
79 |
80 | Urls for user scripts, comma separated.
81 |
82 | ```
83 | url/?scripts=url1.js,url2.js
84 | ```
85 |
86 | #### `styles`
87 |
88 | Urls for user styles, comma separated.
89 |
90 | ```
91 | url/?styles=url1.js,url2.js
92 | ```
93 |
94 |
95 | ---
96 |
97 | ## Contributing
98 |
99 | _We do not have a builded app for now (see [#23](https://github.com/putaindecode/tab/issues/23)), but we use an appcache file._
100 | **This means every modifications in every cached files (for now the entire app & assets) need a corresponding update in the cache manifest.**
101 | _Updating the timestamp in the `cache.appcache` file manually should do the trick until we automate all the things._
102 |
103 | Work on a branch & respect coding style.
104 |
105 | $ git clone https://github.com/putaindecode/tab.git
106 | $ git checkout -b patch-1
107 | $ open index.html
108 |
109 | ## [License](LICENSE)
110 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | New Tab
6 |
7 |
8 |
9 |
10 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
45 |
46 |
47 |
48 |
55 |
56 |
57 |
--------------------------------------------------------------------------------
/index.css:
--------------------------------------------------------------------------------
1 | /* libs below has been compressed via http://csscompressor.com/ */
2 |
3 | /**
4 | * https://github.com/cssrecipes/defaults/blob/master/index.css
5 | */
6 | html{box-sizing:border-box;overflow:auto;border-collapse:collapse}*,:before,:after{box-sizing:inherit;overflow:inherit}*{border-collapse:inherit}:not(body){background-repeat:no-repeat;background-position:50%;background-size:cover}[hidden]{display:none!important}html,body{margin:0;padding:0}
7 |
8 | /**
9 | * https://github.com/cssrecipes/reset/blob/master/button.css
10 | */
11 | .reset-Button{border:none;margin:0;padding:0;width:auto;overflow:visible;background:transparent;color:inherit;font:inherit;line-height:normal;-webkit-font-smoothing:inherit;-moz-osx-font-smoothing:inherit;-webkit-appearance:none}.reset-Button::-moz-focus-inner{border:0;padding:0}
12 |
13 | /*
14 | * Typography & layout
15 | */
16 | .putainde-Tab,
17 | .putainde-Tab body {
18 | width: 100%;
19 | height: 100%;
20 | margin: 0;
21 | padding: 0;
22 | background-color: black;
23 | }
24 |
25 | .putainde-Tab body {
26 | font-size: 1rem;
27 | line-height: 1.525rem;
28 |
29 | font-family: "Helvetica Neue", Helvetica, "Lucida Grande", "Lucida Sans Unicode", Arial, sans-serif;
30 | }
31 |
32 | .putainde-Tab body,
33 | .putainde-Tab a {
34 | color: #fff;
35 | text-shadow:
36 | 0 0 0.2rem rgba(0,0,0,.8),
37 | 0 0.05rem 0.4rem rgba(0,0,0,.5),
38 | 0 -0.05rem 0.4rem rgba(0,0,0,.5),
39 | 0.05rem 0 0.4rem rgba(0,0,0,.5),
40 | -0.05rem 0 0.4rem rgba(0,0,0,.5)
41 | ;
42 | }
43 |
44 | .putainde-Tab small,
45 | .putainde-Tab .small { font-size: .8em }
46 |
47 | /*
48 | * Utilities
49 | */
50 |
51 | .putainde-Tab-stretch {
52 | position: absolute;
53 | top: 0;
54 | right: 0;
55 | bottom: 0;
56 | left: 0;
57 | }
58 |
59 | @-webkit-keyframes putainde-Tab-animation-spin {
60 | from { -webkit-transform: rotate(0deg); }
61 | to { -webkit-transform: rotate(360deg); }
62 | }
63 | @keyframes putainde-Tab-animation-spin {
64 | from {transform:rotate(0deg);}
65 | to {transform:rotate(360deg);}
66 | }
67 | .putainde-Tab-animate-spin {
68 | animation: putainde-Tab-animation-spin .6s infinite linear;
69 | }
70 |
71 | .putainde-Tab-pin {
72 | position: absolute;
73 |
74 | padding: 1rem;
75 | }
76 |
77 | .putainde-Tab-pin--top-right { top: 0; right: 0 }
78 | .putainde-Tab-pin--bottom-right { bottom: 0; right: 0 }
79 | .putainde-Tab-pin--top-left { top: 0; left: 0 }
80 | .putainde-Tab-pin--bottom-left { bottom: 0; left: 0 }
81 |
82 | .putainde-Tab-pin--top { top: 0; left: 0; right: 0; width: 100%; }
83 | .putainde-Tab-pin--right { top: 0; right: 0; bottom: 0; height: 100%; }
84 | .putainde-Tab-pin--bottom { bottom: 0; left: 0; right: 0; width: 100%; }
85 | .putainde-Tab-pin--left { top: 0; bottom: 0; left: 0; height: 100%; }
86 |
87 | .putainde-Tab-pin--top,
88 | .putainde-Tab-pin--bottom { text-align: center }
89 |
90 | .putainde-Tab-pin--right,
91 | .putainde-Tab-pin--left { /* @todo vertical-align */ }
92 |
93 | /*
94 | * components
95 | */
96 |
97 | .putainde-Tab-icon {
98 | transform: translate3d(0,0,0); /* fix webkit/blink poor rendering issues */
99 |
100 | display: inline-block;
101 |
102 | /*width: 1em;
103 | height: 1em;*/
104 | line-height: 1;
105 | vertical-align: middle;
106 |
107 | -webkit-font-smoothing: antialiased;
108 | -moz-osx-font-smoothing: grayscale;
109 |
110 | fill: currentColor;
111 | }
112 |
113 | .putainde-Tab-button {
114 | cursor: pointer;
115 | }
116 |
117 | .putainde-Tab-loader {
118 | color: #fff;
119 | opacity: 1;
120 | }
121 |
122 | .putainde-Tab-background {
123 | /* for children */
124 | position: relative;
125 | width: 100%;
126 | height: 100%;
127 |
128 | /* prevent flash where scrollbars appear when playing with children to get real runtime computation for width/height */
129 | overflow: hidden;
130 | }
131 |
132 | .putainde-Tab-background,
133 | .putainde-Tab-background-imgBlur,
134 | .putainde-Tab-background-img {
135 | /* avoid glitch */
136 | -webkit-backface-visibility: hidden;
137 | }
138 |
139 | .putainde-Tab-background-imgBlur,
140 | .putainde-Tab-background-img {
141 | position: absolute;
142 | left: 50%;
143 | top: 50%;
144 | -webkit-transform: translateY(-50%) translateX(-50%) translateZ(0);
145 | transform: translateY(-50%) translateX(-50%) translateZ(0);
146 |
147 | /* fix image orientation according to exif data */
148 | image-orientation: from-image; /* firefox only */
149 |
150 | /* transition between images */
151 | opacity: 1;
152 | -webkit-transition: opacity .4s;
153 | transition: opacity .4s;
154 | }
155 |
156 | .putainde-Tab-background-imgBlur--hidden,
157 | .putainde-Tab-background-img--hidden {
158 | opacity: 0;
159 | }
160 |
161 | /* poor adjustement for generic landscape only */
162 | .putainde-Tab-background-imgBlur {
163 | min-width: 100%;
164 | filter: url('data:image/svg+xml;charset=utf-8,#filter');
165 | -webkit-filter: blur(20px);
166 | filter: blur(20px);
167 | }
168 |
169 | .putainde-Tab-background-img {}
170 |
171 | /* emulate background cover or contains depending or image/window orientation */
172 | .putainde-Tab-background-img--portrait.putainde-Tab-background-img--windowPortrait.putainde-Tab-background-img--ratioSuperiorThanWindow,
173 | .putainde-Tab-background-img--portrait.putainde-Tab-background-img--windowLandscape.putainde-Tab-background-img--ratioInferiorThanWindow,
174 | .putainde-Tab-background-img--landscape.putainde-Tab-background-img--windowLandscape.putainde-Tab-background-img--ratioSuperiorThanWindow,
175 | .putainde-Tab-background-img--landscape.putainde-Tab-background-img--windowPortrait.putainde-Tab-background-img--ratioInferiorThanWindow { height: 100% }
176 |
177 | .putainde-Tab-background-img--portrait.putainde-Tab-background-img--windowPortrait.putainde-Tab-background-img--ratioInferiorThanWindow,
178 | .putainde-Tab-background-img--portrait.putainde-Tab-background-img--windowLandscape.putainde-Tab-background-img--ratioSuperiorThanWindow,
179 | .putainde-Tab-background-img--landscape.putainde-Tab-background-img--windowLandscape.putainde-Tab-background-img--ratioInferiorThanWindow,
180 | .putainde-Tab-background-img--landscape.putainde-Tab-background-img--windowPortrait.putainde-Tab-background-img--ratioSuperiorThanWindow { width: 100% }
181 |
182 | .putainde-Tab-backgroundOverlay {
183 | background-color: transparent;
184 | background-image: radial-gradient(
185 | ellipse at center,
186 | rgba(0, 0, 0, 0) 0%,
187 | rgba(0, 0, 0, 0) 50%,
188 | rgba(0, 0, 0, .75) 100%
189 | );
190 | }
191 |
192 | .putainde-Tab-time {
193 | font-size: 1.5rem;
194 | font-weight: 200;
195 | }
196 |
197 | .putainde-Tab-backgroundCredit {
198 | text-decoration: none
199 | }
200 | .putainde-Tab-backgroundCredit:hover {
201 | text-decoration: underline
202 | }
203 |
204 | .putainde-Tab-footer {
205 |
206 | }
207 |
208 | .putainde-Tab-footer .putainde-Tab-footer-txt,
209 | .putainde-Tab-footer-link {
210 | -webkit-transition: opacity .5s;
211 | transition: opacity .5s;
212 | }
213 |
214 | .putainde-Tab-footer-txt { opacity: .25; }
215 |
216 | .putainde-Tab-footer:hover .putainde-Tab-footer-txt { opacity: .4; }
217 |
218 | .putainde-Tab-footer-link {
219 | opacity: .3;
220 | text-decoration: none;
221 | }
222 |
223 | .putainde-Tab-footer:hover .putainde-Tab-footer-link { opacity: .5 }
224 | .putainde-Tab-footer:hover .putainde-Tab-footer-link:hover {
225 | opacity: .75;
226 | text-decoration: underline;
227 | }
228 |
--------------------------------------------------------------------------------
/images.js:
--------------------------------------------------------------------------------
1 | /**
2 | * backgrounds data
3 | * @type {Array}
4 | */
5 | window.putaindeTab = {
6 | backgrounds: [
7 | {
8 | color: "rgb(120, 126, 181)",
9 | url: "backgrounds/01.jpg",
10 | source: "Dave Morrow",
11 | sourceUrl: "https://www.flickr.com/photos/daves-f-stop/8079071366",
12 | title: "La Push, Washington, USA"
13 | },
14 | {
15 | color: "rgb(212, 162, 87)",
16 | url: "backgrounds/02.jpg",
17 | source: "Magdalena Roeseler",
18 | sourceUrl: "https://www.flickr.com/photos/magdalenaroeseler/10497301253",
19 | title: "Zug, Switzerland"
20 | },
21 | {
22 | color: "rgb(77, 120, 99)",
23 | url: "backgrounds/03.jpg",
24 | source: "Stian Klo",
25 | sourceUrl: "http://500px.com/photo/41198398/h%C3%B8yvika-beach-by-stian-klo",
26 | title: "And\u00f8ya, Norway"
27 | },
28 | {
29 | color: "rgb(237, 150, 131)",
30 | url: "backgrounds/04.jpg",
31 | source: "skoeber",
32 | sourceUrl: "https://www.flickr.com/photos/asphericlens/6345711358",
33 | title: "Saxon Switzerland, Germany"
34 | },
35 | {
36 | color: "rgb(180, 108, 72)",
37 | url: "backgrounds/05.jpg",
38 | source: "skoeber",
39 | sourceUrl: "https://www.flickr.com/photos/asphericlens/14366773224",
40 | title: "Saxon Switzerland, Germany"
41 | },
42 | {
43 | color: "rgb(232, 185, 49)",
44 | url: "backgrounds/06.jpg",
45 | source: "skoeber",
46 | sourceUrl: "https://www.flickr.com/photos/asphericlens/7160610404",
47 | title: "Saxony, Germany"
48 | },
49 | {
50 | color: "rgb(190, 130, 61)",
51 | url: "backgrounds/07.jpg",
52 | source: "",
53 | sourceUrl: "https://github.com/putaindecode/tab/issues/new",
54 | title: "Source Unknown"
55 | },
56 | {
57 | color: "rgb(180, 84, 44)",
58 | url: "backgrounds/08.jpg",
59 | source: "Jes\u00fas Ignacio Bravo Soler",
60 | sourceUrl: "http://500px.com/photo/68800189/singular-by-jes%C3%BAs-ignacio-bravo-soler",
61 | title: "Otzarreta Forest, Spain"
62 | },
63 | {
64 | color: "rgb(191, 96, 109)",
65 | url: "backgrounds/09.jpg",
66 | source: "Dave Morrow",
67 | sourceUrl: "https://www.flickr.com/photos/daves-f-stop/9593394811",
68 | title: "Palouse Falls, Washington, USA"
69 | },
70 | {
71 | color: "rgb(154, 150, 199)",
72 | url: "backgrounds/10.jpg",
73 | source: "Dave Morrow",
74 | sourceUrl: "https://www.flickr.com/photos/daves-f-stop/11713436133",
75 | title: "San Francisco, California, USA"
76 | },
77 | {
78 | color: "rgb(168, 212, 39)",
79 | url: "backgrounds/11.jpg",
80 | source: "Dave Morrow",
81 | sourceUrl: "https://www.flickr.com/photos/daves-f-stop/8272381830",
82 | title: "Auster-Skaftafellssysla, Iceland"
83 | },
84 | {
85 | color: "rgb(60, 164, 180)",
86 | url: "backgrounds/12.jpg",
87 | source: "Chris Zielecki",
88 | sourceUrl: "https://www.flickr.com/photos/zanthia/7870409330",
89 | title: "Hvide Sande, Denmark"
90 | },
91 | {
92 | color: "rgb(181, 86, 58)",
93 | url: "backgrounds/13.jpg",
94 | source: "skoeber",
95 | sourceUrl: "https://www.flickr.com/photos/asphericlens/7227178308",
96 | title: "Paptsdorf, Germany"
97 | },
98 | {
99 | color: "rgb(61, 108, 180)",
100 | url: "backgrounds/14.jpg",
101 | source: "Trey Ratcliff",
102 | sourceUrl: "https://www.flickr.com/photos/stuckincustoms/3124621920",
103 | title: "Teton Range, Wyoming, USA"
104 | },
105 | {
106 | url: "backgrounds/15.jpg",
107 | source: "Trey Ratcliff",
108 | sourceUrl: "https://www.flickr.com/photos/stuckincustoms/8704121860/",
109 | title: "Tokyo, Japan"
110 | },
111 | {
112 | url: "backgrounds/16.jpg",
113 | source: "Trey Ratcliff",
114 | sourceUrl: "https://www.flickr.com/photos/stuckincustoms/11114091294/",
115 | title: "Li Jiang, China"
116 | },
117 | {
118 | url: "backgrounds/17.jpg",
119 | source: "Trey Ratcliff",
120 | sourceUrl: "https://www.flickr.com/photos/stuckincustoms/11783297005/",
121 | title: "Hobbiton, New Zealand"
122 | },
123 | {
124 | url: "backgrounds/18.jpg",
125 | source: "Trey Ratcliff",
126 | sourceUrl: "https://www.flickr.com/photos/stuckincustoms/8686957036/",
127 | title: "Sydney, Australia"
128 | },
129 | {
130 | url: "backgrounds/19.jpg",
131 | source: "Cristian Ruberti",
132 | sourceUrl: "https://www.flickr.com/photos/ambsab/11197203555/in/pool-2179950@N25/",
133 | title: "Monte Sagro, Italy"
134 | },
135 | {
136 | url: "backgrounds/20.jpg",
137 | source: "invno1",
138 | sourceUrl: "http://www.reddit.com/r/EarthPorn/comments/2fbuw4/mt_jefferson_taken_from_the_pacific_crest_trail_3/",
139 | title: "Mt. Jefferson, Oregon, USA"
140 | },
141 | {
142 | url: "backgrounds/21.jpg",
143 | source: "Trey Ratcliff",
144 | sourceUrl: "https://www.flickr.com/photos/stuckincustoms/8498614095",
145 | title: "Glenorchy, New Zealand"
146 | },
147 | {
148 | url: "backgrounds/22.jpg",
149 | source: "Trey Ratcliff",
150 | sourceUrl: "https://www.flickr.com/photos/stuckincustoms/6961023258",
151 | title: "Oahu, Hawaii"
152 | },
153 | {
154 | url: "backgrounds/23.jpg",
155 | source: "Trey Ratcliff",
156 | sourceUrl: "https://www.flickr.com/photos/stuckincustoms/12933604573/",
157 | title: "Virgin Gorda, Virgin Islands"
158 | },
159 | {
160 | url: "backgrounds/24.jpg",
161 | source: "Romain Guy",
162 | sourceUrl: "https://www.flickr.com/photos/romainguy/3409068082/",
163 | title: "San Francisco, California, USA"
164 | },
165 | {
166 | url: "backgrounds/25.jpg",
167 | source: "Romain Guy",
168 | sourceUrl: "https://www.flickr.com/photos/romainguy/8338439169/",
169 | title: "Antelope Canyon, Arizona, USA"
170 | },
171 | {
172 | url: "backgrounds/26.jpg",
173 | source: "Romain Guy",
174 | sourceUrl: "https://www.flickr.com/photos/romainguy/6840341477/",
175 | title: "Kalalu, Hawaii, USA"
176 | },
177 | {
178 | url: "backgrounds/27.jpg",
179 | source: "David Cantu",
180 | sourceUrl: "http://500px.com/photo/37449470/-the-view-from-jalama-bluffs-by-david-cantu",
181 | title: "Point Arbuello, California, USA"
182 | },
183 | {
184 | url: "backgrounds/28.jpg",
185 | source: "Steve Dunleavy",
186 | sourceUrl: "https://www.flickr.com/photos/stevedunleavy/5192342061/",
187 | title: "Grand Canyon, Arizona, USA"
188 | },
189 | {
190 | url: "backgrounds/29.jpg",
191 | source: "Conor MacNeill",
192 | sourceUrl: "http://500px.com/photo/18655061/yangshuo-cyclist-by-conor-macneill",
193 | title: "Guilin, China"
194 | },
195 | {
196 | url: "backgrounds/30.jpg",
197 | source: "Steve Dunleavy",
198 | sourceUrl: "https://www.flickr.com/photos/stevedunleavy/5044329063/",
199 | title: "Yosemite, California, USA"
200 | },
201 | {
202 | url: "backgrounds/31.jpg",
203 | source: "Jyrki Salmi",
204 | sourceUrl: "http://www.flickr.com/photos/salman2000/9321259912",
205 | title: "Stunted Pine"
206 | },
207 | {
208 | url: "backgrounds/32.jpg",
209 | source: "Trey Ratcliff",
210 | sourceUrl: "https://www.flickr.com/photos/stuckincustoms/8468378029",
211 | title: "Great Wall of China"
212 | },
213 | {
214 | url: "backgrounds/33.jpg",
215 | source: "Trey Ratcliff",
216 | sourceUrl: "https://www.flickr.com/photos/stuckincustoms/12375331335",
217 | title: "Crater Lake, Oregon, USA"
218 | },
219 | {
220 | url: "backgrounds/34.jpg",
221 | source: "Adrian Simionov",
222 | sourceUrl: "http://500px.com/photo/63850509/the-place-of-no-words-by-adrian-simionov",
223 | title: "Queenstown, New Zealand"
224 | },
225 | {
226 | url: "backgrounds/39.jpg",
227 | source: "Trey Ratcliff",
228 | sourceUrl: "http://stuckincustoms.smugmug.com/Portfolio/i-QpP2GC2/A",
229 | title: "Relaxing in Saint Bathans, New Zealand"
230 | },
231 | {
232 | url: "backgrounds/40.jpg",
233 | source: "Gleb Tarro",
234 | sourceUrl: "https://500px.com/photo/96883525/landscape-chilie-patagonia-pehoe-dec-by-gleb-tarro",
235 | title: "Torres Del Paine, Chilean Patagonia"
236 | },
237 | {
238 | url: "backgrounds/41.jpg",
239 | source: "/u/zapekanka",
240 | sourceUrl: "http://i.imgur.com/OyTrZVi.jpg",
241 | title: "Sossusvlei salt and clay pan, Namib desert, Namibia"
242 | },
243 | {
244 | url: "backgrounds/42.jpg",
245 | source: "",
246 | sourceUrl: "http://i.imgur.com/fIdt7Fr.jpg",
247 | title: "Wadi Rum Desert, Jordan"
248 | },
249 | {
250 | url: "backgrounds/43.jpg",
251 | source: "David Kaplan",
252 | sourceUrl: "http://apod.nasa.gov/apod/ap110202.html",
253 | title: "Moon and Venus Over Switzerland"
254 | }
255 | ]
256 | }
257 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | (function(options) {
2 | var backgrounds = options.backgrounds
3 | var updateInterval = 30 // sec, 0 disable auto update
4 |
5 | var loaderEl = document.querySelector(".js-putainde-Tab-loader")
6 | var backgroundEl = document.querySelector(".js-putainde-Tab-background")
7 | var imgEl
8 | var imgBlurEl
9 | var backgroundCreditsEl = document.querySelector(".js-putainde-Tab-backgroundCredit")
10 | var clockEl = document.querySelector(".js-putainde-Tab-time")
11 | var changeBackgroundEl = document.querySelector(".js-putainde-Tab-changeBackground")
12 | var madeByEl = document.querySelector(".js-putainde-Tab-footer-madeBy")
13 |
14 | if (window.hostname === "putaindecode.io") {
15 | madeByEl.style.display = "block"
16 | }
17 |
18 | var timeout
19 |
20 | /**
21 | * RUN THE SHIT
22 | *
23 | * `onload` is used so backgrounds huge list can be listed below, not above.
24 | * This make code easier to read.
25 | */
26 | window.onload = function() {
27 | startClock()
28 |
29 | loadRandomBackground(function() {
30 | // loader is useless after first load
31 | loaderEl.setAttribute("hidden", true)
32 | })
33 |
34 | changeBackgroundEl.addEventListener("click", function() {
35 | changeBackgroundEl.classList.add("putainde-Tab-animate-spin")
36 | loadRandomBackground(function() {
37 | onNextAnimationIteration(changeBackgroundEl, function() {
38 | changeBackgroundEl.classList.remove("putainde-Tab-animate-spin")
39 | })
40 | })
41 | })
42 |
43 | loadCustomisations()
44 |
45 | // we can"t do that, history API is limited to current domain
46 | // well we don"t have a domain for file:/// so we are screwed...
47 | // any idea ?
48 | // https://github.com/putaindecode/tab/issues/2
49 | // history.pushState({}, "", "")
50 | }
51 |
52 | /**
53 | * load a random background
54 | */
55 | function loadRandomBackground(callback) {
56 | loadBackground(getRandomBackground(), function(item, img) {
57 | backgroundCreditsEl.innerHTML = ""
58 |
59 | var updated = false
60 | // first time page loading
61 | if (!imgEl) {
62 | updated = updateBackground(item, img)
63 | }
64 | else {
65 | imgEl.classList.add("putainde-Tab-background-img--hidden")
66 | if (imgBlurEl) {
67 | imgBlurEl.classList.add("putainde-Tab-background-imgBlur--hidden")
68 | }
69 | onNextTransitionEnd(imgEl, function() {
70 | updated = updateBackground(item, img)
71 | })
72 | }
73 |
74 | if (updateInterval) {
75 | if (timeout) {
76 | clearTimeout(timeout)
77 | }
78 |
79 | timeout = setTimeout(loadRandomBackground, updateInterval * 1000)
80 | }
81 |
82 | if (typeof callback === "function") {
83 | callback()
84 | }
85 | })
86 | }
87 |
88 | function updateBackground(item, img) {
89 | // prepare DOM
90 | backgroundEl.innerHTML = ""
91 |
92 | // get window orientation/ratio
93 | var windowOrientation = window.innerWidth > window.innerHeight ? "landscape" : "portrait"
94 | var windowRatio = window.innerWidth / window.innerHeight
95 |
96 | // get image orientation/ratio
97 | // we cannot get real image orientation by simply using width & height cause of exif rotation
98 | // so we have to create the image in the DOM, then get computed width & height
99 | imgEl = document.createElement("img")
100 | imgEl.setAttribute("src", item.url)
101 | imgEl.classList.add("putainde-Tab-background-img") // this class contains the rules to fix the orientation
102 | imgEl.classList.add("putainde-Tab-background-img--hidden")
103 |
104 | backgroundEl.appendChild(imgEl)
105 | var imgStyle = window.getComputedStyle(imgEl)
106 | var imgWidth = parseInt(imgStyle.getPropertyValue("width"))
107 | var imgHeight = parseInt(imgStyle.getPropertyValue("height"))
108 | var imgOrientation = imgWidth > imgHeight ? "landscape" : "portrait"
109 | backgroundEl.removeChild(imgEl)
110 | var imgRatio = imgWidth / imgHeight
111 |
112 |
113 | imgEl.classList.add(imgOrientation === "landscape" ? "putainde-Tab-background-img--landscape" : "putainde-Tab-background-img--portrait")
114 | imgEl.classList.add(windowOrientation === "landscape" ? "putainde-Tab-background-img--windowLandscape" : "putainde-Tab-background-img--windowPortrait")
115 | imgEl.classList.add(windowRatio < imgRatio ? "putainde-Tab-background-img--ratioSuperiorThanWindow" : "putainde-Tab-background-img--ratioInferiorThanWindow")
116 |
117 | //console.log(windowOrientation, windowRatio, imgOrientation, imgRatio, imgEl.className)
118 |
119 | // when we have a image orientation that is different from the browser orientation
120 | // we stop the default "cover" behavior as it might hide too many parts of the picture
121 | // so we add a blurry background to keep something cool
122 | imgBlurEl = false
123 | if (windowOrientation !== imgOrientation) {
124 | imgBlurEl = document.createElement("img")
125 | imgBlurEl.setAttribute("src", item.url)
126 | imgBlurEl.classList.add("putainde-Tab-background-imgBlur")
127 | imgBlurEl.classList.add("putainde-Tab-background-imgBlur--hidden")
128 | backgroundEl.appendChild(imgBlurEl)
129 | }
130 |
131 | // add img after the blurred one to not have to play with z-index :)
132 | backgroundEl.appendChild(imgEl)
133 |
134 | // dom ready, begin visual transition
135 | imgEl.classList.remove("putainde-Tab-background-img--hidden")
136 | if (imgBlurEl) {
137 | imgBlurEl.classList.remove("putainde-Tab-background-imgBlur--hidden")
138 | }
139 |
140 | var credits = item.source ? "Credits: " + item.source : "If you know the source, please let us know."
141 | var title = item.title ? item.title : credits
142 |
143 | backgroundCreditsEl.setAttribute("href", item.sourceUrl)
144 | backgroundCreditsEl.innerHTML = title
145 | if (credits !== title) {
146 | backgroundCreditsEl.setAttribute("title", credits)
147 | }
148 |
149 | return true
150 | }
151 |
152 | /**
153 | * load a background then execute a callback
154 | *
155 | * @param {Object} item background object to load
156 | * @param {Function} callback callback to execute when image is loaded. First arg is the item itself, 2nd is the image object.
157 | */
158 | function loadBackground(item, callback) {
159 | var img = new Image()
160 | img.src = item.url
161 | if (typeof callback === "function") {
162 | img.onload = callback.bind(callback, item, img)
163 | }
164 | }
165 |
166 | /**
167 | * Returns a random integer between min (included) and max (excluded)
168 | *
169 | * Using Math.round() will give you a non-uniform distribution
170 | * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random
171 | */
172 | function getRandomInt(min, max) {
173 | return Math.floor(Math.random() * (max - min + 1)) + min
174 | }
175 |
176 | /**
177 | * get a random background item
178 | */
179 | function getRandomBackground() {
180 | return backgrounds[getRandomInt(0, backgrounds.length - 1)]
181 | }
182 |
183 | /**
184 | * execute callback for the next transitionEnd
185 | * need to be called after the start or unexpected result might append
186 | *
187 | * @param {Object} el dom element to look at
188 | * @param {Function} callback function to execute at the end
189 | */
190 | function onNextTransitionEnd(el, callback) {
191 | onNextAnimationEnd(el, callback, {
192 | transition: "transitionend",
193 | OTransition: "otransitionend",
194 | MozTransition: "transitionend",
195 | WebkitTransition: "webkitTransitionEnd"
196 | })
197 | }
198 |
199 | /**
200 | * execute callback for the next animationIteration
201 | * need to be called after the start or unexpected result might append
202 | *
203 | * @param {Object} el dom element to look at
204 | * @param {Function} callback function to execute at the end
205 | */
206 | function onNextAnimationIteration(el, callback) {
207 | onNextAnimationEnd(el, callback, {
208 | animation: "animationiteration",
209 | OAnimation: "oanimationiteration",
210 | MozAnimation: "animationiteration",
211 | WebkitAnimation: "webkitAnimationIteration"
212 | })
213 | }
214 |
215 | /**
216 | * execute callback for the next animationEnd
217 | * need to be called after the start or unexpected result might append
218 | *
219 | * @param {Object} el dom element to look at
220 | * @param {Function} callback function to execute at the end
221 | * @param {Object} animKeys optional anim keys (used for onNextTransitionEnd())
222 | */
223 | function onNextAnimationEnd(el, callback, animKeys) {
224 | var ani
225 | var anims = animKeys || {
226 | animation: "animationend",
227 | OAnimation: "oanimationend",
228 | MozAnimation: "animationend",
229 | WebkitAnimation: "webkitAnimationEnd"
230 | }
231 |
232 | var i
233 | for (i in anims) {
234 | if (anims.hasOwnProperty(i) && el.style[i] !== undefined) {
235 | ani = anims[i]
236 | }
237 | }
238 |
239 | var duration = parseInt(window.getComputedStyle(el).getPropertyValue("transition-duration")) || 1 // shitty fallback
240 |
241 | if (ani) {
242 | var cb = function() {
243 | callback()
244 | el.removeEventListener(ani, cb)
245 | clearTimeout(shittyFallback)
246 | }
247 | el.addEventListener(ani, cb, false)
248 |
249 | // transition end doesn't work properly when tab is in background on some browser (safari 7/8)
250 | // so we add a shitty timeout
251 | var shittyFallback = setTimeout(cb, (duration * 1000) + 200)
252 | }
253 | else {
254 | setTimeout(callback, 1000) // poor fallback - lol
255 | }
256 | }
257 |
258 | /**
259 | * start the clock
260 | */
261 | function startClock() {
262 | updateClock()
263 |
264 | // do not update each 60sec
265 | // because you don't know when you started
266 | // and you can miss a minute if you start at 00:00:40
267 | setInterval(updateClock, 10 * 1000)
268 | }
269 |
270 | /**
271 | * update the clock
272 | */
273 | function updateClock() {
274 | var date = new Date()
275 | var hours = date.getHours().toString()
276 | var minutes = date.getMinutes().toString()
277 | clockEl.innerHTML = (hours.length < 2 ? "0" : "") + hours + ":" + (minutes.length < 2 ? "0" : "") + minutes
278 | }
279 |
280 | /**
281 | * load user customisations
282 | */
283 | function loadCustomisations() {
284 | var queryString = window.location.search.slice(1)
285 |
286 | queryString.split("&").map(function(declaration) {
287 | var chunks = declaration.split("=", 2)
288 | var key = chunks[0]
289 | var value = chunks[1]
290 |
291 | switch (key) {
292 | case "scripts":
293 | var scriptUrls = value.split(",")
294 | loadCustomScripts(scriptUrls)
295 | break
296 | case "styles":
297 | var styleUrls = value.split(",")
298 | loadCustomStyles(styleUrls)
299 | break
300 | }
301 | })
302 | }
303 |
304 | /**
305 | * load custom user scripts
306 | */
307 | function loadCustomScripts(scriptUrls) {
308 | scriptUrls.forEach(function(url) {
309 | loadJS(url)
310 | })
311 | }
312 |
313 | /**
314 | * load custom user stylesheets
315 | */
316 | function loadCustomStyles(styleUrls) {
317 | styleUrls.forEach(function(url) {
318 | loadCSS(url)
319 | })
320 | }
321 |
322 | // https://github.com/filamentgroup/loadJS/blob/master/loadJS.js
323 | /*! loadJS: load a JS file asynchronously. [c]2014 @scottjehl, Filament Group, Inc. (Based on http://goo.gl/REQGQ by Paul Irish). Licensed MIT */
324 | function loadJS(src, cb) {
325 | "use strict";
326 | var ref = window.document.getElementsByTagName("script")[ 0 ]
327 | var script = window.document.createElement("script")
328 | script.src = src
329 | script.async = true
330 | ref.parentNode.insertBefore(script, ref)
331 | if (cb) {
332 | script.onload = cb
333 | }
334 | return script
335 | }
336 |
337 | // https://github.com/filamentgroup/loadCSS/blob/master/loadCSS.js
338 | /*! loadCSS: load a CSS file asynchronously. [c]2014 @scottjehl, Filament Group, Inc. Licensed MIT */
339 | function loadCSS(href, before, media) {
340 | "use strict";
341 | // Arguments explained:
342 | // `href` is the URL for your CSS file.
343 | // `before` optionally defines the element we'll use as a reference for injecting our
344 | // By default, `before` uses the first