From 33504dfae4410f124e5e2c4ad8b7400fa50fe936 Mon Sep 17 00:00:00 2001 From: Fouad Matin Date: Sat, 3 Jan 2015 12:34:50 -0800 Subject: [PATCH] initial v0.0.2 --- .bowerrc | 3 + .editorconfig | 21 +++ .gitattributes | 1 + .gitignore | 8 + .jshintrc | 24 +++ Gruntfile.js | 327 ++++++++++++++++++++++++++++++++++ app/_locales/en/messages.json | 10 ++ app/images/icon-128.png | Bin 0 -> 4420 bytes app/images/icon-16.png | Bin 0 -> 1364 bytes app/images/icon-48.png | Bin 0 -> 2138 bytes app/manifest.json | 40 +++++ app/scripts/background.js | 30 ++++ app/scripts/chromereload.js | 22 +++ app/scripts/contentscript.js | 148 +++++++++++++++ app/styles/main.css | 74 ++++++++ bower.json | 6 + package.json | 31 ++++ test/.bowerrc | 3 + test/bower.json | 9 + test/index.html | 27 +++ test/spec/test.js | 13 ++ 21 files changed, 797 insertions(+) create mode 100644 .bowerrc create mode 100644 .editorconfig create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 .jshintrc create mode 100644 Gruntfile.js create mode 100644 app/_locales/en/messages.json create mode 100644 app/images/icon-128.png create mode 100644 app/images/icon-16.png create mode 100644 app/images/icon-48.png create mode 100644 app/manifest.json create mode 100644 app/scripts/background.js create mode 100644 app/scripts/chromereload.js create mode 100644 app/scripts/contentscript.js create mode 100644 app/styles/main.css create mode 100644 bower.json create mode 100644 package.json create mode 100644 test/.bowerrc create mode 100644 test/bower.json create mode 100644 test/index.html create mode 100644 test/spec/test.js diff --git a/.bowerrc b/.bowerrc new file mode 100644 index 0000000..ba0accc --- /dev/null +++ b/.bowerrc @@ -0,0 +1,3 @@ +{ + "directory": "app/bower_components" +} diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..8a80734 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,21 @@ +# EditorConfig helps developers define and maintain consistent +# coding styles between different editors and IDEs +# editorconfig.org + +root = true + + +[*] + +# Change these settings to your own preference +indent_style = space +indent_size = 4 + +# We recommend you to keep these unchanged +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.md] +trim_trailing_whitespace = false diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..2125666 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +* text=auto \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5a88969 --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +node_modules +temp +.tmp +dist +.sass-cache +app/bower_components +test/bower_components +package diff --git a/.jshintrc b/.jshintrc new file mode 100644 index 0000000..06a890e --- /dev/null +++ b/.jshintrc @@ -0,0 +1,24 @@ +{ + "node": true, + "browser": true, + "esnext": true, + "bitwise": true, + "camelcase": true, + "curly": true, + "eqeqeq": true, + "immed": true, + "indent": 4, + "latedef": true, + "newcap": true, + "noarg": true, + "quotmark": "single", + "regexp": true, + "undef": true, + "unused": true, + "strict": true, + "trailing": true, + "smarttabs": true, + "globals" : { + "chrome": true + } +} diff --git a/Gruntfile.js b/Gruntfile.js new file mode 100644 index 0000000..d2b75f5 --- /dev/null +++ b/Gruntfile.js @@ -0,0 +1,327 @@ +// Generated on 2015-01-03 using generator-chrome-extension 0.2.9 +'use strict'; + +// # Globbing +// for performance reasons we're only matching one level down: +// 'test/spec/{,*/}*.js' +// use this if you want to recursively match all subfolders: +// 'test/spec/**/*.js' + +module.exports = function (grunt) { + + // Load grunt tasks automatically + require('load-grunt-tasks')(grunt); + + // Time how long tasks take. Can help when optimizing build times + require('time-grunt')(grunt); + + // Configurable paths + var config = { + app: 'app', + dist: 'dist' + }; + + grunt.initConfig({ + + // Project settings + config: config, + + // Watches files for changes and runs tasks based on the changed files + watch: { + bower: { + files: ['bower.json'], + tasks: ['bowerInstall'] + }, + js: { + files: ['<%= config.app %>/scripts/{,*/}*.js'], + // tasks: ['jshint'], + options: { + livereload: true + } + }, + gruntfile: { + files: ['Gruntfile.js'] + }, + styles: { + files: ['<%= config.app %>/styles/{,*/}*.css'], + tasks: [], + options: { + livereload: true + } + }, + livereload: { + options: { + livereload: '<%= connect.options.livereload %>' + }, + files: [ + '<%= config.app %>/*.html', + '<%= config.app %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}', + '<%= config.app %>/manifest.json', + '<%= config.app %>/_locales/{,*/}*.json' + ] + } + }, + + // Grunt server and debug server setting + connect: { + options: { + port: 9000, + livereload: 35729, + // change this to '0.0.0.0' to access the server from outside + hostname: 'localhost' + }, + chrome: { + options: { + open: false, + base: [ + '<%= config.app %>' + ] + } + }, + test: { + options: { + open: false, + base: [ + 'test', + '<%= config.app %>' + ] + } + } + }, + + // Empties folders to start fresh + clean: { + chrome: { + }, + dist: { + files: [{ + dot: true, + src: [ + '<%= config.dist %>/*', + '!<%= config.dist %>/.git*' + ] + }] + } + }, + + // Make sure code styles are up to par and there are no obvious mistakes + jshint: { + options: { + jshintrc: '.jshintrc', + reporter: require('jshint-stylish') + }, + all: [ + 'Gruntfile.js', + '<%= config.app %>/scripts/{,*/}*.js', + '!<%= config.app %>/scripts/vendor/*', + 'test/spec/{,*/}*.js' + ] + }, + mocha: { + all: { + options: { + run: true, + urls: ['http://localhost:<%= connect.options.port %>/index.html'] + } + } + }, + + // Automatically inject Bower components into the HTML file + bowerInstall: { + app: { + src: [ + '<%= config.app %>/*.html' + ] + } + }, + + // Reads HTML for usemin blocks to enable smart builds that automatically + // concat, minify and revision files. Creates configurations in memory so + // additional tasks can operate on them + useminPrepare: { + options: { + dest: '<%= config.dist %>' + }, + html: [ + '<%= config.app %>/popup.html', + '<%= config.app %>/options.html' + ] + }, + + // Performs rewrites based on rev and the useminPrepare configuration + usemin: { + options: { + assetsDirs: ['<%= config.dist %>', '<%= config.dist %>/images'] + }, + html: ['<%= config.dist %>/{,*/}*.html'], + css: ['<%= config.dist %>/styles/{,*/}*.css'] + }, + + // The following *-min tasks produce minifies files in the dist folder + imagemin: { + dist: { + files: [{ + expand: true, + cwd: '<%= config.app %>/images', + src: '{,*/}*.{gif,jpeg,jpg,png}', + dest: '<%= config.dist %>/images' + }] + } + }, + + svgmin: { + dist: { + files: [{ + expand: true, + cwd: '<%= config.app %>/images', + src: '{,*/}*.svg', + dest: '<%= config.dist %>/images' + }] + } + }, + + htmlmin: { + dist: { + options: { + // removeCommentsFromCDATA: true, + // collapseWhitespace: true, + // collapseBooleanAttributes: true, + // removeAttributeQuotes: true, + // removeRedundantAttributes: true, + // useShortDoctype: true, + // removeEmptyAttributes: true, + // removeOptionalTags: true + }, + files: [{ + expand: true, + cwd: '<%= config.app %>', + src: '*.html', + dest: '<%= config.dist %>' + }] + } + }, + + // By default, your `index.html`'s will take care of + // minification. These next options are pre-configured if you do not wish + // to use the Usemin blocks. + // cssmin: { + // dist: { + // files: { + // '<%= config.dist %>/styles/main.css': [ + // '<%= config.app %>/styles/{,*/}*.css' + // ] + // } + // } + // }, + // uglify: { + // dist: { + // files: { + // '<%= config.dist %>/scripts/scripts.js': [ + // '<%= config.dist %>/scripts/scripts.js' + // ] + // } + // } + // }, + // concat: { + // dist: {} + // }, + + // Copies remaining files to places other tasks can use + copy: { + dist: { + files: [{ + expand: true, + dot: true, + cwd: '<%= config.app %>', + dest: '<%= config.dist %>', + src: [ + '*.{ico,png,txt}', + 'images/{,*/}*.{webp,gif}', + '{,*/}*.html', + 'styles/{,*/}*.css', + 'styles/fonts/{,*/}*.*', + '_locales/{,*/}*.json', + ] + }] + } + }, + + // Run some tasks in parallel to speed up build process + concurrent: { + chrome: [ + ], + dist: [ + 'imagemin', + 'svgmin' + ], + test: [ + ] + }, + + // Auto buildnumber, exclude debug files. smart builds that event pages + chromeManifest: { + dist: { + options: { + buildnumber: true, + background: { + target: 'scripts/background.js', + exclude: [ + 'scripts/chromereload.js' + ] + } + }, + src: '<%= config.app %>', + dest: '<%= config.dist %>' + } + }, + + // Compres dist files to package + compress: { + dist: { + options: { + archive: function() { + var manifest = grunt.file.readJSON('app/manifest.json'); + return 'package/Chrome Tab Search-' + manifest.version + '.zip'; + } + }, + files: [{ + expand: true, + cwd: 'dist/', + src: ['**'], + dest: '' + }] + } + } + }); + + grunt.registerTask('debug', function () { + grunt.task.run([ + 'concurrent:chrome', + 'connect:chrome', + 'watch' + ]); + }); + + grunt.registerTask('test', [ + 'connect:test', + 'mocha' + ]); + + grunt.registerTask('build', [ + 'clean:dist', + 'chromeManifest:dist', + 'useminPrepare', + 'concurrent:dist', + 'cssmin', + 'concat', + 'uglify', + 'copy', + 'usemin', + 'compress' + ]); + + grunt.registerTask('default', [ + 'test', + 'build' + ]); +}; diff --git a/app/_locales/en/messages.json b/app/_locales/en/messages.json new file mode 100644 index 0000000..13646a9 --- /dev/null +++ b/app/_locales/en/messages.json @@ -0,0 +1,10 @@ +{ + "appName": { + "message": "Chrome Tab Search", + "description": "The name of the application" + }, + "appDescription": { + "message": "Hot key (Cmd/Ctrl + O) that pulls up spotlight-like interface for open tabs.", + "description": "The description of the application" + } +} diff --git a/app/images/icon-128.png b/app/images/icon-128.png new file mode 100644 index 0000000000000000000000000000000000000000..2a5c977b54e8e188b56f248e05941bcc6d350076 GIT binary patch literal 4420 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 literal 0 HcmV?d00001 diff --git a/app/images/icon-16.png b/app/images/icon-16.png new file mode 100644 index 0000000000000000000000000000000000000000..1558361869807a92130baea590dbbfa9d301a86e GIT binary patch literal 1364 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+n3Xd_B1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxR5#hc&_u!9QqR!T z(8R(}N5ROz&{*HVSl`fC*U-qyz|zXlQ~?TIxIyg#@@$ndN=gc>^!3Zj z%k|2Q_413-^$jg8E%gnI^o@*kfhu&1EAvVcD|GXUm0>2hq!uR^WfqiV=I1GZOiWD5 zFD$Tv3bSNU;+l1ennz|zM-B0$V)JVzP|XC=H|jx7ncO3BHWAB;NpiyW)Z+ZoqGVvir744~DzI`cN=+=uFAB-e&w+(vKt_H^esM;Afr7KMf`)Hma%LWg zuL;)R>ucqiS6q^qmz?V9Vygr+LN7Bj#md~!(%jO-)!4$x(ZtZu)x^@s&Dqk)*xAXz z(8$oy&=scFB|o_|H#M&WrZ)wl*950tP+G_>0NU)5T9jFqn&MWJpQ`}&vsES*w^%wk zn;W>9TL8^7#_kq~-W1$!F~aEsMnhRzv;(cmfWv2m*w+miyyUp2iev)t=ZDc*O}?;uGGV6O?sZjm=(Iu~iv$n%Zek>U?$}rc~qSjZ>#&t{7NO?H2Gz&sdZvRwFL^ zuGu>IvHB`mXTRUUVag#bS7YU~@7KiK<9NbuGUI{xQnmvwZ?d{SzCPm2oPOcO;d{oq zg?G)aG0cea?`Ar@_tvC}^$jumH|?r=th8F*Zu6G^=MNuYZ=HF5qVMk2rpG=meSTy8 zqX(`GI~osOTj=8My^=e;IXM28}Hp5|)P0s%bJ2)FRDt_(p ziG13=yk^Q#fnQ&jyUf}=>0a{Xmx2wq#Xm^gj=L`Gyhv8-;k#RJIRek@4zM`%;;U_t pOa6VQa{*6(ZE$`c`<$JTjbZ!t^^;2fPvroWbDpk#F6*2UngFxm`OE+S literal 0 HcmV?d00001 diff --git a/app/images/icon-48.png b/app/images/icon-48.png new file mode 100644 index 0000000000000000000000000000000000000000..45f05fd68ebd9895bb335ca9108ea7b85f630047 GIT binary patch literal 2138 zcmaJ?X;f2Z8ooe5MHmp2O_mULl&owCge3_}ETI%a4T#c^Tp(g{LvmR~3k;hq6;N6& zo5CO{7Gz3A6bK^4Vh4e81O_AmgCYn9hAOgXC$VDZ$1wMtd%y4f&htF)`@QdT&OH<0 z@1w42pb7whI?b0FC|fb|cc+T%dsuNTTej%KUQ9R$ii8tbLJ;ueKoKB<#$!c+fgp>M z6x$2B0)Ubkm%)UYbUz{+;-Oe_3@VW)kg);4)jd(bV#k0mA_9!!@<~YP9WfHY<&cnv z9q1UkfC5HyeUpV?P_jRRogBj^aFFh92-ifSOn?W%EJPyj1Ybl_ka?n3ZV9 z7nyA-P(cw}$Q8g`h>wsfvLc{3n1qy7`o|S`f{(I%(T6t48ir0}3D8&+Mt-F&Af5jI zp*-G4v= zdLOu)?Q3z^mW!4ZgO*SBzb3tPB(slv`_Z?u;G^?FzRd1InXgOdt_cC)lRg^NlaV;K zaB%_(iqpF<7OxD)*OZ?SeKD$Hd17#`E2o$9c@8mLfzlR|b5Yk@t2%7br1Qt+uKi~c zEVZO<0iAj|nrb40%uddmuAVYn`A&ZIfr;UYiVEGzS(uk{RWRn@Qa?xg&T@A1VWYTT zy5`ouLaObGHDs)q9tRv!4Lo8~uPggm4O;H9ZYj-(8sK!7B7K>T7(CQ+D0Tj+o0ry^ zo}8S+dnY4R?)E|q zzd7uU@;K*ecW;Igz3?@4*Q9x?gJ;o##M)$4ugUK#4N2Q7KYzJZJhw3{&mjM_ftG zp`?()0>vdG!vIr>pVoz$jc?s!=`od`0 z-M;3GBAULqulMDT&vFX-c$Tl-OL|7MZ6&|sQoV9>k&TMLF_)7SY1#PZL?x?^M@!32 z29dhdmsLNj6LE$fPu{AmpTzClVQA>8FEq0Z4)6z+OUA#FT59*h`p(4bQ|*_5HLEnu zS7$XJt!z5Y_7trcNQ~4j*FWkO9mywmyiw^4ZK#f1J-E2pZGyAIbQaXq*^lNfjo!s7 zQ`ok~Qu`Y(|FYxCqM64xr&B1^1;Z-4D~GC_L!hS@y8~%PhC`YRBe=nd73!ua_&!`b zEYyDWAb-KehCm+C)+!0!^?kw3el5ipPLW7-PebO^1}AT%tblxW7Bp3llyexVv% za?qfwRatUv3j5nRhVWl`TBxHX6J<-mtu6( zl&5RaF`W!FWTA&F%D&Lsd$pzA_1pRJm=ZC0^HBNQnnmJpmeDG$zh%VsDY;E#o1sqE z(Wx7x1@2RrF`Z^))t<{8pZYXztV3yr=hOPGZa&=Zomf&({7OC2?|hl>futKetNZmj z%Fp@te1>TDh02=sI-Ti*d+yDVR~z4c7N%6Ez;NoUR8#{1^Vf{DaABYPZ%^~`r#6tk GO#cr{AaXJQ literal 0 HcmV?d00001 diff --git a/app/manifest.json b/app/manifest.json new file mode 100644 index 0000000..a2e7e31 --- /dev/null +++ b/app/manifest.json @@ -0,0 +1,40 @@ +{ + "name": "__MSG_appName__", + "version": "0.0.2", + "manifest_version": 2, + "description": "__MSG_appDescription__", + "icons": { + "16": "images/icon-16.png", + "48": "images/icon-48.png", + "128": "images/icon-128.png" + }, + "default_locale": "en", + "background": { + "scripts": [ + "scripts/chromereload.js", + "scripts/background.js" + ] + }, + "content_security_policy": "script-src 'self' https://ssl.google-analytics.com; object-src 'self'", + "content_scripts": [ + { + "matches": [ + "http://*/*", + "https://*/*" + ], + "css": [ + "styles/main.css" + ], + "js": [ + "scripts/contentscript.js" + ], + "run_at": "document_end", + "all_frames": false + } + ], + "permissions": [ + "tabs", + "http://*/*", + "https://*/*" + ] +} \ No newline at end of file diff --git a/app/scripts/background.js b/app/scripts/background.js new file mode 100644 index 0000000..6b390c1 --- /dev/null +++ b/app/scripts/background.js @@ -0,0 +1,30 @@ +'use strict'; + +console.log('Tab Search starting!'); + +var _gaq = _gaq || []; +_gaq.push(['_setAccount', 'UA-58195300-1']); +_gaq.push(['_trackPageview']); + +(function() { + var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true; + ga.src = 'https://ssl.google-analytics.com/ga.js'; + var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s); +})(); + +function handleSpotlight(request) { + var port = this; + + if (request.hasOwnProperty('query')) { + chrome.tabs.query({active: false}, function(tabs) { + chrome.tabs.sendMessage(port.sender.tab.id, {tabs: tabs}); + }); + } else if (request.hasOwnProperty('activate')) { + _gaq.push(['_trackEvent', 'Background', 'Activate']); + chrome.tabs.update(request.activate, {active: true}); + } +} + +chrome.runtime.onConnect.addListener(function(port) { + port.onMessage.addListener(handleSpotlight.bind(port)); +}); diff --git a/app/scripts/chromereload.js b/app/scripts/chromereload.js new file mode 100644 index 0000000..cb07e47 --- /dev/null +++ b/app/scripts/chromereload.js @@ -0,0 +1,22 @@ +'use strict'; + +// Reload client for Chrome Apps & Extensions. +// The reload client has a compatibility with livereload. +// WARNING: only supports reload command. + +var LIVERELOAD_HOST = 'localhost:'; +var LIVERELOAD_PORT = 35729; +var connection = new WebSocket('ws://' + LIVERELOAD_HOST + LIVERELOAD_PORT + '/livereload'); + +connection.onerror = function (error) { + console.log('reload connection got error' + JSON.stringify(error)); +}; + +connection.onmessage = function (e) { + if (e.data) { + var data = JSON.parse(e.data); + if (data && data.command === 'reload') { + chrome.runtime.reload(); + } + } +}; diff --git a/app/scripts/contentscript.js b/app/scripts/contentscript.js new file mode 100644 index 0000000..ca24652 --- /dev/null +++ b/app/scripts/contentscript.js @@ -0,0 +1,148 @@ +'use strict'; + +var holder, tabList, port; +var opened = false; +var _tabs = []; + +function _listTab(tab) { + var item = document.createElement('div'); + item.classList.add('tab-item'); + + var title = document.createElement('h2'); + title.classList.add('tab--title'); + title.innerText = tab.title; + + var link = document.createElement('p'); + link.classList.add('tab--link'); + link.innerText = tab.url; + + item.appendChild(title); + item.appendChild(link); + item.tabId = tab.id; + + function navigateTab() { + _closeSearch(); + port.postMessage({activate: item.tabId}); + } + + item.addEventListener('click', navigateTab); + item.navigate = navigateTab; + + tabList.appendChild(item); +} + +function _openSearch() { + opened = true; + holder = document.createElement('div'); + holder.classList.add('tab-search'); + tabList = document.createElement('div'); + tabList.classList.add('tab-list'); + + setTimeout(function() { + holder.classList.add('fadeIn'); + }, 0); + + var input = document.createElement('input'); + + input.placeholder = 'Tab Search'; + + holder.appendChild(input); + holder.appendChild(tabList); + document.body.appendChild(holder); + + input.focus(); + + input.addEventListener('click', function(e) { + e.stopPropagation(); + }, false); + input.addEventListener('keydown', function(e) { + var val = input.value.trim(); + + port.postMessage({ + query: '' + }); + + if (e.keyCode === 27) { + return _closeSearch(); + } else if (e.keyCode === 8 && val.length === 0) { + return _closeSearch(); + } else if (e.keyCode === 13 && val.length > 0) { + var tabs = _tabs.filter(function(tab) { + return tab.url.indexOf(val) >= 0 || tab.title.trim().toLowerCase().indexOf(val) >= 0; + }); + + if (tabs.length > 0) { + tabList.firstChild.navigate(); + } + } + }); + + input.addEventListener('keyup', function() { + var val = input.value.trim().toLowerCase(); + + while (tabList.firstChild) { + tabList.removeChild(tabList.firstChild); + } + + if (val.length === 0) { + return; + } + + var tabs = _tabs.filter(function(tab) { + return tab.url.indexOf(val) >= 0 || tab.title.trim().toLowerCase().indexOf(val) >= 0; + }); + + tabs.map(_listTab); + }); +} + +function _closeSearch() { + opened = false; + + if (holder) { + holder.classList.remove('fadeIn'); + } + setTimeout(function() { + if (holder) { + holder.parentNode.removeChild(holder); + } + + holder = null; + tabList = null; + }, 250); +} + +function _listenBack() { + _closeSearch(); + document.removeEventListener('click', _listenBack); +} + +function _listen() { + document.addEventListener('keydown', function handleKeyup(e) { + if ((e.ctrlKey || e.metaKey) && e.keyCode === 79) { + e.preventDefault(); + + if (!opened) { + _openSearch(); + document.addEventListener('click', _listenBack); + } + + return false; + } + }, false); +} + +function loadTabs(response) { + _tabs = response.tabs; +} + +function TabSearch() { + port = chrome.runtime.connect({name: 'tab-search'}); + + port.postMessage({query: ''}); + port.onMessage.addListener(loadTabs); + chrome.runtime.onMessage.addListener(loadTabs); +} + +_listen(); +TabSearch(); diff --git a/app/styles/main.css b/app/styles/main.css new file mode 100644 index 0000000..c943c0c --- /dev/null +++ b/app/styles/main.css @@ -0,0 +1,74 @@ +.tab-search, +.tab-search input, +.tab-list { + box-sizing: border-box; +} +.tab-search { + position: fixed; + top: 30%; + width: 70%; + height: 70px; + margin-left: 15%; + opacity: 0; + z-index: 500; + transform: translateY(20px) scale(.85); + transition: all .25s ease-out; +} +.tab-search.fadeIn { + opacity: 1; + transform: translateY(0) scale(.9999); +} +.tab-search input, +.tab-search input:focus { + position: relative; + width: 100%; + height: 100%; + margin: 0; + padding: 8px 16px; + font-size: 38px; + line-height: normal; + background: rgba(255,255,255,.96); + border: none; + border-radius: 4px; + z-index: 300; + box-shadow: 0 5px 30px rgba(50,50,50,.85); +} +.tab-search input:focus { + outline: none; +} +.tab-list { + max-height: 400px; + overflow-y: scroll; + padding-top: 15px; +} +.tab-item { + width: 90%; + margin: 0 auto 15px; + padding: 10px 20px; + background: white; + border-radius: 4px; + text-align: left; + box-shadow: 0 0 3px rgba(0,0,0,.15); + cursor: pointer; + transition: background .45s ease-out; +} +.tab-item:hover { + background: #CFD8DC; +} +.tab--title { + margin: 15px 0 0; + font-weight: 400; + font-size: 22px; + line-height: normal; + color: #455A64; + transition: color .15s ease-out; +} +.tab-item:hover .tab--title { + color: #37474F; +} +.tab--link { + margin: 8px 0; + font-size: 14px; + line-height: 18px; + color: #78909C; +} diff --git a/bower.json b/bower.json new file mode 100644 index 0000000..2dbc60d --- /dev/null +++ b/bower.json @@ -0,0 +1,6 @@ +{ + "name": "chrome-tab-search", + "version": "0.0.0", + "dependencies": {}, + "devDependencies": {} +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..0ed0fed --- /dev/null +++ b/package.json @@ -0,0 +1,31 @@ +{ + "name": "chrome-tab-search", + "version": "0.0.1", + "dependencies": {}, + "devDependencies": { + "grunt": "~0.4.1", + "grunt-contrib-copy": "~0.5.0", + "grunt-contrib-concat": "~0.3.0", + "grunt-contrib-uglify": "~0.4.0", + "grunt-contrib-jshint": "~0.9.2", + "grunt-contrib-cssmin": "~0.9.0", + "grunt-contrib-connect": "~0.7.1", + "grunt-contrib-clean": "~0.5.0", + "grunt-contrib-htmlmin": "~0.2.0", + "grunt-bower-install": "~1.0.0", + "grunt-contrib-imagemin": "~0.7.1", + "grunt-contrib-watch": "~0.6.1", + "grunt-usemin": "~2.1.0", + "grunt-mocha": "~0.4.10", + "grunt-svgmin": "~0.4.0", + "grunt-concurrent": "~0.5.0", + "load-grunt-tasks": "~0.4.0", + "time-grunt": "~0.3.1", + "jshint-stylish": "~0.1.5", + "grunt-chrome-manifest": "~0.2.0", + "grunt-contrib-compress": "~0.9.1" + }, + "engines": { + "node": ">=0.8.0" + } +} diff --git a/test/.bowerrc b/test/.bowerrc new file mode 100644 index 0000000..44491d3 --- /dev/null +++ b/test/.bowerrc @@ -0,0 +1,3 @@ +{ + "directory": "bower_components" +} diff --git a/test/bower.json b/test/bower.json new file mode 100644 index 0000000..db2ceb5 --- /dev/null +++ b/test/bower.json @@ -0,0 +1,9 @@ +{ + "name": "tab-search", + "private": true, + "dependencies": { + "chai": "~1.8.0", + "mocha": "~1.14.0" + }, + "devDependencies": {} +} diff --git a/test/index.html b/test/index.html new file mode 100644 index 0000000..d18b7d3 --- /dev/null +++ b/test/index.html @@ -0,0 +1,27 @@ + + + + + + Mocha Spec Runner + + + +
+ + + + + + + + + + + + + diff --git a/test/spec/test.js b/test/spec/test.js new file mode 100644 index 0000000..adfd614 --- /dev/null +++ b/test/spec/test.js @@ -0,0 +1,13 @@ +/* global describe, it */ + +(function () { + 'use strict'; + + describe('Give it some context', function () { + describe('maybe a bit more context here', function () { + it('should run here few assertions', function () { + + }); + }); + }); +})();