Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.lein-repl-history
target
.repl
out
.nrepl-port
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
# Clojurifier

A Chrome extension that replaces occurrences of the words "scala" and "haskell" to "Clojure" in web pages text nodes.

## how to build
```
lein chromebuild once
```
_It requires JDK 1.7+_
The unpacked extension will be in target/unpacked

###[how to install a Chrome extension](http://superuser.com/questions/247651/how-does-one-install-an-extension-for-chrome-browser-from-the-local-file-system/247654#247654)
23 changes: 23 additions & 0 deletions project.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
(defproject clojurifier "0.2.0-SNAPSHOT"
:license {:name "Eclipse Public License"
:url "http://www.eclipse.org/legal/epl-v10.html"}
:dependencies [[org.clojure/clojure "1.5.1"]
[org.clojure/clojurescript "0.0-2156"]
[org.clojure/core.async "0.1.242.0-44b1e3-alpha"]
[khroma "0.0.2"]
[prismatic/dommy "0.1.2"]]
:source-paths ["src"]
:profiles {:dev {:plugins [[com.cemerick/austin "0.1.3"]
[lein-cljsbuild "1.0.1"]
[lein-resource "0.3.6"]
[lein-chromebuild "0.1.0"]]
:cljsbuild {:builds {:main
{:source-paths ["src"]
:compiler {:output-to "target/unpacked/clojurifier.js"
:output-dir "target/js"
:optimizations :whitespace
:pretty-print true}}}}
:chromebuild {:resource-paths ["resources/js" "resources/html" "resources/images"]
:target-path "target/unpacked"}
}
})
Empty file added resources/html/popup.html
Empty file.
Binary file added resources/images/icon128.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added resources/images/icon16.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added resources/images/icon48.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions resources/js/background.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
clojurifier.background.init();

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does it do? I couldn't find a clojurifier.background namespace.

1 change: 1 addition & 0 deletions resources/js/content.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
clojurifier.content.init();
18 changes: 18 additions & 0 deletions resources/js/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"name": "Clojurifier",
"version": "0.2",
"icons": {
"16": "icon16.png",
"48": "icon48.png",
"128": "icon128.png"
},
"content_scripts": [
{
"matches": ["*://*/*"],
"js": ["clojurifier.js", "content.js"],
"run_at": "document_end"
}
],
"manifest_version": 2
}

33 changes: 33 additions & 0 deletions src/clojurifier/clojurifier.cljs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
(ns clojurifier.clojurifier
(:require [clojure.string :as s]
[khroma.log :as console]))


; the strings we want to replace
(def mapping {(js/RegExp. "haskell" "i") "Clojure"
(js/RegExp. "scala" "i") "Clojure"})


(defn clojurize
"Replaces all matched strings by 'mapping' in the element"
[element]
(doseq [[k v] mapping]
(set! (.-textContent element) (.replace (.-textContent element) k v))))


(defn- clojurable?
"Decides if we want to try finding matching strings in the element.
Script and textarea elements are out of our scope."
[node]
(and (= (.-nodeType node) 1) (not (#{"script" "textarea"} (.-nodeName node)))))


(defn clojurify
"Replaces all matched strings by 'mapping' in the element and its children if the element is a text element"
[element]
(let [nn (s/lower-case (.-nodeName element))]
(if (= nn "#text")
(clojurize element)
(when (clojurable? element)
(dotimes [i (.-length (.-childNodes element))]
(clojurify (.item (.-childNodes element) i) clojurize))))))
12 changes: 12 additions & 0 deletions src/clojurifier/content.cljs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
(ns clojurifier.content
(:require [clojurifier.mutant :refer [observe-body]]
[clojurifier.clojurifier :refer [clojurify]])
(:require-macros [dommy.macros :refer [sel1]]))

(defn init
"Clojurifies everything on the page and starts observing the body element for changes"
[]
(clojurify (sel1 :body))
(observe-body))


33 changes: 33 additions & 0 deletions src/clojurifier/mutant.cljs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
(ns clojurifier.mutant
(:require [khroma.log :as console]
[clojurifier.clojurifier :refer [clojurify]]))


; so we can use the circular dependency between observe-element and handle-mutation
(declare observe-element)

(defn handle-mutation
"Handles the change of an element. Clojurifies it and starts observing its children."
[mutation]
(clojurify (.-target mutation))
(doseq [child (-> mutation .-target .-childNodes (array-seq 0))]
(observe-element child)))

(defn handle-mutations
"Handles the incoming mutations one by one"
[mutations]
(doseq [m (js->clj mutations)]
(handle-mutation m)))

(defn observe-element
"Starts observing an element, calling handle-mutations on every attribute, child or text data change"
[elem]
(let [observer (js/MutationObserver. handle-mutations)]
(.observe observer elem
(clj->js {:attributes true :childList true :characterData true}))))

(defn observe-body
"Observes the body element for changes, indirectly watches all of it's children too"
[]
(observe-element js/document.body))