├── .bowerrc ├── .editorconfig ├── .gitattributes ├── .gitignore ├── .jshintrc ├── Gruntfile.js ├── README.md ├── app ├── _locales │ └── en │ │ └── messages.json ├── images │ ├── icon-128.png │ ├── icon-16.png │ └── icon-48.png ├── manifest.json ├── pages │ ├── notice.html │ └── permission.html ├── scripts │ ├── background.js │ ├── chromereload.js │ ├── contentscript.js │ └── notice.js └── styles │ ├── background.css │ └── main.css ├── bower.json ├── package.json ├── promo ├── large-tile.png ├── marquee.png ├── screenshot.png └── small-tile.png └── test ├── .bowerrc ├── bower.json ├── index.html └── spec └── test.js /.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory": "app/bower_components" 3 | } 4 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | 8 | [*] 9 | 10 | # Change these settings to your own preference 11 | indent_style = space 12 | indent_size = 4 13 | 14 | # We recommend you to keep these unchanged 15 | end_of_line = lf 16 | charset = utf-8 17 | trim_trailing_whitespace = true 18 | insert_final_newline = true 19 | 20 | [*.md] 21 | trim_trailing_whitespace = false 22 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "node": true, 3 | "browser": true, 4 | "esnext": true, 5 | "bitwise": true, 6 | "camelcase": true, 7 | "curly": true, 8 | "eqeqeq": true, 9 | "immed": true, 10 | "indent": 4, 11 | "latedef": true, 12 | "newcap": true, 13 | "noarg": true, 14 | "quotmark": "single", 15 | "regexp": true, 16 | "undef": true, 17 | "unused": true, 18 | "strict": true, 19 | "trailing": true, 20 | "smarttabs": true, 21 | "globals" : { 22 | "chrome": true 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | // Generated on 2015-01-03 using generator-chrome-extension 0.2.9 2 | 'use strict'; 3 | 4 | // # Globbing 5 | // for performance reasons we're only matching one level down: 6 | // 'test/spec/{,*/}*.js' 7 | // use this if you want to recursively match all subfolders: 8 | // 'test/spec/**/*.js' 9 | 10 | module.exports = function (grunt) { 11 | 12 | // Load grunt tasks automatically 13 | require('load-grunt-tasks')(grunt); 14 | 15 | // Time how long tasks take. Can help when optimizing build times 16 | require('time-grunt')(grunt); 17 | 18 | // Configurable paths 19 | var config = { 20 | app: 'app', 21 | dist: 'dist' 22 | }; 23 | 24 | grunt.initConfig({ 25 | 26 | // Project settings 27 | config: config, 28 | 29 | // Watches files for changes and runs tasks based on the changed files 30 | watch: { 31 | bower: { 32 | files: ['bower.json'], 33 | tasks: ['bowerInstall'] 34 | }, 35 | js: { 36 | files: ['<%= config.app %>/scripts/{,*/}*.js'], 37 | // tasks: ['jshint'], 38 | options: { 39 | livereload: true 40 | } 41 | }, 42 | gruntfile: { 43 | files: ['Gruntfile.js'] 44 | }, 45 | styles: { 46 | files: ['<%= config.app %>/styles/{,*/}*.css'], 47 | tasks: [], 48 | options: { 49 | livereload: true 50 | } 51 | }, 52 | livereload: { 53 | options: { 54 | livereload: '<%= connect.options.livereload %>' 55 | }, 56 | files: [ 57 | '<%= config.app %>/*.html', 58 | '<%= config.app %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}', 59 | '<%= config.app %>/manifest.json', 60 | '<%= config.app %>/_locales/{,*/}*.json' 61 | ] 62 | } 63 | }, 64 | 65 | // Grunt server and debug server setting 66 | connect: { 67 | options: { 68 | port: 9000, 69 | livereload: 35729, 70 | // change this to '0.0.0.0' to access the server from outside 71 | hostname: 'localhost' 72 | }, 73 | chrome: { 74 | options: { 75 | open: false, 76 | base: [ 77 | '<%= config.app %>' 78 | ] 79 | } 80 | }, 81 | test: { 82 | options: { 83 | open: false, 84 | base: [ 85 | 'test', 86 | '<%= config.app %>' 87 | ] 88 | } 89 | } 90 | }, 91 | 92 | // Empties folders to start fresh 93 | clean: { 94 | chrome: { 95 | }, 96 | dist: { 97 | files: [{ 98 | dot: true, 99 | src: [ 100 | '<%= config.dist %>/*', 101 | '!<%= config.dist %>/.git*' 102 | ] 103 | }] 104 | } 105 | }, 106 | 107 | // Make sure code styles are up to par and there are no obvious mistakes 108 | jshint: { 109 | options: { 110 | jshintrc: '.jshintrc', 111 | reporter: require('jshint-stylish') 112 | }, 113 | all: [ 114 | 'Gruntfile.js', 115 | '<%= config.app %>/scripts/{,*/}*.js', 116 | '!<%= config.app %>/scripts/vendor/*', 117 | 'test/spec/{,*/}*.js' 118 | ] 119 | }, 120 | mocha: { 121 | all: { 122 | options: { 123 | run: true, 124 | urls: ['http://localhost:<%= connect.options.port %>/index.html'] 125 | } 126 | } 127 | }, 128 | 129 | // Automatically inject Bower components into the HTML file 130 | bowerInstall: { 131 | app: { 132 | src: [ 133 | '<%= config.app %>/*.html' 134 | ] 135 | } 136 | }, 137 | 138 | // Reads HTML for usemin blocks to enable smart builds that automatically 139 | // concat, minify and revision files. Creates configurations in memory so 140 | // additional tasks can operate on them 141 | useminPrepare: { 142 | options: { 143 | dest: '<%= config.dist %>' 144 | }, 145 | html: [ 146 | '<%= config.app %>/popup.html', 147 | '<%= config.app %>/options.html' 148 | ] 149 | }, 150 | 151 | // Performs rewrites based on rev and the useminPrepare configuration 152 | usemin: { 153 | options: { 154 | assetsDirs: ['<%= config.dist %>', '<%= config.dist %>/images'] 155 | }, 156 | html: ['<%= config.dist %>/{,*/}*.html'], 157 | css: ['<%= config.dist %>/styles/{,*/}*.css'] 158 | }, 159 | 160 | // The following *-min tasks produce minifies files in the dist folder 161 | imagemin: { 162 | dist: { 163 | files: [{ 164 | expand: true, 165 | cwd: '<%= config.app %>/images', 166 | src: '{,*/}*.{gif,jpeg,jpg,png}', 167 | dest: '<%= config.dist %>/images' 168 | }] 169 | } 170 | }, 171 | 172 | svgmin: { 173 | dist: { 174 | files: [{ 175 | expand: true, 176 | cwd: '<%= config.app %>/images', 177 | src: '{,*/}*.svg', 178 | dest: '<%= config.dist %>/images' 179 | }] 180 | } 181 | }, 182 | 183 | htmlmin: { 184 | dist: { 185 | options: { 186 | // removeCommentsFromCDATA: true, 187 | // collapseWhitespace: true, 188 | // collapseBooleanAttributes: true, 189 | // removeAttributeQuotes: true, 190 | // removeRedundantAttributes: true, 191 | // useShortDoctype: true, 192 | // removeEmptyAttributes: true, 193 | // removeOptionalTags: true 194 | }, 195 | files: [{ 196 | expand: true, 197 | cwd: '<%= config.app %>', 198 | src: '*.html', 199 | dest: '<%= config.dist %>' 200 | }] 201 | } 202 | }, 203 | 204 | // By default, your `index.html`'s will take care of 205 | // minification. These next options are pre-configured if you do not wish 206 | // to use the Usemin blocks. 207 | // cssmin: { 208 | // dist: { 209 | // files: { 210 | // '<%= config.dist %>/styles/main.css': [ 211 | // '<%= config.app %>/styles/{,*/}*.css' 212 | // ] 213 | // } 214 | // } 215 | // }, 216 | // uglify: { 217 | // dist: { 218 | // files: { 219 | // '<%= config.dist %>/scripts/scripts.js': [ 220 | // '<%= config.dist %>/scripts/scripts.js' 221 | // ] 222 | // } 223 | // } 224 | // }, 225 | // concat: { 226 | // dist: {} 227 | // }, 228 | 229 | // Copies remaining files to places other tasks can use 230 | copy: { 231 | dist: { 232 | files: [{ 233 | expand: true, 234 | dot: true, 235 | cwd: '<%= config.app %>', 236 | dest: '<%= config.dist %>', 237 | src: [ 238 | '*.{ico,png,txt}', 239 | 'images/{,*/}*.{webp,gif}', 240 | '{,*/}*.html', 241 | '{,*/}*.js', 242 | 'styles/{,*/}*.css', 243 | 'styles/fonts/{,*/}*.*', 244 | '_locales/{,*/}*.json', 245 | ] 246 | }] 247 | } 248 | }, 249 | 250 | // Run some tasks in parallel to speed up build process 251 | concurrent: { 252 | chrome: [ 253 | ], 254 | dist: [ 255 | 'imagemin', 256 | 'svgmin' 257 | ], 258 | test: [ 259 | ] 260 | }, 261 | 262 | // Auto buildnumber, exclude debug files. smart builds that event pages 263 | chromeManifest: { 264 | dist: { 265 | options: { 266 | buildnumber: true, 267 | background: { 268 | target: 'scripts/background.js', 269 | exclude: [ 270 | 'scripts/chromereload.js' 271 | ] 272 | } 273 | }, 274 | src: '<%= config.app %>', 275 | dest: '<%= config.dist %>' 276 | } 277 | }, 278 | 279 | // Compres dist files to package 280 | compress: { 281 | dist: { 282 | options: { 283 | archive: function() { 284 | var manifest = grunt.file.readJSON('app/manifest.json'); 285 | return 'package/Chrome Tab Search-' + manifest.version + '.zip'; 286 | } 287 | }, 288 | files: [{ 289 | expand: true, 290 | cwd: 'dist/', 291 | src: ['**'], 292 | dest: '' 293 | }] 294 | } 295 | } 296 | }); 297 | 298 | grunt.registerTask('debug', function () { 299 | grunt.task.run([ 300 | 'concurrent:chrome', 301 | 'connect:chrome', 302 | 'watch' 303 | ]); 304 | }); 305 | 306 | grunt.registerTask('test', [ 307 | 'connect:test', 308 | 'mocha' 309 | ]); 310 | 311 | grunt.registerTask('build', [ 312 | 'clean:dist', 313 | 'chromeManifest:dist', 314 | 'useminPrepare', 315 | 'concurrent:dist', 316 | // 'cssmin', 317 | 'concat', 318 | 'uglify', 319 | 'copy', 320 | 'usemin', 321 | 'compress' 322 | ]); 323 | 324 | grunt.registerTask('default', [ 325 | 'test', 326 | 'build' 327 | ]); 328 | }; 329 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Tablight 2 | =============== 3 | 4 | ![Logo](/app/images/icon-128.png "Logo") 5 | 6 | Mac Spotlight search for Chrome tabs 7 | 8 | [Website](https://fouad.co/tablight) 9 | -------------------------------------------------------------------------------- /app/_locales/en/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "appName": { 3 | "message": "Tablight", 4 | "description": "The name of the application" 5 | }, 6 | "appDescription": { 7 | "message": "Hot key (Cmd/Ctrl + O) that pulls up spotlight-like interface for open tabs.", 8 | "description": "The description of the application" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /app/images/icon-128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fouad/chrome-tablight/93221f601fb72ef3f6af4e22fda1b688fe634cec/app/images/icon-128.png -------------------------------------------------------------------------------- /app/images/icon-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fouad/chrome-tablight/93221f601fb72ef3f6af4e22fda1b688fe634cec/app/images/icon-16.png -------------------------------------------------------------------------------- /app/images/icon-48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fouad/chrome-tablight/93221f601fb72ef3f6af4e22fda1b688fe634cec/app/images/icon-48.png -------------------------------------------------------------------------------- /app/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "__MSG_appName__", 3 | "version": "0.0.7", 4 | "manifest_version": 2, 5 | "description": "__MSG_appDescription__", 6 | "icons": { 7 | "16": "images/icon-16.png", 8 | "48": "images/icon-48.png", 9 | "128": "images/icon-128.png" 10 | }, 11 | "default_locale": "en", 12 | "background": { 13 | "scripts": [ 14 | "scripts/chromereload.js", 15 | "scripts/background.js" 16 | ] 17 | }, 18 | "commands": { 19 | "open-search": { 20 | "description": "open search", 21 | "global": false, 22 | "suggested_key": { 23 | "default": "Ctrl+Shift+O", 24 | "mac": "Command+Shift+O" 25 | } 26 | } 27 | }, 28 | "content_security_policy": "script-src 'self' https://ssl.google-analytics.com; object-src 'self'", 29 | "permissions": [ 30 | "tabs", 31 | "activeTab" 32 | ] 33 | } -------------------------------------------------------------------------------- /app/pages/notice.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |
7 | 8 |

