diff --git a/contacts/.gitignore b/contacts/.gitignore
index e04714b..4188ff6 100644
--- a/contacts/.gitignore
+++ b/contacts/.gitignore
@@ -7,3 +7,4 @@ pom.xml.asc
*.class
/.lein-*
/.nrepl-port
+.repl*
diff --git a/contacts/contacts.iml b/contacts/contacts.iml
index 1b6b335..b678c63 100644
--- a/contacts/contacts.iml
+++ b/contacts/contacts.iml
@@ -5,41 +5,61 @@
+
+
+
+
+
+
+
-
+
+
+
-
-
-
-
-
+
+
+
+
+
-
+
-
+
+
+
+
+
+
+
+
+
+
+
+
-
+
+
-
-
-
-
+
+
+
+
+
-
-
-
+
@@ -48,19 +68,26 @@
-
+
-
-
-
-
-
-
-
+
+
+
+
+
+
-
-
-
+
+
+
+
+
+
+
+
+
+
+
@@ -69,7 +96,6 @@
-
@@ -81,6 +107,6 @@
+
-
-
+
\ No newline at end of file
diff --git a/contacts/project.clj b/contacts/project.clj
index 284de73..761a896 100644
--- a/contacts/project.clj
+++ b/contacts/project.clj
@@ -6,20 +6,24 @@
:jvm-opts ^:replace ["-Xms512m" "-Xmx512m" "-server"]
- :dependencies [[org.clojure/clojure "1.5.1"]
- [org.clojure/clojurescript "0.0-2173"]
- [com.datomic/datomic-free "0.9.4699"]
+ :dependencies [[org.clojure/clojure "1.7.0"]
+ [org.clojure/clojurescript "0.0-3308"]
+ [com.datomic/datomic-free "0.9.5153"]
[bidi "1.10.2"]
- [om "0.5.3"]
- [secretary "1.1.0"]
+ [org.omcljs/om "0.9.0"]
[ring/ring "1.2.2"]
- [fogus/ring-edn "0.2.0"]
+ [com.cognitect/transit-clj "0.8.271"]
+ [com.cognitect/transit-cljs "0.8.220"]
+ [cljs-http "0.1.30" :exclusions
+ [org.clojure/clojure org.clojure/clojurescript
+ com.cognitect/transit-cljs]]
+ [cljsjs/codemirror "5.1.0-2"]
[com.stuartsierra/component "0.2.1"]
- [org.clojure/core.async "0.1.278.0-76b25b-alpha"]]
+ [org.clojure/core.async "0.1.346.0-17112a-alpha"]]
:source-paths ["src/clj"]
- :plugins [[lein-cljsbuild "1.0.2"]
+ :plugins [[lein-cljsbuild "1.0.5"]
[lein-ring "0.8.10"]
[lein-beanstalk "0.2.7"]]
diff --git a/contacts/resources/data/initial.edn b/contacts/resources/data/initial.edn
index 139597f..7c2303e 100644
--- a/contacts/resources/data/initial.edn
+++ b/contacts/resources/data/initial.edn
@@ -1,2 +1,18 @@
-
-
+[{:db/id #db/id[:db.part/user]
+ :person/first-name "Bob"
+ :person/last-name "Smith"
+ :person/email [{:email/address "bob.smith@foo.com"}]
+ :person/telephone [{:telephone/number "111-111-1111"}]
+ :person/address [{:address/street "Maple Street"
+ :address/city "Boston"
+ :address/state "Massachusetts"
+ :address/zipcode "11111"}]}
+ {:db/id #db/id[:db.part/user]
+ :person/first-name "Martha"
+ :person/last-name "Smith"
+ :person/email [{:email/address "martha.smith@bar.com"}]
+ :person/telephone [{:telephone/number "111-111-1112"}]
+ :person/address [{:address/street "Maple Street"
+ :address/city "Boston"
+ :address/state "Massachusetts"
+ :address/zipcode "11111"}]}]
\ No newline at end of file
diff --git a/contacts/resources/public/codemirror/closebrackets.js b/contacts/resources/public/codemirror/closebrackets.js
new file mode 100644
index 0000000..7030268
--- /dev/null
+++ b/contacts/resources/public/codemirror/closebrackets.js
@@ -0,0 +1,185 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
+(function(mod) {
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
+ mod(require("../../lib/codemirror"));
+ else if (typeof define == "function" && define.amd) // AMD
+ define(["../../lib/codemirror"], mod);
+ else // Plain browser env
+ mod(CodeMirror);
+})(function(CodeMirror) {
+ var defaults = {
+ pairs: "()[]{}''\"\"",
+ triples: "",
+ explode: "[]{}"
+ };
+
+ var Pos = CodeMirror.Pos;
+
+ CodeMirror.defineOption("autoCloseBrackets", false, function(cm, val, old) {
+ if (old && old != CodeMirror.Init) {
+ cm.removeKeyMap(keyMap);
+ cm.state.closeBrackets = null;
+ }
+ if (val) {
+ cm.state.closeBrackets = val;
+ cm.addKeyMap(keyMap);
+ }
+ });
+
+ function getOption(conf, name) {
+ if (name == "pairs" && typeof conf == "string") return conf;
+ if (typeof conf == "object" && conf[name] != null) return conf[name];
+ return defaults[name];
+ }
+
+ var bind = defaults.pairs + "`";
+ var keyMap = {Backspace: handleBackspace, Enter: handleEnter};
+ for (var i = 0; i < bind.length; i++)
+ keyMap["'" + bind.charAt(i) + "'"] = handler(bind.charAt(i));
+
+ function handler(ch) {
+ return function(cm) { return handleChar(cm, ch); };
+ }
+
+ function getConfig(cm) {
+ var deflt = cm.state.closeBrackets;
+ if (!deflt) return null;
+ var mode = cm.getModeAt(cm.getCursor());
+ return mode.closeBrackets || deflt;
+ }
+
+ function handleBackspace(cm) {
+ var conf = getConfig(cm);
+ if (!conf || cm.getOption("disableInput")) return CodeMirror.Pass;
+
+ var pairs = getOption(conf, "pairs");
+ var ranges = cm.listSelections();
+ for (var i = 0; i < ranges.length; i++) {
+ if (!ranges[i].empty()) return CodeMirror.Pass;
+ var around = charsAround(cm, ranges[i].head);
+ if (!around || pairs.indexOf(around) % 2 != 0) return CodeMirror.Pass;
+ }
+ for (var i = ranges.length - 1; i >= 0; i--) {
+ var cur = ranges[i].head;
+ cm.replaceRange("", Pos(cur.line, cur.ch - 1), Pos(cur.line, cur.ch + 1));
+ }
+ }
+
+ function handleEnter(cm) {
+ var conf = getConfig(cm);
+ var explode = conf && getOption(conf, "explode");
+ if (!explode || cm.getOption("disableInput")) return CodeMirror.Pass;
+
+ var ranges = cm.listSelections();
+ for (var i = 0; i < ranges.length; i++) {
+ if (!ranges[i].empty()) return CodeMirror.Pass;
+ var around = charsAround(cm, ranges[i].head);
+ if (!around || explode.indexOf(around) % 2 != 0) return CodeMirror.Pass;
+ }
+ cm.operation(function() {
+ cm.replaceSelection("\n\n", null);
+ cm.execCommand("goCharLeft");
+ ranges = cm.listSelections();
+ for (var i = 0; i < ranges.length; i++) {
+ var line = ranges[i].head.line;
+ cm.indentLine(line, null, true);
+ cm.indentLine(line + 1, null, true);
+ }
+ });
+ }
+
+ function handleChar(cm, ch) {
+ var conf = getConfig(cm);
+ if (!conf || cm.getOption("disableInput")) return CodeMirror.Pass;
+
+ var pairs = getOption(conf, "pairs");
+ var pos = pairs.indexOf(ch);
+ if (pos == -1) return CodeMirror.Pass;
+ var triples = getOption(conf, "triples");
+
+ var identical = pairs.charAt(pos + 1) == ch;
+ var ranges = cm.listSelections();
+ var opening = pos % 2 == 0;
+
+ var type, next;
+ for (var i = 0; i < ranges.length; i++) {
+ var range = ranges[i], cur = range.head, curType;
+ var next = cm.getRange(cur, Pos(cur.line, cur.ch + 1));
+ if (opening && !range.empty()) {
+ curType = "surround";
+ } else if ((identical || !opening) && next == ch) {
+ if (triples.indexOf(ch) >= 0 && cm.getRange(cur, Pos(cur.line, cur.ch + 3)) == ch + ch + ch)
+ curType = "skipThree";
+ else
+ curType = "skip";
+ } else if (identical && cur.ch > 1 && triples.indexOf(ch) >= 0 &&
+ cm.getRange(Pos(cur.line, cur.ch - 2), cur) == ch + ch &&
+ (cur.ch <= 2 || cm.getRange(Pos(cur.line, cur.ch - 3), Pos(cur.line, cur.ch - 2)) != ch)) {
+ curType = "addFour";
+ } else if (identical) {
+ if (!CodeMirror.isWordChar(next) && enteringString(cm, cur, ch)) curType = "both";
+ else return CodeMirror.Pass;
+ } else if (opening && (cm.getLine(cur.line).length == cur.ch ||
+ isClosingBracket(next, pairs) ||
+ /\s/.test(next))) {
+ curType = "both";
+ } else {
+ return CodeMirror.Pass;
+ }
+ if (!type) type = curType;
+ else if (type != curType) return CodeMirror.Pass;
+ }
+
+ var left = pos % 2 ? pairs.charAt(pos - 1) : ch;
+ var right = pos % 2 ? ch : pairs.charAt(pos + 1);
+ cm.operation(function() {
+ if (type == "skip") {
+ cm.execCommand("goCharRight");
+ } else if (type == "skipThree") {
+ for (var i = 0; i < 3; i++)
+ cm.execCommand("goCharRight");
+ } else if (type == "surround") {
+ var sels = cm.getSelections();
+ for (var i = 0; i < sels.length; i++)
+ sels[i] = left + sels[i] + right;
+ cm.replaceSelections(sels, "around");
+ } else if (type == "both") {
+ cm.replaceSelection(left + right, null);
+ cm.triggerElectric(left + right);
+ cm.execCommand("goCharLeft");
+ } else if (type == "addFour") {
+ cm.replaceSelection(left + left + left + left, "before");
+ cm.execCommand("goCharRight");
+ }
+ });
+ }
+
+ function isClosingBracket(ch, pairs) {
+ var pos = pairs.lastIndexOf(ch);
+ return pos > -1 && pos % 2 == 1;
+ }
+
+ function charsAround(cm, pos) {
+ var str = cm.getRange(Pos(pos.line, pos.ch - 1),
+ Pos(pos.line, pos.ch + 1));
+ return str.length == 2 ? str : null;
+ }
+
+ // Project the token type that will exists after the given char is
+ // typed, and use it to determine whether it would cause the start
+ // of a string token.
+ function enteringString(cm, pos, ch) {
+ var line = cm.getLine(pos.line);
+ var token = cm.getTokenAt(pos);
+ if (/\bstring2?\b/.test(token.type)) return false;
+ var stream = new CodeMirror.StringStream(line.slice(0, pos.ch) + ch + line.slice(pos.ch), 4);
+ stream.pos = stream.start = token.start;
+ for (;;) {
+ var type1 = cm.getMode().token(stream, token.state);
+ if (stream.pos >= pos.ch + 1) return /\bstring2?\b/.test(type1);
+ stream.start = stream.pos;
+ }
+ }
+});
diff --git a/contacts/resources/public/codemirror/matchbrackets.js b/contacts/resources/public/codemirror/matchbrackets.js
new file mode 100644
index 0000000..70e1ae1
--- /dev/null
+++ b/contacts/resources/public/codemirror/matchbrackets.js
@@ -0,0 +1,120 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: http://codemirror.net/LICENSE
+
+(function(mod) {
+ if (typeof exports == "object" && typeof module == "object") // CommonJS
+ mod(require("../../lib/codemirror"));
+ else if (typeof define == "function" && define.amd) // AMD
+ define(["../../lib/codemirror"], mod);
+ else // Plain browser env
+ mod(CodeMirror);
+})(function(CodeMirror) {
+ var ie_lt8 = /MSIE \d/.test(navigator.userAgent) &&
+ (document.documentMode == null || document.documentMode < 8);
+
+ var Pos = CodeMirror.Pos;
+
+ var matching = {"(": ")>", ")": "(<", "[": "]>", "]": "[<", "{": "}>", "}": "{<"};
+
+ function findMatchingBracket(cm, where, strict, config) {
+ var line = cm.getLineHandle(where.line), pos = where.ch - 1;
+ var match = (pos >= 0 && matching[line.text.charAt(pos)]) || matching[line.text.charAt(++pos)];
+ if (!match) return null;
+ var dir = match.charAt(1) == ">" ? 1 : -1;
+ if (strict && (dir > 0) != (pos == where.ch)) return null;
+ var style = cm.getTokenTypeAt(Pos(where.line, pos + 1));
+
+ var found = scanForBracket(cm, Pos(where.line, pos + (dir > 0 ? 1 : 0)), dir, style || null, config);
+ if (found == null) return null;
+ return {from: Pos(where.line, pos), to: found && found.pos,
+ match: found && found.ch == match.charAt(0), forward: dir > 0};
+ }
+
+ // bracketRegex is used to specify which type of bracket to scan
+ // should be a regexp, e.g. /[[\]]/
+ //
+ // Note: If "where" is on an open bracket, then this bracket is ignored.
+ //
+ // Returns false when no bracket was found, null when it reached
+ // maxScanLines and gave up
+ function scanForBracket(cm, where, dir, style, config) {
+ var maxScanLen = (config && config.maxScanLineLength) || 10000;
+ var maxScanLines = (config && config.maxScanLines) || 1000;
+
+ var stack = [];
+ var re = config && config.bracketRegex ? config.bracketRegex : /[(){}[\]]/;
+ var lineEnd = dir > 0 ? Math.min(where.line + maxScanLines, cm.lastLine() + 1)
+ : Math.max(cm.firstLine() - 1, where.line - maxScanLines);
+ for (var lineNo = where.line; lineNo != lineEnd; lineNo += dir) {
+ var line = cm.getLine(lineNo);
+ if (!line) continue;
+ var pos = dir > 0 ? 0 : line.length - 1, end = dir > 0 ? line.length : -1;
+ if (line.length > maxScanLen) continue;
+ if (lineNo == where.line) pos = where.ch - (dir < 0 ? 1 : 0);
+ for (; pos != end; pos += dir) {
+ var ch = line.charAt(pos);
+ if (re.test(ch) && (style === undefined || cm.getTokenTypeAt(Pos(lineNo, pos + 1)) == style)) {
+ var match = matching[ch];
+ if ((match.charAt(1) == ">") == (dir > 0)) stack.push(ch);
+ else if (!stack.length) return {pos: Pos(lineNo, pos), ch: ch};
+ else stack.pop();
+ }
+ }
+ }
+ return lineNo - dir == (dir > 0 ? cm.lastLine() : cm.firstLine()) ? false : null;
+ }
+
+ function matchBrackets(cm, autoclear, config) {
+ // Disable brace matching in long lines, since it'll cause hugely slow updates
+ var maxHighlightLen = cm.state.matchBrackets.maxHighlightLineLength || 1000;
+ var marks = [], ranges = cm.listSelections();
+ for (var i = 0; i < ranges.length; i++) {
+ var match = ranges[i].empty() && findMatchingBracket(cm, ranges[i].head, false, config);
+ if (match && cm.getLine(match.from.line).length <= maxHighlightLen) {
+ var style = match.match ? "CodeMirror-matchingbracket" : "CodeMirror-nonmatchingbracket";
+ marks.push(cm.markText(match.from, Pos(match.from.line, match.from.ch + 1), {className: style}));
+ if (match.to && cm.getLine(match.to.line).length <= maxHighlightLen)
+ marks.push(cm.markText(match.to, Pos(match.to.line, match.to.ch + 1), {className: style}));
+ }
+ }
+
+ if (marks.length) {
+ // Kludge to work around the IE bug from issue #1193, where text
+ // input stops going to the textare whever this fires.
+ if (ie_lt8 && cm.state.focused) cm.focus();
+
+ var clear = function() {
+ cm.operation(function() {
+ for (var i = 0; i < marks.length; i++) marks[i].clear();
+ });
+ };
+ if (autoclear) setTimeout(clear, 800);
+ else return clear;
+ }
+ }
+
+ var currentlyHighlighted = null;
+ function doMatchBrackets(cm) {
+ cm.operation(function() {
+ if (currentlyHighlighted) {currentlyHighlighted(); currentlyHighlighted = null;}
+ currentlyHighlighted = matchBrackets(cm, false, cm.state.matchBrackets);
+ });
+ }
+
+ CodeMirror.defineOption("matchBrackets", false, function(cm, val, old) {
+ if (old && old != CodeMirror.Init)
+ cm.off("cursorActivity", doMatchBrackets);
+ if (val) {
+ cm.state.matchBrackets = typeof val == "object" ? val : {};
+ cm.on("cursorActivity", doMatchBrackets);
+ }
+ });
+
+ CodeMirror.defineExtension("matchBrackets", function() {matchBrackets(this, true);});
+ CodeMirror.defineExtension("findMatchingBracket", function(pos, strict, config){
+ return findMatchingBracket(this, pos, strict, config);
+ });
+ CodeMirror.defineExtension("scanForBracket", function(pos, dir, style, config){
+ return scanForBracket(this, pos, dir, style, config);
+ });
+});
diff --git a/contacts/resources/public/css/main.css b/contacts/resources/public/css/main.css
index c0fab42..fdb550e 100644
--- a/contacts/resources/public/css/main.css
+++ b/contacts/resources/public/css/main.css
@@ -1,63 +1,70 @@
body {
- margin: 0px;
- padding: 0px;
- width: 100%;
+ font-size: 24px;
}
-ul {
- margin: 0;
- padding: 0 0 0 10px;
+#demo1, #demo2 {
+ border: 1px solid #ccc;
+ display: -ms-flex;
+ display: -webkit-flex;
+ display: flex;
+ height: 600px;
}
-li {
- list-style: none;
- margin: 0;
- padding: 10px;
+#demo2 {
+ padding: 2em;
+ font-family: helvetica, arial, sans-serif;
}
-label {
- font-size: 11px;
- font-weight: bold;
- text-transform: uppercase;
+#demo2 h3 {
+ margin: 0;
}
-.button {
- font-family: sans-serif;
- padding: 5px 10px;
- border: 1px solid #666;
- border-radius: 4px;
- background-color: white;
- text-transform: uppercase;
+#demo2 li {
+ padding: 0 0 0.5em 0;
}
-#contacts {
- font-family: sans-serif;
- position: absolute;
- width: 400px;
- left: 50%;
- margin-left: -200px;
- margin-top: 10px;
+#demo2 li label {
+ font-weight: bold;
+ padding-right: 1em;
}
-#contacts-list, #contact-info {
- border: 1px solid #ccc;
- min-height: 50px;
- padding: 10px;
- margin-top: 10px;
+textarea {
+ width: 50%;
}
-#contacts-list li {
- cursor: pointer;
+.CodeMirror {
+ height: 510px;
+ border-bottom: 1px solid #ccc;
}
-.section {
- margin-top: 20px;
+#demo1 > div {
+ width: 50%;
}
-.list {
- font-size: 13px;
+button {
+ font-size: 24px;
+ border: 2px solid black;
+ background-color: white;
+ border-radius: 4px;
+ margin-top: 1em;
+ margin-left: 1em;
}
-.editable button {
- margin-left: 10px;
+#input {
}
+
+#output {
+ background-color: #efefef;
+ border-left: 1px solid #ccc;
+ height: 100%;
+}
+
+pre {
+ white-space: pre-wrap; /* css-3 */
+ white-space: -moz-pre-wrap; /* Mozilla, since 1999 */
+ white-space: -pre-wrap; /* Opera 4-6 */
+ white-space: -o-pre-wrap; /* Opera 7 */
+ word-wrap: break-word; /* Internet Explorer 5.5+ */
+ padding: 0;
+ margin: 0;
+}
\ No newline at end of file
diff --git a/contacts/resources/public/html/demo1.html b/contacts/resources/public/html/demo1.html
new file mode 100644
index 0000000..e3cc183
--- /dev/null
+++ b/contacts/resources/public/html/demo1.html
@@ -0,0 +1,20 @@
+
+
+
+
+ Demo 1
+
+
+
+
+
+