├── .babelrc ├── .bowerrc ├── .gitattributes ├── .gitignore ├── app ├── _locales │ └── en │ │ └── messages.json ├── images │ ├── icon-128.png │ ├── icon-16.png │ ├── icon-19.png │ ├── icon-38.png │ ├── icon-disabled.png │ └── icon.png ├── manifest.json ├── scripts │ ├── background.js │ ├── chromereload.js │ ├── ipc.js │ ├── proxy.js │ └── websitemanager.js └── styles │ └── main.css ├── bower.json ├── gulpfile.babel.js ├── package.json ├── test ├── index.html └── spec │ └── test.js └── webpack.config.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["es2015"] 3 | } 4 | -------------------------------------------------------------------------------- /.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory": "app/bower_components" 3 | } 4 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | temp 3 | .tmp 4 | dist 5 | .sass-cache 6 | app/bower_components 7 | test/bower_components 8 | package 9 | -------------------------------------------------------------------------------- /app/_locales/en/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "appName": { 3 | "message": "CacheBrowser", 4 | "description": "The name of the application" 5 | }, 6 | "appDescription": { 7 | "message": "CacheBrowser Chrome Extension", 8 | "description": "The description of the application" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /app/images/icon-128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CacheBrowser/cachebrowser-chrome/71f60b09f8adc5d3756f439e54a107ee9623e044/app/images/icon-128.png -------------------------------------------------------------------------------- /app/images/icon-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CacheBrowser/cachebrowser-chrome/71f60b09f8adc5d3756f439e54a107ee9623e044/app/images/icon-16.png -------------------------------------------------------------------------------- /app/images/icon-19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CacheBrowser/cachebrowser-chrome/71f60b09f8adc5d3756f439e54a107ee9623e044/app/images/icon-19.png -------------------------------------------------------------------------------- /app/images/icon-38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CacheBrowser/cachebrowser-chrome/71f60b09f8adc5d3756f439e54a107ee9623e044/app/images/icon-38.png -------------------------------------------------------------------------------- /app/images/icon-disabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CacheBrowser/cachebrowser-chrome/71f60b09f8adc5d3756f439e54a107ee9623e044/app/images/icon-disabled.png -------------------------------------------------------------------------------- /app/images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CacheBrowser/cachebrowser-chrome/71f60b09f8adc5d3756f439e54a107ee9623e044/app/images/icon.png -------------------------------------------------------------------------------- /app/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "__MSG_appName__", 3 | "version": "0.0.1", 4 | "manifest_version": 2, 5 | "description": "__MSG_appDescription__", 6 | "icons": { 7 | "16": "images/icon.png", 8 | "128": "images/icon.png" 9 | }, 10 | "default_locale": "en", 11 | "background": { 12 | "scripts": [ 13 | "scripts/background.js" 14 | ] 15 | }, 16 | "permissions": [ 17 | "http://*/*", 18 | "https://*/*", 19 | "background", 20 | "webRequest", 21 | "*://*.google.com/", 22 | "webRequestBlocking", 23 | "tabs", 24 | "proxy" 25 | ], 26 | "options_ui": { 27 | "page": "options.html", 28 | "chrome_style": true 29 | }, 30 | "page_action": { 31 | "default_icon": { 32 | "19": "images/icon.png", 33 | "38": "images/icon.png" 34 | }, 35 | "default_title": "CacheBrowser" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /app/scripts/background.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import { IPCManager } from './ipc'; 4 | import { ProxyManager } from './proxy'; 5 | import { WebsiteManager } from './websitemanager'; 6 | 7 | var ipc = new IPCManager(); 8 | var proxy = new ProxyManager(); 9 | var websiteManager = new WebsiteManager(ipc); 10 | 11 | function setTabIcon(tab) { 12 | var website = websiteManager.websiteFromUrl(tab.url); 13 | var active = websiteManager.isWebsiteActive(website); 14 | var icon = active ? 'images/icon.png' : 'images/icon-disabled.png'; 15 | 16 | chrome.pageAction.setIcon({ 17 | tabId: tab.id, 18 | path: icon 19 | }); 20 | } 21 | 22 | chrome.runtime.onInstalled.addListener(details => { 23 | console.log('previousVersion', details.previousVersion); 24 | }); 25 | 26 | chrome.tabs.onUpdated.addListener(tabId => { 27 | chrome.pageAction.show(tabId); 28 | chrome.tabs.get(tabId, tab => {setTabIcon(tab)}); 29 | }); 30 | 31 | 32 | chrome.pageAction.onClicked.addListener(function (tab) { 33 | var website = websiteManager.websiteFromUrl(tab.url); 34 | var active = websiteManager.toggleWebsite(website); 35 | 36 | setTabIcon(tab); 37 | proxy.updatePAC(websiteManager.getActiveWebsites(), () => { 38 | chrome.tabs.reload(tab.id); 39 | }); 40 | }); 41 | 42 | // TODO not sure if needed on load 43 | proxy.updatePAC(websiteManager.getActiveWebsites()); 44 | 45 | // chrome.webRequest.onBeforeSendHeaders.addListener(function(details) { 46 | // chrome.tabs.get(details.tabId, function(tab) { 47 | // var website = websiteManager.websiteFromUrl(tab.url); 48 | // details.requestHeaders['X-CB-WEBSITE'] = website; 49 | // return {requestHeaders: details.requestHeaders}; 50 | // }); 51 | // }, {urls: ['']}, ['blocking', 'requestHeaders']); 52 | -------------------------------------------------------------------------------- /app/scripts/chromereload.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // Reload client for Chrome Apps & Extensions. 4 | // The reload client has a compatibility with livereload. 5 | // WARNING: only supports reload command. 6 | 7 | const LIVERELOAD_HOST = 'localhost:'; 8 | const LIVERELOAD_PORT = 35729; 9 | const connection = new WebSocket('ws://' + LIVERELOAD_HOST + LIVERELOAD_PORT + '/livereload'); 10 | 11 | connection.onerror = error => { 12 | console.log('reload connection got error:', error); 13 | }; 14 | 15 | connection.onmessage = e => { 16 | if (e.data) { 17 | const data = JSON.parse(e.data); 18 | if (data && data.command === 'reload') { 19 | chrome.runtime.reload(); 20 | } 21 | } 22 | }; 23 | 24 | -------------------------------------------------------------------------------- /app/scripts/ipc.js: -------------------------------------------------------------------------------- 1 | 2 | export class IPCManager { 3 | constructor() { 4 | 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /app/scripts/proxy.js: -------------------------------------------------------------------------------- 1 | export class ProxyManager{ 2 | constructor() { 3 | this.proxySettings = { 4 | host: '127.0.0.1', 5 | port: '8080' 6 | } 7 | } 8 | 9 | generatePAC(activeWebsites) { 10 | var condition = 11 | 'host == "' + activeWebsites.join('" || host == "') + '"'; 12 | 13 | return ` 14 | function FindProxyForURL(url, host) { 15 | if (${condition}) { 16 | return "PROXY ${this.proxySettings.host}:${this.proxySettings.port}"; 17 | } 18 | return "DIRECT"; 19 | } 20 | ` 21 | } 22 | 23 | updatePAC(activeWebsites, callback) { 24 | var pac = this.generatePAC(activeWebsites); 25 | console.log(pac); 26 | var config = { 27 | mode: 'pac_script', 28 | pacScript: { 29 | data: pac 30 | } 31 | }; 32 | chrome.proxy.settings.set({value: config, scope: 'regular'}, function() { 33 | if (callback) { 34 | callback(); 35 | } 36 | }); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /app/scripts/websitemanager.js: -------------------------------------------------------------------------------- 1 | 2 | export class WebsiteManager { 3 | constructor(ipc) { 4 | this.ipc = ipc; 5 | 6 | this.activeWebsites = new Set(); 7 | } 8 | 9 | websiteFromUrl(url) { 10 | var url = new URL(url); 11 | return url.hostname; 12 | } 13 | 14 | isWebsiteActive(website) { 15 | return this.activeWebsites.has(website); 16 | } 17 | 18 | toggleWebsite(website) { 19 | if (this.activeWebsites.has(website)) { 20 | this.activeWebsites.delete(website); 21 | return false; 22 | } else { 23 | this.activeWebsites.add(website); 24 | return true; 25 | } 26 | } 27 | 28 | getActiveWebsites() { 29 | return Array.from(this.activeWebsites); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /app/styles/main.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding: 20px; 3 | } 4 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cachebrowser", 3 | "private": true, 4 | "version": "0.0.0", 5 | "dependencies": {}, 6 | "devDependencies": { 7 | "chai": "^3.5.0", 8 | "mocha": "^2.5.3" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /gulpfile.babel.js: -------------------------------------------------------------------------------- 1 | // generated on 2016-06-27 using generator-chrome-extension 0.5.6 2 | import gulp from 'gulp'; 3 | import gulpLoadPlugins from 'gulp-load-plugins'; 4 | import del from 'del'; 5 | import runSequence from 'run-sequence'; 6 | import {stream as wiredep} from 'wiredep'; 7 | 8 | import jetpack from 'fs-jetpack'; 9 | import webpack from 'webpack-stream'; 10 | 11 | import * as _ from 'lodash'; 12 | 13 | var projectDir = jetpack; 14 | var srcDir = projectDir.cwd('./app'); 15 | var destDir = projectDir.cwd('./dist'); 16 | 17 | const $ = gulpLoadPlugins(); 18 | 19 | var paths = { 20 | copyFromAppDir: [ 21 | './**/*.html', 22 | './**/*.+(jpg|png|svg)', 23 | './fonts/**', 24 | './styles/**', 25 | '_locales/**', 26 | "*.json" 27 | ] 28 | }; 29 | 30 | 31 | gulp.task('copy', function () { 32 | return projectDir.copyAsync(srcDir.path('.'), destDir.path(), { 33 | overwrite: true, 34 | matching: paths.copyFromAppDir 35 | }); 36 | }); 37 | 38 | function lint(files, options) { 39 | return () => { 40 | return gulp.src(files) 41 | .pipe($.eslint(options)) 42 | .pipe($.eslint.format()); 43 | }; 44 | } 45 | 46 | gulp.task('lint', lint(srcDir.path('scripts/**/*.js'), { 47 | env: { 48 | es6: true 49 | }, 50 | parserOptions: { 51 | "ecmaVersion": 6, 52 | "sourceType": "module", 53 | "ecmaFeatures": { 54 | "modules": true 55 | } 56 | } 57 | })); 58 | 59 | gulp.task('webpack', function () { 60 | return gulp.src(srcDir.path('scripts/background.js')) 61 | .pipe(webpack(require('./webpack.config.js') )) 62 | .pipe(gulp.dest(destDir.path('scripts/'))); 63 | }); 64 | 65 | gulp.task('clean', del.bind(null, ['.tmp', 'dist'])); 66 | 67 | gulp.task('watch', ['lint', 'webpack'], () => { 68 | gulp.watch(srcDir.path('scripts/**/*.js'), ['lint', 'webpack']); 69 | gulp.watch( 70 | _.map(paths.copyFromAppDir, (v) => srcDir.path(v)), 71 | ['copy']); 72 | gulp.watch('bower.json', ['wiredep']); 73 | }); 74 | 75 | gulp.task('size', () => { 76 | return gulp.src(destDir.path('**/*')).pipe($.size({title: 'build', gzip: true})); 77 | }); 78 | 79 | gulp.task('wiredep', () => { 80 | gulp.src(srcDir.path('*.html')) 81 | .pipe(wiredep({ 82 | ignorePath: /^(\.\.\/)*\.\./ 83 | })) 84 | .pipe(gulp.dest(srcDir.path('.'))); 85 | }); 86 | 87 | gulp.task('package', function () { 88 | var manifest = require('./dist/manifest.json'); 89 | return gulp.src(destDir.path('**')) 90 | .pipe($.zip('CacheBrowser-' + manifest.version + '.zip')) 91 | .pipe(gulp.dest('package')); 92 | }); 93 | 94 | gulp.task('build', (cb) => { 95 | runSequence( 96 | 'lint', 'webpack', 97 | ['copy'], 98 | 'size', cb); 99 | }); 100 | 101 | gulp.task('default', ['clean'], cb => { 102 | runSequence('build', cb); 103 | }); 104 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cachebrowser", 3 | "private": true, 4 | "engines": { 5 | "node": ">=0.8.0" 6 | }, 7 | "devDependencies": { 8 | "babel-core": "^6.7.2", 9 | "babel-preset-es2015": "^6.6.0", 10 | "del": "^2.2.0", 11 | "gulp": "^3.9.1", 12 | "gulp-babel": "^6.1.2", 13 | "gulp-cache": "^0.4.3", 14 | "gulp-chrome-manifest": "0.0.13", 15 | "gulp-clean-css": "^2.0.3", 16 | "gulp-eslint": "^2.0.0", 17 | "gulp-if": "^2.0.0", 18 | "gulp-imagemin": "^2.4.0", 19 | "gulp-livereload": "^3.8.1", 20 | "gulp-load-plugins": "^1.2.0", 21 | "gulp-htmlmin": "^1.3.0", 22 | "gulp-size": "^2.1.0", 23 | "gulp-sourcemaps": "^1.6.0", 24 | "gulp-uglify": "^1.5.3", 25 | "gulp-useref": "^3.0.8", 26 | "gulp-zip": "^3.2.0", 27 | "main-bower-files": "^2.11.1", 28 | "run-sequence": "^1.1.5", 29 | "wiredep": "^4.0.0" 30 | }, 31 | "eslintConfig": { 32 | "env": { 33 | "node": true, 34 | "browser": true 35 | }, 36 | "globals": { 37 | "chrome": true 38 | }, 39 | "rules": { 40 | "eol-last": 0, 41 | "quotes": [ 42 | 2, 43 | "single" 44 | ] 45 | } 46 | }, 47 | "dependencies": { 48 | "babel-core": "^6.10.4", 49 | "babel-loader": "^6.2.4", 50 | "babel-polyfill": "^6.9.1", 51 | "fs-jetpack": "^0.9.2", 52 | "lodash": "^4.13.1", 53 | "webpack": "^1.13.1", 54 | "webpack-stream": "^3.2.0" 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /test/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Mocha Spec Runner 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 18 | 19 | 20 | 21 | 22 | 23 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /test/spec/test.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | describe('Give it some context', function () { 5 | describe('maybe a bit more context here', function () { 6 | it('should run here few assertions', function () { 7 | 8 | }); 9 | }); 10 | }); 11 | })(); 12 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | var webpack = require('webpack'); 3 | 4 | module.exports = { 5 | entry: [ 6 | // 'babel-polyfill', 7 | './app/scripts/background.js' 8 | ], 9 | output: { 10 | filename: 'background.js' 11 | }, 12 | devtool: 'source-map', 13 | module: { 14 | loaders: [ 15 | { 16 | loader: "babel-loader", 17 | query: { 18 | // plugins: ['transform-runtime'], 19 | presets: ['es2015'], 20 | } 21 | }, 22 | ] 23 | }, 24 | debug: true 25 | }; 26 | --------------------------------------------------------------------------------