├── .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 |
11 | 12 | 13 |
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