├── dicts └── .gitkeep ├── js ├── processor │ ├── noop.js │ ├── help.js │ ├── visual.js │ ├── insert.js │ ├── command.js │ └── hint.js ├── hint │ ├── action_map.js │ ├── letter_conversion.js │ ├── key_factory.js │ ├── algorithm │ │ ├── custom.js │ │ ├── standard.js │ │ └── comfortable.js │ ├── action.js │ ├── element_collection.js │ └── element.js ├── constants.js ├── background │ ├── favicon.js │ ├── history.js │ ├── tab.js │ └── script.js ├── box │ ├── select.js │ ├── help.js │ └── command.js ├── options.js ├── default_option.js ├── key │ ├── map.js │ └── sequence.js ├── filter.js ├── main.js ├── executer.js ├── viewport.js ├── homedics.js ├── mode.js └── command.js ├── icons ├── icon16.png ├── icon48.png └── icon128.png ├── .gitignore ├── spec ├── support │ ├── jasmine-2.0.0 │ │ ├── jasmine_favicon.png │ │ ├── jasmine.css │ │ ├── console.js │ │ └── boot.js │ ├── custom_matcher.js │ ├── mocking.js │ └── jasmine-ajax.js ├── fixtures │ └── hint │ │ └── element.html ├── spec_helper.js ├── js │ ├── key │ │ └── map_spec.js │ ├── options_spec.js │ ├── hint │ │ └── element_spec.js │ ├── mode_spec.js │ ├── executer_spec.js │ └── homedics_spec.js └── lib │ └── jp_spec.js ├── package.json ├── wercker.yml ├── README.md ├── bin ├── make.sh └── build_dict ├── lib ├── element_wrapper.js ├── stacking_contexts.js ├── utility.js ├── jquery.extend.js ├── jp.js └── dom.js ├── manifest.json ├── karma.conf.js ├── _locales ├── ja │ └── messages.json └── en │ └── messages.json ├── options ├── main.js ├── main.css └── options.html └── css └── main.css /dicts/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /js/processor/noop.js: -------------------------------------------------------------------------------- 1 | var NoopProcessor = function() { 2 | }; 3 | -------------------------------------------------------------------------------- /icons/icon16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkengo/hometype/HEAD/icons/icon16.png -------------------------------------------------------------------------------- /icons/icon48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkengo/hometype/HEAD/icons/icon48.png -------------------------------------------------------------------------------- /icons/icon128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkengo/hometype/HEAD/icons/icon128.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /hometype.zip 2 | /coverage 3 | /node_modules 4 | /dicts/*.ml 5 | .DS_Store 6 | -------------------------------------------------------------------------------- /spec/support/jasmine-2.0.0/jasmine_favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tkengo/hometype/HEAD/spec/support/jasmine-2.0.0/jasmine_favicon.png -------------------------------------------------------------------------------- /spec/fixtures/hint/element.html: -------------------------------------------------------------------------------- 1 |
no class content
2 |
class content
3 |
other content
4 | -------------------------------------------------------------------------------- /js/hint/action_map.js: -------------------------------------------------------------------------------- 1 | var ActionMap = { 2 | 'm': 'mouseover', 3 | 'y': 'yankUrl', 4 | 'w': 'openNewWindow', 5 | 'v': 'viewSource', 6 | 'default': 'click' 7 | }; 8 | -------------------------------------------------------------------------------- /spec/spec_helper.js: -------------------------------------------------------------------------------- 1 | beforeEach(function() { 2 | jasmine.getFixtures().fixturesPath = 'base/spec/fixtures' 3 | jasmine.addMatchers(customMatchers); 4 | }); 5 | 6 | context = describe; 7 | -------------------------------------------------------------------------------- /js/constants.js: -------------------------------------------------------------------------------- 1 | Constant = { 2 | hint_key: { 3 | algorithm: { 4 | standard: "1", 5 | comfortable: "2", 6 | custom: "3" 7 | }, 8 | letter_type: { 9 | lowercase: "1", 10 | uppercase: "2" 11 | } 12 | } 13 | }; 14 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Hometype", 3 | "version": "0.0.0", 4 | "description": "", 5 | "repository": "git@github.com:tkengo/hometype.git", 6 | "devDependencies": { 7 | "karma-jasmine": "^0.2.2", 8 | "karma-coverage": "^0.2.1", 9 | "karma-phantomjs-launcher": "^0.1.4", 10 | "karma": "^0.12.3" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /js/hint/letter_conversion.js: -------------------------------------------------------------------------------- 1 | function convertLettersByOption(characters) 2 | { 3 | switch (Opt.hint_letter_type) { 4 | case Constant.hint_key.letter_type.lowercase: { 5 | return characters.toLowerCase(); 6 | } 7 | case Constant.hint_key.letter_type.uppercase: { 8 | return characters.toUpperCase(); 9 | } 10 | default: { 11 | return characters; 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /wercker.yml: -------------------------------------------------------------------------------- 1 | box: wercker/nodejs@1.0.2 2 | build: 3 | steps: 4 | - aussiegeek/install-phantomjs@0.0.3 5 | - npm-install 6 | - npm-test 7 | - script: 8 | name: execute test 9 | code: ./node_modules/karma/bin/karma start 10 | deploy: 11 | steps: 12 | - wercker/add-ssh-key@0.0.3: 13 | keyname: HOMETYPE_SSH_KEY 14 | - tkengo/update-coverages@0.0.2 15 | -------------------------------------------------------------------------------- /js/processor/help.js: -------------------------------------------------------------------------------- 1 | var HelpModeProcessor = function() { 2 | this.helpBox = new HometypeHelpBox(); 3 | }; 4 | 5 | /** 6 | * Callback method that invoke when enter the help mode. 7 | */ 8 | HelpModeProcessor.prototype.notifyEnterMode = function() { 9 | this.helpBox = new HometypeHelpBox(); 10 | this.helpBox.show(); 11 | }; 12 | 13 | /** 14 | * Callback method that invoke when leave the help mode. 15 | */ 16 | HelpModeProcessor.prototype.notifyLeaveMode = function() { 17 | this.helpBox.hide(); 18 | }; 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Hometype 2 | 3 | This is an extension to operate Google Chrome without touching a mouse. 4 | 5 | See [Hometype page](http://tkengo.github.io/hometype) to get detail. 6 | 7 | [![wercker status](https://app.wercker.com/status/ae1d66ed438061ad9bf7d19b4c00f0c2/m/master "wercker status")](https://app.wercker.com/project/bykey/ae1d66ed438061ad9bf7d19b4c00f0c2) 8 | 9 | # Installation 10 | 11 | Please install from [Chrome webstore](https://chrome.google.com/webstore/detail/hometype/fngmcejiekfcoflogkfnmhableinjmln). 12 | -------------------------------------------------------------------------------- /js/hint/key_factory.js: -------------------------------------------------------------------------------- 1 | var HintKeyFactory = { 2 | create: function(targetLength) { 3 | switch (Opt.hint_key_algorithm) { 4 | case Constant.hint_key.algorithm.standard: 5 | return new StandardHintAlgorithm(targetLength); 6 | case Constant.hint_key.algorithm.comfortable: 7 | return new ComfortableHintAlgorithm(targetLength); 8 | case Constant.hint_key.algorithm.custom: 9 | return new CustomHintAlgorithm(targetLength); 10 | default: 11 | return new StandardHintAlgorithm(targetLength); 12 | } 13 | } 14 | }; 15 | -------------------------------------------------------------------------------- /js/processor/visual.js: -------------------------------------------------------------------------------- 1 | var VisualModeProcessor = function() { 2 | }; 3 | 4 | VisualModeProcessor.prototype.onKeyDown = function(key, currentKey, e) { 5 | var isMetaKey = e.ctrlKey || e.altKey || e.shiftKey || e.metaKey; 6 | if (!isMetaKey && $.inArray(currentKey, [ 'Down', 'Up', 'Right', 'Left' ]) == -1) { 7 | e.stopPropagation(); 8 | e.preventDefault(); 9 | } 10 | }; 11 | 12 | /** 13 | * Callback method that invoke when leave the visual mode. 14 | */ 15 | VisualModeProcessor.prototype.notifyLeaveMode = function() { 16 | Viewport.resetContentEditable(); 17 | }; 18 | -------------------------------------------------------------------------------- /spec/support/custom_matcher.js: -------------------------------------------------------------------------------- 1 | var customMatchers = { 2 | toBeEmpty: function(util, customEqualityTesters) { 3 | return { 4 | compare: function(actual, expected) { 5 | return { 6 | pass: util.equals(actual, {}) || actual == '' || actual == undefined || (actual.length && actual.length == 0) 7 | }; 8 | } 9 | }; 10 | }, 11 | toHave: function(util, customEqualityTesters) { 12 | return { 13 | compare: function(actual, expected) { 14 | return { 15 | pass: actual.length == expected 16 | }; 17 | } 18 | }; 19 | } 20 | }; 21 | -------------------------------------------------------------------------------- /spec/support/mocking.js: -------------------------------------------------------------------------------- 1 | var listeners = {}; 2 | 3 | chrome = { 4 | storage: { 5 | onChanged: { 6 | addListener: function() { 7 | } 8 | }, 9 | sync: { 10 | get: function() { 11 | } 12 | } 13 | }, 14 | runtime: { 15 | sendMessage: function(params, callback) { 16 | }, 17 | connect: function(connectInfo) { 18 | return { 19 | onMessage: { 20 | addListener: function(listener) { 21 | } 22 | } 23 | }; 24 | } 25 | }, 26 | extension: { 27 | getURL: function(path) { 28 | return path; 29 | } 30 | } 31 | }; 32 | -------------------------------------------------------------------------------- /js/processor/insert.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2013 Kengo Tateishi (@tkengo) 3 | * Licensed under MIT license. 4 | * http://www.opensource.org/licenses/mit-license.php 5 | * 6 | * Insert mode processor. 7 | */ 8 | var InsertModeProcessor = function() { 9 | }; 10 | 11 | /** 12 | * Callback method that invoke when leave the insert mode. 13 | */ 14 | InsertModeProcessor.prototype.notifyLeaveMode = function() { 15 | $(document.activeElement).blur(); 16 | 17 | chrome.runtime.sendMessage({ command: 'getContinuousState' }, function(status) { 18 | if (status) { 19 | new Executer('followLink --continuous').execute(); 20 | } 21 | }); 22 | }; 23 | -------------------------------------------------------------------------------- /bin/make.sh: -------------------------------------------------------------------------------- 1 | #/bin/sh 2 | 3 | BASE_DIR=$(cd $(dirname $0)/..;pwd) 4 | ZIP_FILE=$BASE_DIR/hometype.zip 5 | PUBLISH_DIR=$BASE_DIR/__publish 6 | IGNORE_FILES=" 7 | .git 8 | .gitignore 9 | bin/ 10 | README.md 11 | karma.conf.js 12 | package.json 13 | wercker.yml 14 | spec 15 | " 16 | 17 | if [ -e "$ZIP_FILE" ]; then 18 | rm -f $ZIP_FILE 19 | fi 20 | 21 | git clone git@github.com:tkengo/hometype.git $PUBLISH_DIR 22 | 23 | cd $PUBLISH_DIR && ./bin/build_dict 24 | 25 | for ignore_file in $IGNORE_FILES; do 26 | rm -fr $PUBLISH_DIR/$ignore_file 27 | done 28 | 29 | sed -i '' -e ':a' -e 'N' -e '$!ba' -e 's/\n/ /g' _locales/en/messages.json 30 | sed -i '' -e ':a' -e 'N' -e '$!ba' -e 's/\n/ /g' _locales/ja/messages.json 31 | 32 | cd $BASE_DIR && zip -r $ZIP_FILE ${PUBLISH_DIR##*/} 33 | rm -fr $PUBLISH_DIR 34 | -------------------------------------------------------------------------------- /js/background/favicon.js: -------------------------------------------------------------------------------- 1 | function convertFaviconToDataURL(url, callback) 2 | { 3 | var img = document.createElement('img'); 4 | img.addEventListener('load', function() { 5 | var canvas = document.createElement('canvas'); 6 | var context = canvas.getContext('2d'); 7 | 8 | canvas.width = img.width; 9 | canvas.height = img.height; 10 | context.drawImage(img, 0, 0); 11 | context.globalCompositeOperation = 'source-in'; 12 | 13 | callback(canvas.toDataURL()); 14 | }); 15 | 16 | img.src = 'chrome://favicon/' + url; 17 | } 18 | 19 | function convertFaviconsToDataURL(urls, callback, startIndex, result) 20 | { 21 | startIndex = startIndex || 0; 22 | result = result || []; 23 | 24 | if (urls.length > startIndex) { 25 | convertFaviconToDataURL(urls[startIndex], function(dataUrl) { 26 | result.push(dataUrl); 27 | convertFaviconsToDataURL(urls, callback, startIndex + 1, result); 28 | }); 29 | } else { 30 | callback(result); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /js/hint/algorithm/custom.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2013 Kengo Tateishi (@tkengo) 3 | * Licensed under MIT license. 4 | * http://www.opensource.org/licenses/mit-license.php 5 | * 6 | * Standard hint key algorithm. 7 | */ 8 | 9 | /** 10 | * Constructor 11 | * 12 | * @param array keys The string of hint keys. 13 | * @param boolean targetLength Length of hint for target. 14 | */ 15 | var CustomHintAlgorithm = function(targetLength) { 16 | var keys = convertLettersByOption(Opt.custom_hint_keys); 17 | 18 | var n = keys.length; 19 | var l = 1; 20 | while (n < targetLength) { 21 | ++l; 22 | n *= keys.length; 23 | } 24 | 25 | this.keys = keys; 26 | this.needKeyLength = l; 27 | this.keysIndex = 0; 28 | }; 29 | 30 | /** 31 | * Pop a next hint key. 32 | */ 33 | CustomHintAlgorithm.prototype.pop = function() { 34 | var hintKey = ''; 35 | 36 | var keySize = this.keys.length; 37 | for (var n = 0, len = this.needKeyLength; n < len; n++) { 38 | var an = Math.floor(this.keysIndex / Math.pow(keySize, n)) % keySize; 39 | hintKey += this.keys[an]; 40 | } 41 | this.keysIndex++; 42 | 43 | return hintKey; 44 | }; 45 | -------------------------------------------------------------------------------- /spec/js/key/map_spec.js: -------------------------------------------------------------------------------- 1 | describe('KeyMap', function() { 2 | var key = 'a'; 3 | var command = 'testCommand'; 4 | 5 | beforeEach(function() { 6 | KeyMap.clear(); 7 | }); 8 | 9 | it('should assign a key to a command in any modes', function() { 10 | var mode = ModeList.NORMAL_MODE; 11 | 12 | KeyMap.assign(mode, key, command); 13 | expect(_map[mode][key]).toBe(command); 14 | }); 15 | 16 | it('should assign a key to a command in the normal mode', function() { 17 | KeyMap.nmap(key, command); 18 | expect(_map[ModeList.NORMAL_MODE][key]).toBe(command); 19 | }); 20 | 21 | it('should assign a key to a command in the insert mode', function() { 22 | KeyMap.imap(key, command); 23 | expect(_map[ModeList.INSERT_MODE][key]).toBe(command); 24 | }); 25 | 26 | it('should assign a key to a command in the hint mode', function() { 27 | KeyMap.fmap(key, command); 28 | expect(_map[ModeList.HINT_MODE][key]).toBe(command); 29 | }); 30 | 31 | it('should assign a key to a command in the visual mode', function() { 32 | KeyMap.vmap(key, command); 33 | expect(_map[ModeList.VISUAL_MODE][key]).toBe(command); 34 | }); 35 | 36 | it('should assign a key to a command in the command mode', function() { 37 | KeyMap.cmap(key, command); 38 | expect(_map[ModeList.COMMAND_MODE][key]).toBe(command); 39 | }); 40 | 41 | it('should assign a key to a command in the help mode', function() { 42 | KeyMap.hmap(key, command); 43 | expect(_map[ModeList.HELP_MODE][key]).toBe(command); 44 | }); 45 | }); 46 | -------------------------------------------------------------------------------- /bin/build_dict: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'open-uri' 4 | require 'nkf' 5 | dict_url = 'https://raw.githubusercontent.com/skk-users-jp/dic-mirror/gh-pages/SKK-JISYO.ML' 6 | 7 | target_regexp = { 8 | alphabet: /^>?[a-z].*$/i, 9 | a: /^>?あ.*$/, 10 | i: /^>?い.*$/, 11 | u: /^>?う.*$/, 12 | e: /^>?え.*$/, 13 | o: /^>?お.*$/, 14 | k: /^>?[かきくけこ].*$/, 15 | s: /^>?[さしすせそ].*$/, 16 | t: /^>?[たちつてと].*$/, 17 | n: /^>?[なにぬねの].*$/, 18 | h: /^>?[はひふへほ].*$/, 19 | m: /^>?[まみむめも].*$/, 20 | y: /^>?[やゆよ].*$/, 21 | r: /^>?[らりるれろ].*$/, 22 | w: /^>?わ.*$/, 23 | g: /^>?[がぎぐげご].*$/, 24 | z: /^>?[ざじずぜぞ].*$/, 25 | d: /^>?[だぢづでど].*$/, 26 | b: /^>?[ばびぶべぼ].*$/, 27 | p: /^>?[ぱぴぷぺぽ].*$/, 28 | j: /^>?じ.*$/, 29 | c: /^>?[かしくせこち].*$/, 30 | q: /^>?く.*$/, 31 | f: /^>?ふ.*$/ 32 | } 33 | 34 | results = target_regexp.inject({}){|r,l|r[l[0]] = {}; r } 35 | 36 | open(dict_url) do |f| 37 | while line = f.gets 38 | line = NKF.nkf('-E -w', line) 39 | target_regexp.each do |letter, regexp| 40 | if line =~ regexp 41 | hiragana, kanji = line.gsub(/([^a-z]+)[a-z]/, '\1').gsub('>', '').split(' ') 42 | results[letter][hiragana] = [] unless results[letter][hiragana] 43 | results[letter][hiragana].concat(kanji.sub(/;.*/, '').split('/')) 44 | end 45 | end 46 | end 47 | 48 | results.each do |letter, dicts| 49 | open("dicts/#{letter}.ml", 'w') do |o| 50 | dicts.each do |hiragana, value| 51 | o.puts "#{hiragana}:#{value.uniq.join(' ').strip.gsub(/ +/, ' ')}" 52 | end 53 | end 54 | end 55 | end 56 | -------------------------------------------------------------------------------- /js/box/select.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2013 Kengo Tateishi (@tkengo) 3 | * Licensed under MIT license. 4 | * http://www.opensource.org/licenses/mit-license.php 5 | * 6 | * Select box. 7 | */ 8 | var HometypeSelectBox = function(select) { 9 | // Create select box elements. 10 | this.ul = $('