81 | )
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/README.markdown:
--------------------------------------------------------------------------------
1 | # DEPRECATED: Safari no longer supports extensions that modify the behaviour of the address bar. Consider using a separate tool, such as Alfred: https://www.alfredapp.com
2 |
3 | ---
4 |
5 | # KeySearch
6 |
7 | **_A keyword search extension for Safari._**
8 |
9 | **[Download](http://www.macosxtips.co.uk/keysearch)**
10 |
11 | ## Introduction
12 |
13 | **_KeySearch_ adds a new way of searching the Internet to Safari.** It uses short keywords to allow you to search any site you want, right from the toolbar.
14 |
15 | While browsing the web, just press ^S (or click the toolbar button) to bring up the KeySearch bar. Type a keyword, then your search query, and hit ⏎, and KeySearch will find the site you want to search and perform the search for you.
16 |
17 | No more going to Google first!
18 |
19 | ## Examples
20 |
21 | KeySearch comes with plenty of keywords that are ready for you to use.
22 |
23 | **Want to see videos of cute kittens?** `youtube cute kittens`
24 |
25 | **Forgotten who directed *Star Trek*?** `imdb Star Trek 2009`
26 |
27 | **Need directions for a trip?** `map New York to LA`
28 |
29 | ## Creating Keywords
30 |
31 | To create a new keyword, just do the following:
32 |
33 | - right-click on any search box in a web page
34 | - choose “Create keyword for this search”
35 | - type in a keyword
36 |
37 | …And let KeySearch take care of the rest.
38 |
39 | You can keep track of all your keywords by clicking “Edit Keywords” in the KeySearch bar. Here, you can manually create new keywords, add specific keyboard shortcuts for each keyword, and delete (or temporarily disable) keywords.
40 |
41 | ## Customisations
42 |
43 | There are a few different ways you can search using Keysearch:
44 |
45 | - Click the toolbar button to bring up the popover search
46 |
47 | - Use a keyboard shortcut to bring up the popover search (configurable in settings)
48 |
49 | - Use a keyboard shortcut to search using a semi-transparent “HUD” over the current web page
50 |
51 | - Search using the address bar (or “omnibar” in Safari 6). The keyboard shortcut ⌘L can be used to activate the address bar.
52 |
53 | ## Sites that don’t have search
54 |
55 | Some sites don’t have their own search engines, but you can still use KeySearch to search them.
56 |
57 | Instead of a keyword, just type `>example.com query` to search any site using Google Site Search. If you want to search the site that is currently open, just use `> query`.
58 |
59 | ## Faster Bookmarks
60 |
61 | Keywords aren’t just great for search. You can assign them to any web address you want, and use them as a quick way to access your favourite sites. Use `yt` to take you to YouTube, or `fb` to take you to Facebook.
62 |
--------------------------------------------------------------------------------
/src/reducers/rules.js:
--------------------------------------------------------------------------------
1 | import {createReducer} from '../utils'
2 | import * as types from '../constants/actionTypes'
3 |
4 | const initialState = {
5 | rulesList: {rules: null, error: null, loading: false},
6 | newRule: {error: null, loading: false},
7 | updateRule: {error: null, loading: false},
8 | deleteRule: {error: null, loading: false}
9 | }
10 |
11 | export default createReducer(initialState, {
12 | [types.RULES_REQUEST]: (state, payload) => {
13 | return {...state, rulesList: {rules: state.rulesList.rules, error: null, loading: true}}
14 | },
15 | [types.RULES_SUCCESS]: (state, payload) => {
16 | return {...state, rulesList: {rules: payload, error: null, loading: false}}
17 | },
18 | [types.RULES_FAILURE]: (state, payload) => {
19 | return {...state, rulesList: {rules: null, error: payload, loading: false}}
20 | },
21 | [types.CREATE_RULE_REQUEST]: (state, payload) => {
22 | return {...state, newRule: {error: null, loading: true}}
23 | },
24 | [types.CREATE_RULE_SUCCESS]: (state, payload) => {
25 | // Add the rule in the local state
26 | let rulesList = Object.assign({}, state.rulesList)
27 | rulesList.rules.unshift(payload)
28 | return {...state, rulesList, newRule: {error: null, loading: false}}
29 | },
30 | [types.CREATE_RULE_FAILURE]: (state, payload) => {
31 | return {...state, newRule: {error: payload, loading: false}}
32 | },
33 | [types.UPDATE_RULE_REQUEST]: (state, payload) => {
34 | return {...state, updateRule: {error: null, loading: true}}
35 | },
36 | [types.UPDATE_RULE_SUCCESS]: (state, payload) => {
37 | // Update the rule in the local state
38 | let rulesList = Object.assign({}, state.rulesList)
39 | for(var i = 0; i < rulesList.rules.length; i++) {
40 | if (rulesList.rules[i].id === payload.id) {
41 | rulesList.rules[i] = payload
42 | break
43 | }
44 | }
45 | return {...state, rulesList, updateRule: {error: null, loading: false}}
46 | },
47 | [types.UPDATE_RULE_FAILURE]: (state, payload) => {
48 | return {...state, updateRule: {error: payload, loading: false}}
49 | },
50 | [types.DELETE_RULE_REQUEST]: (state, payload) => {
51 | return {...state, deleteRule: {error: null, loading: true}}
52 | },
53 | [types.DELETE_RULE_SUCCESS]: (state, payload) => {
54 | // Remove the deleted rule from the local state
55 | let rulesList = Object.assign({}, state.rulesList)
56 | for(var i = 0; i < rulesList.rules.length; i++) {
57 | if (rulesList.rules[i].id === payload) {
58 | rulesList.rules.splice(i, 1)
59 | break
60 | }
61 | }
62 | return {...state, rulesList, deleteRule: {error: null, loading: false}}
63 | },
64 | [types.DELETE_RULE_FAILURE]: (state, payload) => {
65 | return {...state, deleteRule: {error: payload, loading: false}}
66 | }
67 | })
68 |
--------------------------------------------------------------------------------
/src/containers/RulesApp.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import {bindActionCreators} from 'redux'
3 | import {connect} from 'react-redux'
4 | import {VelocityTransitionGroup} from 'velocity-react'
5 | import * as actionCreators from '../actions'
6 | import {Button, Rule, TokenInput} from '../components'
7 | import block from 'bem-cn'
8 | import FileSaver from 'file-saver'
9 | import Dropzone from 'react-dropzone'
10 |
11 |
12 | class RulesApp extends React.Component {
13 |
14 | componentDidMount() {
15 | this.props.actions.loadRules()
16 | }
17 |
18 | handleAdd(e) {
19 | console.log('RulesApp handleAdd')
20 | this.props.actions.createRule({name: '', key: '', url: '', enabled: true})
21 | }
22 |
23 | handleImport(files, rejected) {
24 | console.log('RulesApp handleImport')
25 | if (files.length > 0) {
26 | console.log('Files: ', files);
27 | let reader = new FileReader()
28 | reader.onload = (function(createRule) {
29 | return function(e) {
30 | let rules = JSON.parse(e.target.result)
31 | console.log(rules)
32 | for (let rule of rules) {
33 | createRule(rule)
34 | }
35 | }
36 | })(this.props.actions.createRule)
37 | reader.readAsText(files[0]);
38 | }
39 | }
40 |
41 | handleExport(e) {
42 | console.log('RulesApp handleExport')
43 | console.log(this.props.rulesList.rules)
44 | // No ideal configuration here for now...
45 | // application/octet-stream => downloads, "Unknown" filename
46 | // application/json => displays with incorrect encoding, "Unknown.json" default name in save dialog
47 | // application/json;charset=utf-8 => fails completely
48 | // text/plain;charset=utf-8 => displays with correct encoding, "Unknown.css" default name in save dialog
49 | let blob = new Blob([JSON.stringify(this.props.rulesList.rules, null, 2)], {type: 'text/plain;charset=utf-8'})
50 | FileSaver.saveAs(blob, 'keysearch.json')
51 | }
52 |
53 | render() {
54 | let {rulesList, actions} = this.props
55 | let rows = !rulesList.rules ? [] : rulesList.rules.map((rule, i) => )
61 | let b = block('rules-app')
62 | return (
63 |