Tablight

9 |

v

10 |
11 | 12 |
13 |

New update!

14 | 15 |
16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /app/pages/permission.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |
7 | 8 |

Tablight

9 |

v

10 |
11 | 12 |
13 |

Thanks for Installing!

14 |

15 | Press to search (Change the shortcut) 16 |

17 |

18 | I'd like to know how many people find this extension useful and which 19 | features actually help. All data collected is anonymized and does not 20 | contain any info about the tabs you're on. 21 |

22 |

Is it OK to share usage metrics with this developer?

23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /app/scripts/background.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | console.log('Tab Search starting!'); 4 | var _gaq = _gaq || []; 5 | var _tracking = false; 6 | 7 | chrome.runtime.onInstalled.addListener(function(details){ 8 | if(details.reason == "install"){ 9 | var permissionUrl = chrome.extension.getURL('pages/permission.html'); 10 | 11 | chrome.tabs.create({url: permissionUrl}); 12 | }else if(details.reason == "update"){ 13 | var thisVersion = chrome.runtime.getManifest().version; 14 | console.log("Updated from " + details.previousVersion + " to " + thisVersion + "!"); 15 | var noticeUrl = chrome.extension.getURL('pages/permission.html'); 16 | 17 | chrome.tabs.create({url: noticeUrl}); 18 | } 19 | }); 20 | 21 | chrome.commands.onCommand.addListener(function(command) { 22 | var contentScript = chrome.extension.getURL('scripts/contentscript.js'); 23 | var contentStyle = chrome.extension.getURL('styles/main.css'); 24 | 25 | chrome.tabs.query({active: true}, function(tabs) { 26 | var currentTab = tabs[0]; 27 | console.log(contentScript); 28 | 29 | chrome.tabs.executeScript({file: 'scripts/contentscript.js'}, function() { 30 | var port = chrome.tabs.connect(currentTab.id); 31 | 32 | chrome.tabs.query({active: false}, function(tabs) { 33 | port.postMessage({tabs: tabs}); 34 | port.onMessage.addListener(handleSpotlight.bind(port)); 35 | }); 36 | }); 37 | chrome.tabs.insertCSS({file: 'styles/main.css'}); 38 | }); 39 | }); 40 | 41 | function setupTracking() { 42 | if (!_tracking) { 43 | return; 44 | } 45 | 46 | _gaq.push(['_setAccount', 'UA-58195300-1']); 47 | _gaq.push(['_trackPageview']); 48 | 49 | (function() { 50 | var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true; 51 | ga.src = 'https://ssl.google-analytics.com/ga.js'; 52 | var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s); 53 | })(); 54 | } 55 | 56 | var actions = { 57 | facebook: { 58 | 59 | } 60 | }; 61 | 62 | function lookup(type) { 63 | actions[type]; 64 | } 65 | 66 | function handleSpotlight(request) { 67 | var port = this; 68 | 69 | if (request.hasOwnProperty('query')) { 70 | var query = request.query.trim(); 71 | 72 | if (query.indexOf('fb ') === 0) { 73 | 74 | chrome.tabs.query({url: '*://www.facebook.com/*'}, function(tabs) { 75 | if (tabs.length < 1) { 76 | chrome.tabs.create({ 77 | url: 'https://www.facebook.com', 78 | active: false 79 | }, function() { 80 | lookup('facebook'); 81 | }); 82 | } else { 83 | lookup('facebook'); 84 | } 85 | }); 86 | } 87 | // chrome.tabs.query({active: false}, function(tabs) { 88 | // chrome.tabs.sendMessage(port.sender.tab.id, {tabs: tabs}); 89 | // }); 90 | } else if (request.hasOwnProperty('activate')) { 91 | if (_tracking) { 92 | _gaq.push(['_trackEvent', 'Background', 'Activate']); 93 | } 94 | 95 | chrome.tabs.update(request.activate, {active: true}); 96 | } else if (request.hasOwnProperty('version')) { 97 | chrome.commands.getAll(function(commands) { 98 | port.postMessage({ 99 | version: chrome.runtime.getManifest().version, 100 | command: commands[0] 101 | }); 102 | }); 103 | } else if (request.hasOwnProperty('analytics')) { 104 | _tracking = true; 105 | setupTracking(); 106 | chrome.tabs.remove(port.sender.tab.id); 107 | } 108 | } 109 | 110 | chrome.runtime.onConnect.addListener(function(port) { 111 | port.onMessage.addListener(handleSpotlight.bind(port)); 112 | }); 113 | -------------------------------------------------------------------------------- /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 | var LIVERELOAD_HOST = 'localhost:'; 8 | var LIVERELOAD_PORT = 35729; 9 | var connection = new WebSocket('ws://' + LIVERELOAD_HOST + LIVERELOAD_PORT + '/livereload'); 10 | 11 | connection.onerror = function (error) { 12 | console.log('reload connection got error' + JSON.stringify(error)); 13 | }; 14 | 15 | connection.onmessage = function (e) { 16 | if (e.data) { 17 | var data = JSON.parse(e.data); 18 | if (data && data.command === 'reload') { 19 | chrome.runtime.reload(); 20 | } 21 | } 22 | }; 23 | -------------------------------------------------------------------------------- /app/scripts/contentscript.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var holder, tabList, _port, _cursor; 4 | var opened = false; 5 | var _tabs = []; 6 | 7 | function _listTab(tab) { 8 | var item = document.createElement('div'); 9 | item.classList.add('tab-item'); 10 | 11 | var title = document.createElement('h2'); 12 | title.classList.add('tab--title'); 13 | title.innerText = tab.title; 14 | 15 | var link = document.createElement('p'); 16 | link.classList.add('tab--link'); 17 | link.innerText = tab.url; 18 | 19 | item.appendChild(title); 20 | item.appendChild(link); 21 | item.tabId = tab.id; 22 | 23 | function navigateTab() { 24 | _closeSearch(); 25 | _port.postMessage({activate: item.tabId}); 26 | } 27 | 28 | item.addEventListener('click', navigateTab); 29 | item.navigate = navigateTab; 30 | 31 | tabList.appendChild(item); 32 | } 33 | 34 | function _openSearch(port) { 35 | console.log(port); 36 | _port = port; 37 | opened = true; 38 | holder = document.createElement('div'); 39 | holder.classList.add('tab-search'); 40 | tabList = document.createElement('div'); 41 | tabList.classList.add('tab-list'); 42 | 43 | setTimeout(function() { 44 | holder.classList.add('fadeIn'); 45 | }, 0); 46 | 47 | var input = document.createElement('input'); 48 | 49 | input.placeholder = 'Tab Search'; 50 | 51 | holder.appendChild(input); 52 | holder.appendChild(tabList); 53 | document.body.appendChild(holder); 54 | 55 | input.focus(); 56 | 57 | input.addEventListener('click', function(e) { 58 | e.stopPropagation(); 59 | }, false); 60 | document.addEventListener('keydown', function(e) { 61 | if (e.keyCode === 27) { 62 | return _closeSearch(); 63 | } 64 | }); 65 | input.addEventListener('keydown', function(e) { 66 | var val = input.value.trim(); 67 | 68 | // por 69 | 70 | if (e.keyCode === 38) { 71 | 72 | } else if (e.keyCode === 40) { 73 | 74 | } else if (e.keyCode === 27) { 75 | return _closeSearch(); 76 | } else if (e.keyCode === 8 && val.length === 0) { 77 | return _closeSearch(); 78 | } else if (e.keyCode === 13 && val.length > 0) { 79 | var tabs = _tabs.filter(function(tab) { 80 | return tab.url.indexOf(val) >= 0 || tab.title.trim().toLowerCase().indexOf(val) >= 0; 81 | }); 82 | 83 | if (tabs.length > 0) { 84 | tabList.firstChild.navigate(); 85 | } 86 | } 87 | }); 88 | 89 | input.addEventListener('keyup', function() { 90 | var val = input.value.trim().toLowerCase(); 91 | 92 | _port.postMessage({query: val}); 93 | 94 | while (tabList.firstChild) { 95 | tabList.removeChild(tabList.firstChild); 96 | } 97 | 98 | if (val.length === 0) { 99 | return; 100 | } 101 | 102 | _cursor = null; 103 | 104 | var tabs = _tabs.filter(function(tab) { 105 | return tab.url.indexOf(val) >= 0 || tab.title.trim().toLowerCase().indexOf(val) >= 0; 106 | }); 107 | 108 | tabs.map(_listTab); 109 | }); 110 | } 111 | 112 | function _closeSearch() { 113 | opened = false; 114 | 115 | if (holder) { 116 | holder.classList.remove('fadeIn'); 117 | } 118 | setTimeout(function() { 119 | if (holder) { 120 | holder.parentNode.removeChild(holder); 121 | } 122 | 123 | holder = null; 124 | tabList = null; 125 | _port = null; 126 | }, 250); 127 | } 128 | 129 | function _listenBack() { 130 | _closeSearch(); 131 | document.removeEventListener('click', _listenBack); 132 | } 133 | 134 | function _listen() { 135 | document.addEventListener('keydown', function handleKeyup(e) { 136 | if ((e.ctrlKey || e.metaKey) && e.keyCode === 79) { 137 | e.preventDefault(); 138 | 139 | if (!opened) { 140 | _openSearch(); 141 | document.addEventListener('click', _listenBack); 142 | } 143 | 144 | return false; 145 | } 146 | }, false); 147 | } 148 | 149 | function loadTabs(response) { 150 | _tabs = response.tabs; 151 | } 152 | 153 | function TabSearch() { 154 | chrome.runtime.onConnect.addListener(function(port) { 155 | port.onMessage.addListener(function(msg) { 156 | if (msg.hasOwnProperty('tabs')) { 157 | loadTabs(msg); 158 | 159 | if (!opened) { 160 | _openSearch(port); 161 | document.addEventListener('click', _listenBack); 162 | } 163 | } 164 | }); 165 | }); 166 | 167 | // port = chrome.runtime.connect({name: 'tab-search'}); 168 | 169 | // port.postMessage({query: ''}); 170 | // port.onMessage.addListener(loadTabs); 171 | // chrome.runtime.onMessage.addListener(loadTabs); 172 | } 173 | 174 | // _listen(); 175 | TabSearch(); 176 | -------------------------------------------------------------------------------- /app/scripts/notice.js: -------------------------------------------------------------------------------- 1 | var port = chrome.runtime.connect({name: "tablight"}); 2 | var vspan = document.querySelector('.version'); 3 | var osCut = document.querySelector('.os'); 4 | 5 | port.onMessage.addListener(function(msg) { 6 | vspan.innerText = msg.version; 7 | osCut.innerText = msg.command.shortcut; 8 | console.log(msg.command); 9 | }); 10 | port.postMessage({ 11 | version: true 12 | }); 13 | 14 | var decline = document.querySelector('.analytics-decline'); 15 | var accept = document.querySelector('.analytics-accept'); 16 | var configure = document.querySelector('.configure'); 17 | 18 | decline.addEventListener('click', function() { 19 | window.close(); 20 | }); 21 | 22 | configure.addEventListener('click', function() { 23 | chrome.tabs.create({url: 'chrome://extensions/configureCommands'}); 24 | }); 25 | 26 | accept.addEventListener('click', function() { 27 | port.postMessage({ 28 | analytics: true 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /app/styles/background.css: -------------------------------------------------------------------------------- 1 | body { 2 | /*background: #f8f8f8;*/ 3 | background: #cbd9e0; 4 | -webkit-font-smoothing: antialiased; 5 | } 6 | h1, h2, h3, h4, p { 7 | font-weight: 400; 8 | font-family: 'Open Sans'; 9 | /*-webkit-font-smoothing: antialiased;*/ 10 | } 11 | header { 12 | padding: 20px 0; 13 | } 14 | header, 15 | .container { 16 | text-align: center; 17 | color: rgba(55,55,55,.85); 18 | } 19 | h4 { 20 | font-weight: bold; 21 | } 22 | .container p { 23 | max-width: 500px; 24 | margin: 15px auto; 25 | font-size: 16px; 26 | } 27 | .container button { 28 | margin: 15px 3px; 29 | padding: 8px 16px; 30 | font-size: 16px; 31 | background: none; 32 | color: rgba(100,100,100,.85); 33 | border: 2px solid; 34 | border-radius: 4px; 35 | cursor: pointer; 36 | transition: background .35s ease-out; 37 | outline: none; 38 | } 39 | 40 | .container button:hover { 41 | background: rgba(100,100,100,.1); 42 | } 43 | 44 | .container button:active { 45 | background: rgba(0,0,0,.1); 46 | } 47 | .container button.analytics-accept { 48 | color: white; 49 | background: rgba(68, 138, 255, 1); 50 | border-color: rgba(68, 138, 255, 1); 51 | } 52 | .container button.analytics-accept:hover { 53 | color: white; 54 | background: rgba(68, 138, 255, .85); 55 | border-color: rgba(68, 138, 255, .85); 56 | } 57 | .container button.analytics-accept { 58 | color: white; 59 | background: rgba(68, 138, 255, 1); 60 | border-color: rgba(68, 138, 255, 1); 61 | } 62 | -------------------------------------------------------------------------------- /app/styles/main.css: -------------------------------------------------------------------------------- 1 | .tab-search, 2 | .tab-search input, 3 | .tab-list { 4 | box-sizing: border-box; 5 | } 6 | .tab-search { 7 | position: fixed; 8 | top: 30%; 9 | width: 70%; 10 | height: 70px; 11 | margin-left: 15%; 12 | opacity: 0; 13 | z-index: 5000; 14 | transform: translateY(20px) scale(.85); 15 | transition: all .25s ease-out; 16 | } 17 | .tab-search.fadeIn { 18 | opacity: 1; 19 | transform: translateY(0) scale(.9999); 20 | } 21 | .tab-search input, 22 | .tab-search input:focus { 23 | position: relative; 24 | width: 100%; 25 | height: 100%; 26 | margin: 0; 27 | padding: 8px 16px; 28 | font-size: 38px; 29 | line-height: normal; 30 | background: rgba(255,255,255,.96); 31 | border: none; 32 | border-radius: 4px; 33 | z-index: 300; 34 | box-shadow: 0 5px 30px rgba(50,50,50,.85); 35 | } 36 | .tab-search input:focus { 37 | outline: none; 38 | } 39 | .tab-list { 40 | max-height: 400px; 41 | overflow-y: scroll; 42 | padding-top: 15px; 43 | } 44 | .tab-item { 45 | width: 90%; 46 | margin: 0 auto 15px; 47 | padding: 10px 20px; 48 | background: white; 49 | border-radius: 4px; 50 | text-align: left; 51 | /*box-shadow: 0 2px 6px rgba(0,0,0,.35);*/ 52 | box-shadow: 0 2px 10px rgba(50,50,50,.45); 53 | cursor: pointer; 54 | transition: background .45s ease-out; 55 | } 56 | .tab-item:hover { 57 | background: #CFD8DC; 58 | } 59 | .tab--title { 60 | margin: 15px 0 0; 61 | font-weight: 400; 62 | font-size: 22px; 63 | line-height: normal; 64 | color: #455A64; 65 | transition: color .15s ease-out; 66 | } 67 | .tab-item:hover .tab--title { 68 | color: #37474F; 69 | } 70 | .tab--link { 71 | margin: 8px 0; 72 | font-size: 14px; 73 | line-height: 18px; 74 | color: #78909C; 75 | } 76 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "chrome-tab-search", 3 | "version": "0.0.0", 4 | "dependencies": {}, 5 | "devDependencies": {} 6 | } 7 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "chrome-tab-search", 3 | "version": "0.0.1", 4 | "dependencies": {}, 5 | "devDependencies": { 6 | "grunt": "~0.4.1", 7 | "grunt-contrib-copy": "~0.5.0", 8 | "grunt-contrib-concat": "~0.3.0", 9 | "grunt-contrib-uglify": "~0.4.0", 10 | "grunt-contrib-jshint": "~0.9.2", 11 | "grunt-contrib-cssmin": "~0.9.0", 12 | "grunt-contrib-connect": "~0.7.1", 13 | "grunt-contrib-clean": "~0.5.0", 14 | "grunt-contrib-htmlmin": "~0.2.0", 15 | "grunt-bower-install": "~1.0.0", 16 | "grunt-contrib-imagemin": "~0.7.1", 17 | "grunt-contrib-watch": "~0.6.1", 18 | "grunt-usemin": "~2.1.0", 19 | "grunt-mocha": "~0.4.10", 20 | "grunt-svgmin": "~0.4.0", 21 | "grunt-concurrent": "~0.5.0", 22 | "load-grunt-tasks": "~0.4.0", 23 | "time-grunt": "~0.3.1", 24 | "jshint-stylish": "~0.1.5", 25 | "grunt-chrome-manifest": "~0.2.0", 26 | "grunt-contrib-compress": "~0.9.1" 27 | }, 28 | "engines": { 29 | "node": ">=0.8.0" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /promo/large-tile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fouad/chrome-tablight/93221f601fb72ef3f6af4e22fda1b688fe634cec/promo/large-tile.png -------------------------------------------------------------------------------- /promo/marquee.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fouad/chrome-tablight/93221f601fb72ef3f6af4e22fda1b688fe634cec/promo/marquee.png -------------------------------------------------------------------------------- /promo/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fouad/chrome-tablight/93221f601fb72ef3f6af4e22fda1b688fe634cec/promo/screenshot.png -------------------------------------------------------------------------------- /promo/small-tile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fouad/chrome-tablight/93221f601fb72ef3f6af4e22fda1b688fe634cec/promo/small-tile.png -------------------------------------------------------------------------------- /test/.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory": "bower_components" 3 | } 4 | -------------------------------------------------------------------------------- /test/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tab-search", 3 | "private": true, 4 | "dependencies": { 5 | "chai": "~1.8.0", 6 | "mocha": "~1.14.0" 7 | }, 8 | "devDependencies": {} 9 | } 10 | -------------------------------------------------------------------------------- /test/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Mocha Spec Runner 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /test/spec/test.js: -------------------------------------------------------------------------------- 1 | /* global describe, it */ 2 | 3 | (function () { 4 | 'use strict'; 5 | 6 | describe('Give it some context', function () { 7 | describe('maybe a bit more context here', function () { 8 | it('should run here few assertions', function () { 9 | 10 | }); 11 | }); 12 | }); 13 | })(); 14 | --------------------------------------------------------------------------------