Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Comparing changes

Choose two branches to see what's changed or to start a new pull request. If you need to, you can also compare across forks.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also compare across forks.
base fork: pere-martir/tarjetasflash
base: 693562ae4c
...
head fork: pere-martir/tarjetasflash
compare: c2731e893c
Checking mergeability… Don't worry, you can still create the pull request.
  • 2 commits
  • 70 files changed
  • 0 commit comments
  • 1 contributor
Showing with 4,055 additions and 13 deletions.
  1. +1 −1  { → chrome-extension}/background.html
  2. 0  { → chrome-extension}/jquery-1.6.1.js
  3. 0  { → chrome-extension}/jquery.couch.js
  4. 0  { → chrome-extension}/manifest.json
  5. +50 −0 chrome-extension/tarjetasflash.js
  6. +0 −12 tarjetasflash.js
  7. +7 −0 website/.couchappignore
  8. +1 −0  website/.couchapprc
  9. +40 −0 website/README.md
  10. +29 −0 website/_attachments/index.html
  11. +75 −0 website/_attachments/style/main.css
  12. +1 −0  website/_id
  13. +4 −0 website/couchapp.json
  14. +11 −0 website/evently/items/_changes/data.js
  15. +18 −0 website/evently/items/_changes/mustache.html
  16. +5 −0 website/evently/items/_changes/query.json
  17. +14 −0 website/evently/profile/profileReady/mustache.html
  18. +12 −0 website/evently/profile/profileReady/selectors/form/submit.js
  19. +1 −0  website/language
  20. +3 −0  website/shows/hello.js
  21. +235 −0 website/vendor/couchapp/_attachments/jquery.couch.app.js
  22. +90 −0 website/vendor/couchapp/_attachments/jquery.couch.app.util.js
  23. +399 −0 website/vendor/couchapp/_attachments/jquery.evently.js
  24. +346 −0 website/vendor/couchapp/_attachments/jquery.mustache.js
  25. +174 −0 website/vendor/couchapp/_attachments/jquery.pathbinder.js
  26. +17 −0 website/vendor/couchapp/_attachments/loader.js
  27. +22 −0 website/vendor/couchapp/evently/README.md
  28. +16 −0 website/vendor/couchapp/evently/account/_init.js
  29. +1 −0  website/vendor/couchapp/evently/account/adminParty/mustache.html
  30. +10 −0 website/vendor/couchapp/evently/account/doLogin.js
  31. +8 −0 website/vendor/couchapp/evently/account/doLogout.js
  32. +10 −0 website/vendor/couchapp/evently/account/doSignup.js
  33. +4 −0 website/vendor/couchapp/evently/account/loggedIn/after.js
  34. +7 −0 website/vendor/couchapp/evently/account/loggedIn/data.js
  35. +4 −0 website/vendor/couchapp/evently/account/loggedIn/mustache.html
  36. +3 −0  website/vendor/couchapp/evently/account/loggedIn/selectors.json
  37. +1 −0  website/vendor/couchapp/evently/account/loggedOut/mustache.html
  38. +4 −0 website/vendor/couchapp/evently/account/loggedOut/selectors.json
  39. +3 −0  website/vendor/couchapp/evently/account/loginForm/after.js
  40. +6 −0 website/vendor/couchapp/evently/account/loginForm/mustache.html
  41. +1 −0  website/vendor/couchapp/evently/account/loginForm/selectors/a[href=#signup].json
  42. +6 −0 website/vendor/couchapp/evently/account/loginForm/selectors/form/submit.js
  43. +3 −0  website/vendor/couchapp/evently/account/signupForm/after.js
  44. +6 −0 website/vendor/couchapp/evently/account/signupForm/mustache.html
  45. +1 −0  website/vendor/couchapp/evently/account/signupForm/selectors/a[href=#login].json
  46. +6 −0 website/vendor/couchapp/evently/account/signupForm/selectors/form/submit.js
  47. +21 −0 website/vendor/couchapp/evently/profile/loggedIn.js
  48. +3 −0  website/vendor/couchapp/evently/profile/loggedOut/after.js
  49. +1 −0  website/vendor/couchapp/evently/profile/loggedOut/mustache.html
  50. +3 −0  website/vendor/couchapp/evently/profile/noProfile/data.js
  51. +11 −0 website/vendor/couchapp/evently/profile/noProfile/mustache.html
  52. +36 −0 website/vendor/couchapp/evently/profile/noProfile/selectors/form/submit.js
  53. +3 −0  website/vendor/couchapp/evently/profile/profileReady/after.js
  54. +3 −0  website/vendor/couchapp/evently/profile/profileReady/data.js
  55. +8 −0 website/vendor/couchapp/evently/profile/profileReady/mustache.html
  56. +39 −0 website/vendor/couchapp/lib/atom.js
  57. +25 −0 website/vendor/couchapp/lib/cache.js
  58. +20 −0 website/vendor/couchapp/lib/code.js
  59. +121 −0 website/vendor/couchapp/lib/docform.js
  60. +18 −0 website/vendor/couchapp/lib/linkup.js
  61. +13 −0 website/vendor/couchapp/lib/list.js
  62. +1,300 −0 website/vendor/couchapp/lib/markdown.js
  63. +261 −0 website/vendor/couchapp/lib/md5.js
  64. +339 −0 website/vendor/couchapp/lib/mustache.js
  65. +83 −0 website/vendor/couchapp/lib/path.js
  66. +8 −0 website/vendor/couchapp/lib/redirect.js
  67. +21 −0 website/vendor/couchapp/lib/utils.js
  68. +53 −0 website/vendor/couchapp/lib/validate.js
  69. +5 −0 website/vendor/couchapp/metadata.json
  70. +5 −0 website/views/recent-items/map.js
View
2  background.html → chrome-extension/background.html
@@ -30,7 +30,7 @@
function onRequest(request, sender, callback)
{
- console.log("url: " + request.url + " word: " + request.word);
+ console.log("onRequest, word=" + request.word);
// Since 'urlPrefix' is default to the domain of the script,
// it must be overriden before opening the db.
View
0  jquery-1.6.1.js → chrome-extension/jquery-1.6.1.js
File renamed without changes
View
0  jquery.couch.js → chrome-extension/jquery.couch.js
File renamed without changes
View
0  manifest.json → chrome-extension/manifest.json
File renamed without changes
View
50 chrome-extension/tarjetasflash.js
@@ -0,0 +1,50 @@
+
+console.log("Hello! This is tarjetaflash.");
+
+Array.prototype.unique = function() {
+ var a = [];
+ var l = this.length;
+ for(var i=0; i<l; i++) {
+ for(var j=i+1; j<l; j++) {
+ // If this[i] is found later in the array
+ if (this[i] === this[j])
+ j = ++i;
+ }
+ a.push(this[i]);
+ }
+ return a;
+};
+
+/*
+ <span class="clickable" onclick="redirectWR(event,&quot;OXiten&quot;)">
+ <span class="forma"> strada</span> /<span class="fonetica"> 'strada</span>
+*/
+var words = $(".hw,.forma").map(function(index, element) {
+ // Trim leading and tailing non-alphabet characters
+ // For example: "1. colle" and "2. colle"
+ return $(element).text().replace(/^[\s0-9.]+|[\s0-9.]+$/g, '');
+ }).get().unique();
+
+$.each(words, function remember_word(_, word) {
+ var lang = '';
+ var splits = document.location.pathname.split('/');
+
+ // Spanish
+ if ('es' == splits[1]) {
+ if ('en' == splits[2]) lang = 'es'; else return;
+ }
+
+ // Italian
+ if ('iten' == splits[1]) lang = 'it';
+ else if ('enit' == splits[1]) return;
+
+ word = jQuery.trim(word);
+ console.log("TarjetaFlash word: '" + word + "' language: '" + lang + "'");
+ chrome.extension.sendRequest(
+ {'lang': lang, 'word': word, 'timestamp': Date.now() },
+ function(response) {});
+ }
+);
+
+
+
View
12 tarjetasflash.js
@@ -1,12 +0,0 @@
-
-console.log("hello! This is tarjetaflash.");
-
-$(".hw").each(function() {
- var lang = document.location.pathname.split('/')[1];
- var word = $(this).text();
- console.log("WordReference Entry: " + word);
- chrome.extension.sendRequest(
- {'url': document.URL, 'lang': lang, 'word': word, 'timestamp': Date.now() },
- function(response) {});
- });
-
View
7 website/.couchappignore
@@ -0,0 +1,7 @@
+[
+ // filenames matching these regexps will not be pushed to the database
+ // uncomment to activate; separate entries with ","
+ // ".*~$"
+ // ".*\\.swp$"
+ // ".*\\.bak$"
+]
View
1  website/.couchapprc
@@ -0,0 +1 @@
+{}
View
40 website/README.md
@@ -0,0 +1,40 @@
+## Generated by CouchApp
+
+CouchApps are web applications which can be served directly from [CouchDB](http://couchdb.apache.org). This gives them the nice property of replicating just like any other data stored in CouchDB. They are also simple to write as they can use the built-in jQuery libraries and plugins that ship with CouchDB.
+
+[More info about CouchApps here.](http://couchapp.org)
+
+## Deploying this app
+
+Assuming you just cloned this app from git, and you have changed into the app directory in your terminal, you want to push it to your CouchDB with the CouchApp command line tool, like this:
+
+ couchapp push . http://name:password@hostname:5984/mydatabase
+
+If you don't have a password on your CouchDB (admin party) you can do it like this (but it's a bad, idea, set a password):
+
+ couchapp push . http://hostname:5984/mydatabase
+
+If you get sick of typing the URL, you should setup a `.couchapprc` file in the root of your directory. Remember not to check this into version control as it will have passwords in it.
+
+The `.couchapprc` file should have contents like this:
+
+ {
+ "env" : {
+ "public" : {
+ "db" : "http://name:pass@mycouch.couchone.com/mydatabase"
+ },
+ "default" : {
+ "db" : "http://name:pass@localhost:5984/mydatabase"
+ }
+ }
+ }
+
+Now that you have the `.couchapprc` file set up, you can push your app to the CouchDB as simply as:
+
+ couchapp push
+
+This pushes to the `default` as specified. To push to the `public` you'd run:
+
+ couchapp push public
+
+Of course you can continue to add more deployment targets as you see fit, and give them whatever names you like.
View
29 website/_attachments/index.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>My New CouchApp</title>
+ <link rel="stylesheet" href="style/main.css" type="text/css">
+ </head>
+ <body>
+ <div id="account"></div>
+
+ <h1>Generated CouchApp</h1>
+
+ <div id="profile"></div>
+ <div id="items"></div>
+
+ <div id="sidebar">
+ <p>Edit welcome message.</p>
+ <p>Ideas: You could easily turn this into a photo sharing app, or a grocery list, or a chat room.</p>
+ </div>
+ </body>
+ <script src="vendor/couchapp/loader.js"></script>
+ <script type="text/javascript" charset="utf-8">
+ $.couch.app(function(app) {
+ $("#account").evently("account", app);
+ $("#profile").evently("profile", app);
+ $.evently.connect("#account","#profile", ["loggedIn","loggedOut"]);
+ $("#items").evently("items", app);
+ });
+ </script>
+</html>
View
75 website/_attachments/style/main.css
@@ -0,0 +1,75 @@
+/* add styles here */
+
+body {
+ font:1em Helvetica, sans-serif;
+ padding:4px;
+}
+
+h1 {
+ margin-top:0;
+}
+
+#account {
+ float:right;
+}
+
+#profile {
+ border:4px solid #edd;
+ background:#fee;
+ padding:8px;
+ margin-bottom:8px;
+}
+
+#items {
+ border:4px solid #dde;
+ background:#eef;
+ padding:8px;
+ width:60%;
+ float:left;
+}
+
+#sidebar {
+ border:4px solid #dfd;
+ padding:8px;
+ float:right;
+ width:30%;
+}
+
+#items li {
+ border:4px solid #f5f5ff;
+ background:#fff;
+ padding:8px;
+ margin:4px 0;
+}
+
+form {
+ padding:4px;
+ margin:6px;
+ background-color:#ddd;
+}
+
+div.avatar {
+ padding:2px;
+ padding-bottom:0;
+ margin-right:4px;
+ float:left;
+ font-size:0.78em;
+ width : 60px;
+ height : 60px;
+ text-align: center;
+}
+
+div.avatar .name {
+ padding-top:2px;
+}
+
+div.avatar img {
+ margin:0 auto;
+ padding:0;
+ width : 40px;
+ height : 40px;
+}
+
+#items ul {
+ list-style: none;
+}
View
1  website/_id
@@ -0,0 +1 @@
+_design/helloworld
View
4 website/couchapp.json
@@ -0,0 +1,4 @@
+{
+ "name": "Name of your CouchApp",
+ "description": "CouchApp"
+}
View
11 website/evently/items/_changes/data.js
@@ -0,0 +1,11 @@
+function(data) {
+ // $.log(data)
+ var p;
+ return {
+ items : data.rows.map(function(r) {
+ p = (r.value && r.value.profile) || {};
+ p.message = r.value && r.value.message;
+ return p;
+ })
+ }
+};
View
18 website/evently/items/_changes/mustache.html
@@ -0,0 +1,18 @@
+<p>Customize this format here: <tt>ddoc.evently.items._changes.mustache</tt></p>
+<h3>Recent Messages</h3>
+<ul>
+ {{#items}}
+ <li>
+ <div class="avatar">
+ {{#gravatar_url}}<img src="{{gravatar_url}}" alt="{{name}}"/>{{/gravatar_url}}
+ <div class="name">
+ {{nickname}}
+ </div>
+ </div>
+ <p>{{message}}</p>
+ <div style="clear:left;"></div>
+ </li>
+ {{/items}}
+</ul>
+<p><em>Protip:</em> If you setup continuous replication between this database and a remote one, this list will reflect remote changes in near real-time.</p>
+<p>This would be a good place to add pagination.</p>
View
5 website/evently/items/_changes/query.json
@@ -0,0 +1,5 @@
+{
+ "view" : "recent-items",
+ "descending" : "true",
+ "limit" : 50
+}
View
14 website/evently/profile/profileReady/mustache.html
@@ -0,0 +1,14 @@
+<p>Most applications will customize this template (<tt>ddoc.evently.profile.profileReady.mustache</tt>) for user input.</p>
+
+<div class="avatar">
+ {{#gravatar_url}}<img src="{{gravatar_url}}"/>{{/gravatar_url}}
+ <div class="name">
+ {{name}}
+ </div>
+</div>
+
+<form>
+ <label>New message from {{nickname}}: <input type="text" name="message" size=60 value=""></label>
+</form>
+
+<div style="clear:left;"></div>
View
12 website/evently/profile/profileReady/selectors/form/submit.js
@@ -0,0 +1,12 @@
+function() {
+ var form = $(this);
+ var fdoc = form.serializeObject();
+ fdoc.created_at = new Date();
+ fdoc.profile = $$("#profile").profile;
+ $$(this).app.db.saveDoc(fdoc, {
+ success : function() {
+ form[0].reset();
+ }
+ });
+ return false;
+};
View
1  website/language
@@ -0,0 +1 @@
+javascript
View
3  website/shows/hello.js
@@ -0,0 +1,3 @@
+function(doc, req) {
+ return "Hello World";
+}
View
235 website/vendor/couchapp/_attachments/jquery.couch.app.js
@@ -0,0 +1,235 @@
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under
+// the License.
+
+// Usage: The passed in function is called when the page is ready.
+// CouchApp passes in the app object, which takes care of linking to
+// the proper database, and provides access to the CouchApp helpers.
+// $.couch.app(function(app) {
+// app.db.view(...)
+// ...
+// });
+
+(function($) {
+
+ function Design(db, name, code) {
+ this.doc_id = "_design/"+name;
+ if (code) {
+ this.code_path = this.doc_id + "/" + code;
+ } else {
+ this.code_path = this.doc_id;
+ }
+ this.view = function(view, opts) {
+ db.view(name+'/'+view, opts);
+ };
+ this.list = function(list, view, opts) {
+ db.list(name+'/'+list, view, opts);
+ };
+ }
+
+ function docForm() { alert("docForm has been moved to vendor/couchapp/lib/docForm.js, use app.require to load") };
+
+ function resolveModule(path, names, parents, current) {
+ parents = parents || [];
+ if (names.length === 0) {
+ if (typeof current != "string") {
+ throw ["error","invalid_require_path",
+ 'Must require a JavaScript string, not: '+(typeof current)];
+ }
+ return [current, parents];
+ }
+ var n = names.shift();
+ if (n == '..') {
+ parents.pop();
+ var pp = parents.pop();
+ if (!pp) {
+ throw ["error", "invalid_require_path", path];
+ }
+ return resolveModule(path, names, parents, pp);
+ } else if (n == '.') {
+ var p = parents.pop();
+ if (!p) {
+ throw ["error", "invalid_require_path", path];
+ }
+ return resolveModule(path, names, parents, p);
+ } else {
+ parents = [];
+ }
+ if (!current[n]) {
+ throw ["error", "invalid_require_path", path];
+ }
+ parents.push(current);
+ return resolveModule(path, names, parents, current[n]);
+ }
+
+ function makeRequire(ddoc) {
+ var moduleCache = [];
+ function getCachedModule(name, parents) {
+ var key, i, len = moduleCache.length;
+ for (i=0;i<len;++i) {
+ key = moduleCache[i].key;
+ if (key[0] === name && key[1] === parents) {
+ return moduleCache[i].module;
+ }
+ }
+ return null;
+ }
+ function setCachedModule(name, parents, module) {
+ moduleCache.push({ key: [name, parents], module: module });
+ }
+ var require = function (name, parents) {
+ var cachedModule = getCachedModule(name, parents);
+ if (cachedModule !== null) {
+ return cachedModule;
+ }
+ var exports = {};
+ var resolved = resolveModule(name, name.split('/'), parents, ddoc);
+ var source = resolved[0];
+ parents = resolved[1];
+ var s = "var func = function (exports, require) { " + source + " };";
+ try {
+ eval(s);
+ func.apply(ddoc, [exports, function(name) {return require(name, parents)}]);
+ } catch(e) {
+ throw ["error","compilation_error","Module require('"+name+"') raised error "+e.toSource()];
+ }
+ setCachedModule(name, parents, exports);
+ return exports;
+ }
+ return require;
+ };
+
+ function mockReq() {
+ var p = document.location.pathname.split('/'),
+ qs = document.location.search.replace(/^\?/,'').split('&'),
+ q = {};
+ qs.forEach(function(param) {
+ var ps = param.split('='),
+ k = decodeURIComponent(ps[0]),
+ v = decodeURIComponent(ps[1]);
+ if (["startkey", "endkey", "key"].indexOf(k) != -1) {
+ q[k] = JSON.parse(v);
+ } else {
+ q[k] = v;
+ }
+ });
+ p.shift();
+ return {
+ path : p,
+ query : q
+ };
+ };
+
+ $.couch.app = $.couch.app || function(appFun, opts) {
+ opts = opts || {};
+ var urlPrefix = (opts.urlPrefix || ""),
+ index = urlPrefix.split('/').length,
+ fragments = unescape(document.location.href).split('/'),
+ dbname = opts.db || fragments[index + 2],
+ dname = opts.design || fragments[index + 4];
+ $.couch.urlPrefix = urlPrefix;
+ var db = $.couch.db(dbname),
+ design = new Design(db, dname, opts.load_path);
+ var appExports = $.extend({
+ db : db,
+ design : design,
+ view : design.view,
+ list : design.list,
+ docForm : docForm, // deprecated
+ req : mockReq()
+ }, $.couch.app.app);
+ function handleDDoc(ddoc) {
+ if (ddoc) {
+ appExports.ddoc = ddoc;
+ appExports.require = makeRequire(ddoc);
+ }
+ appFun.apply(appExports, [appExports]);
+ }
+ if (opts.ddoc) {
+ // allow the ddoc to be embedded in the html
+ // to avoid a second http request
+ $.couch.app.ddocs[design.doc_id] = opts.ddoc;
+ }
+ if ($.couch.app.ddocs[design.doc_id]) {
+ $(function() {handleDDoc($.couch.app.ddocs[design.doc_id])});
+ } else {
+ // only open 1 connection for this ddoc
+ if ($.couch.app.ddoc_handlers[design.doc_id]) {
+ // we are already fetching, just wait
+ $.couch.app.ddoc_handlers[design.doc_id].push(handleDDoc);
+ } else {
+ $.couch.app.ddoc_handlers[design.doc_id] = [handleDDoc];
+ // use getDbProperty to bypass %2F encoding on _show/app
+ db.getDbProperty(design.code_path, {
+ success : function(doc) {
+ $.couch.app.ddocs[design.doc_id] = doc;
+ $.couch.app.ddoc_handlers[design.doc_id].forEach(function(h) {
+ $(function() {h(doc)});
+ });
+ $.couch.app.ddoc_handlers[design.doc_id] = null;
+ },
+ error : function() {
+ $.couch.app.ddoc_handlers[design.doc_id].forEach(function(h) {
+ $(function() {h()});
+ });
+ $.couch.app.ddoc_handlers[design.doc_id] = null;
+ }
+ });
+ }
+ }
+ };
+ $.couch.app.ddocs = {};
+ $.couch.app.ddoc_handlers = {};
+ // legacy support. $.CouchApp is deprecated, please use $.couch.app
+ $.CouchApp = $.couch.app;
+})(jQuery);
+
+// JavaScript 1.6 compatibility functions that are missing from IE7/IE8
+
+if (!Array.prototype.forEach)
+{
+ Array.prototype.forEach = function(fun /*, thisp*/)
+ {
+ var len = this.length >>> 0;
+ if (typeof fun != "function")
+ throw new TypeError();
+
+ var thisp = arguments[1];
+ for (var i = 0; i < len; i++)
+ {
+ if (i in this)
+ fun.call(thisp, this[i], i, this);
+ }
+ };
+}
+
+if (!Array.prototype.indexOf)
+{
+ Array.prototype.indexOf = function(elt)
+ {
+ var len = this.length >>> 0;
+
+ var from = Number(arguments[1]) || 0;
+ from = (from < 0)
+ ? Math.ceil(from)
+ : Math.floor(from);
+ if (from < 0)
+ from += len;
+
+ for (; from < len; from++)
+ {
+ if (from in this &&
+ this[from] === elt)
+ return from;
+ }
+ return -1;
+ };
+}
View
90 website/vendor/couchapp/_attachments/jquery.couch.app.util.js
@@ -0,0 +1,90 @@
+$.log = function(m) {
+ if (window && window.console && window.console.log) {
+ window.console.log(arguments.length == 1 ? m : arguments);
+ }
+};
+
+// http://stackoverflow.com/questions/1184624/serialize-form-to-json-with-jquery/1186309#1186309
+$.fn.serializeObject = function() {
+ var o = {};
+ var a = this.serializeArray();
+ $.each(a, function() {
+ if (o[this.name]) {
+ if (!o[this.name].push) {
+ o[this.name] = [o[this.name]];
+ }
+ o[this.name].push(this.value || '');
+ } else {
+ o[this.name] = this.value || '';
+ }
+ });
+ return o;
+};
+
+// todo remove this crap
+function escapeHTML(st) {
+ return(
+ st && st.replace(/&/g,'&amp;').
+ replace(/>/g,'&gt;').
+ replace(/</g,'&lt;').
+ replace(/"/g,'&quot;')
+ );
+};
+
+function safeHTML(st, len) {
+ return st ? escapeHTML(st.substring(0,len)) : '';
+}
+
+// todo this should take a replacement template
+$.linkify = function(body) {
+ return body.replace(/((ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?)/gi,function(a) {
+ return '<a target="_blank" href="'+a+'">'+a+'</a>';
+ }).replace(/\@([\w\-]+)/g,function(user,name) {
+ return '<a href="#/mentions/'+encodeURIComponent(name.toLowerCase())+'">'+user+'</a>';
+ }).replace(/\#([\w\-\.]+)/g,function(word,tag) {
+ return '<a href="#/tags/'+encodeURIComponent(tag.toLowerCase())+'">'+word+'</a>';
+ });
+};
+
+$.fn.prettyDate = function() {
+ $(this).each(function() {
+ var string, title = $(this).attr("title");
+ if (title) {
+ string = $.prettyDate(title);
+ } else {
+ string = $.prettyDate($(this).text());
+ }
+ $(this).text(string);
+ });
+};
+
+$.prettyDate = function(time){
+
+ var date = new Date(time.replace(/-/g,"/").replace("T", " ").replace("Z", " +0000").replace(/(\d*\:\d*:\d*)\.\d*/g,"$1")),
+ diff = (((new Date()).getTime() - date.getTime()) / 1000),
+ day_diff = Math.floor(diff / 86400);
+
+ if (isNaN(day_diff)) return time;
+
+ return day_diff < 1 && (
+ diff < 60 && "just now" ||
+ diff < 120 && "1 minute ago" ||
+ diff < 3600 && Math.floor( diff / 60 ) + " minutes ago" ||
+ diff < 7200 && "1 hour ago" ||
+ diff < 86400 && Math.floor( diff / 3600 ) + " hours ago") ||
+ day_diff == 1 && "yesterday" ||
+ day_diff < 21 && day_diff + " days ago" ||
+ day_diff < 45 && Math.ceil( day_diff / 7 ) + " weeks ago" ||
+ time;
+ // day_diff < 730 && Math.ceil( day_diff / 31 ) + " months ago" ||
+ // Math.ceil( day_diff / 365 ) + " years ago";
+};
+
+$.argsToArray = function(args) {
+ if (!args.callee) return args;
+ var array = [];
+ for (var i=0; i < args.length; i++) {
+ array.push(args[i]);
+ };
+ return array;
+}
View
399 website/vendor/couchapp/_attachments/jquery.evently.js
@@ -0,0 +1,399 @@
+// $$ inspired by @wycats: http://yehudakatz.com/2009/04/20/evented-programming-with-jquery/
+function $$(node) {
+ var data = $(node).data("$$");
+ if (data) {
+ return data;
+ } else {
+ data = {};
+ $(node).data("$$", data);
+ return data;
+ }
+};
+
+(function($) {
+ // utility functions used in the implementation
+
+ function forIn(obj, fun) {
+ var name;
+ for (name in obj) {
+ if (obj.hasOwnProperty(name)) {
+ fun(name, obj[name]);
+ }
+ }
+ };
+ $.forIn = forIn;
+ function funViaString(fun, hint) {
+ if (fun && fun.match && fun.match(/^function/)) {
+ eval("var f = "+fun);
+ if (typeof f == "function") {
+ return function() {
+ try {
+ return f.apply(this, arguments);
+ } catch(e) {
+ // IF YOU SEE AN ERROR HERE IT HAPPENED WHEN WE TRIED TO RUN YOUR FUNCTION
+ $.log({"message": "Error in evently function.", "error": e,
+ "src" : fun, "hint":hint});
+ throw(e);
+ }
+ };
+ }
+ }
+ return fun;
+ };
+
+ function runIfFun(me, fun, args) {
+ // if the field is a function, call it, bound to the widget
+ var f = funViaString(fun, me);
+ if (typeof f == "function") {
+ return f.apply(me, args);
+ } else {
+ return fun;
+ }
+ }
+
+ $.evently = {
+ connect : function(source, target, events) {
+ events.forEach(function(ev) {
+ $(source).bind(ev, function() {
+ var args = $.makeArray(arguments);
+ // remove the original event to keep from stacking args extra deep
+ // it would be nice if jquery had a way to pass the original
+ // event to the trigger method.
+ args.shift();
+ $(target).trigger(ev, args);
+ return false;
+ });
+ });
+ },
+ paths : [],
+ changesDBs : {},
+ changesOpts : {}
+ };
+
+ function extractFrom(name, evs) {
+ return evs[name];
+ };
+
+ function extractEvents(name, ddoc) {
+ // extract events from ddoc.evently and ddoc.vendor.*.evently
+ var events = [true, {}]
+ , vendor = ddoc.vendor || {}
+ , evently = ddoc.evently || {}
+ ;
+ $.forIn(vendor, function(k, v) {
+ if (v.evently && v.evently[name]) {
+ events.push(v.evently[name]);
+ }
+ });
+ if (evently[name]) {events.push(evently[name]);}
+ return $.extend.apply(null, events);
+ }
+
+ function extractPartials(ddoc) {
+ var partials = [true, {}]
+ , vendor = ddoc.vendor || {}
+ , evently = ddoc.evently || {}
+ ;
+ $.forIn(vendor, function(k, v) {
+ if (v.evently && v.evently._partials) {
+ partials.push(v.evently._partials);
+ }
+ });
+ if (evently._partials) {partials.push(evently._partials);}
+ return $.extend.apply(null, partials);
+ };
+
+ function applyCommon(events) {
+ if (events._common) {
+ $.forIn(events, function(k, v) {
+ events[k] = $.extend(true, {}, events._common, v);
+ });
+ delete events._common;
+ return events;
+ } else {
+ return events;
+ }
+ }
+
+ $.fn.evently = function(events, app, args) {
+ var elem = $(this);
+ // store the app on the element for later use
+ if (app) {
+ $$(elem).app = app;
+ }
+
+ if (typeof events == "string") {
+ events = extractEvents(events, app.ddoc);
+ }
+ events = applyCommon(events);
+ $$(elem).evently = events;
+ if (app && app.ddoc) {
+ $$(elem).partials = extractPartials(app.ddoc);
+ }
+ // setup the handlers onto elem
+ forIn(events, function(name, h) {
+ eventlyHandler(elem, name, h, args);
+ });
+
+ if (events._init) {
+ elem.trigger("_init", args);
+ }
+
+ if (app && events._changes) {
+ $("body").bind("evently-changes-"+app.db.name, function() {
+ elem.trigger("_changes");
+ });
+ followChanges(app);
+ elem.trigger("_changes");
+ }
+ };
+
+ // eventlyHandler applies the user's handler (h) to the
+ // elem, bound to trigger based on name.
+ function eventlyHandler(elem, name, h, args) {
+ if ($.evently.log) {
+ elem.bind(name, function() {
+ $.log(elem, name);
+ });
+ }
+ if (h.path) {
+ elem.pathbinder(name, h.path);
+ }
+ var f = funViaString(h, name);
+ if (typeof f == "function") {
+ elem.bind(name, {args:args}, f);
+ } else if (typeof f == "string") {
+ elem.bind(name, {args:args}, function() {
+ $(this).trigger(f, arguments);
+ return false;
+ });
+ } else if ($.isArray(h)) {
+ // handle arrays recursively
+ for (var i=0; i < h.length; i++) {
+ eventlyHandler(elem, name, h[i], args);
+ }
+ } else {
+ // an object is using the evently / mustache template system
+ if (h.fun) {
+ throw("e.fun has been removed, please rename to e.before")
+ }
+ // templates, selectors, etc are intepreted
+ // when our named event is triggered.
+ elem.bind(name, {args:args}, function() {
+ renderElement($(this), h, arguments);
+ return false;
+ });
+ }
+ };
+
+ $.fn.replace = function(elem) {
+ // $.log("Replace", this)
+ $(this).empty().append(elem);
+ };
+
+ // todo: ability to call this
+ // to render and "prepend/append/etc" a new element to the host element (me)
+ // as well as call this in a way that replaces the host elements content
+ // this would be easy if there is a simple way to get at the element we just appended
+ // (as html) so that we can attache the selectors
+ function renderElement(me, h, args, qrun, arun) {
+ // if there's a query object we run the query,
+ // and then call the data function with the response.
+ if (h.before && (!qrun || !arun)) {
+ funViaString(h.before, me).apply(me, args);
+ }
+ if (h.async && !arun) {
+ runAsync(me, h, args)
+ } else if (h.query && !qrun) {
+ // $.log("query before renderElement", arguments)
+ runQuery(me, h, args)
+ } else {
+ // $.log("renderElement")
+ // $.log(me, h, args, qrun)
+ // otherwise we just render the template with the current args
+ var selectors = runIfFun(me, h.selectors, args);
+ var act = (h.render || "replace").replace(/\s/g,"");
+ var app = $$(me).app;
+ if (h.mustache) {
+ // $.log("rendering", h.mustache)
+ var newElem = mustachioed(me, h, args);
+ me[act](newElem);
+ }
+ if (selectors) {
+ if (act == "replace") {
+ var s = me;
+ } else {
+ var s = newElem;
+ }
+ forIn(selectors, function(selector, handlers) {
+ // $.log("selector", selector);
+ // $.log("selected", $(selector, s));
+ $(selector, s).evently(handlers, app, args);
+ // $.log("applied", selector);
+ });
+ }
+ if (h.after) {
+ runIfFun(me, h.after, args);
+ }
+ }
+ };
+
+ // todo this should return the new element
+ function mustachioed(me, h, args) {
+ var partials = $$(me).partials;
+ return $($.mustache(
+ runIfFun(me, h.mustache, args),
+ runIfFun(me, h.data, args),
+ runIfFun(me, $.extend(true, partials, h.partials), args)));
+ };
+
+ function runAsync(me, h, args) {
+ // the callback is the first argument
+ funViaString(h.async, me).apply(me, [function() {
+ renderElement(me, h,
+ $.argsToArray(arguments).concat($.argsToArray(args)), false, true);
+ }].concat($.argsToArray(args)));
+ };
+
+
+ function runQuery(me, h, args) {
+ // $.log("runQuery: args", args)
+ var app = $$(me).app;
+ var qu = runIfFun(me, h.query, args);
+ var qType = qu.type;
+ var viewName = qu.view;
+ var userSuccess = qu.success;
+ // $.log("qType", qType)
+
+ var q = {};
+ forIn(qu, function(k, v) {
+ if (["type", "view"].indexOf(k) == -1) {
+ q[k] = v;
+ }
+ });
+
+ if (qType == "newRows") {
+ q.success = function(resp) {
+ // $.log("runQuery newRows success", resp.rows.length, me, resp)
+ resp.rows.reverse().forEach(function(row) {
+ renderElement(me, h, [row].concat($.argsToArray(args)), true)
+ });
+ if (userSuccess) userSuccess(resp);
+ };
+ newRows(me, app, viewName, q);
+ } else {
+ q.success = function(resp) {
+ // $.log("runQuery success", resp)
+ renderElement(me, h, [resp].concat($.argsToArray(args)), true);
+ userSuccess && userSuccess(resp);
+ };
+ // $.log(app)
+ app.view(viewName, q);
+ }
+ }
+
+ // this is for the items handler
+ // var lastViewId, highKey, inFlight;
+ // this needs to key per elem
+ function newRows(elem, app, view, opts) {
+ // $.log("newRows", arguments);
+ // on success we'll set the top key
+ var thisViewId, successCallback = opts.success, full = false;
+ function successFun(resp) {
+ // $.log("newRows success", resp)
+ $$(elem).inFlight = false;
+ var JSONhighKey = JSON.stringify($$(elem).highKey);
+ resp.rows = resp.rows.filter(function(r) {
+ return JSON.stringify(r.key) != JSONhighKey;
+ });
+ if (resp.rows.length > 0) {
+ if (opts.descending) {
+ $$(elem).highKey = resp.rows[0].key;
+ } else {
+ $$(elem).highKey = resp.rows[resp.rows.length -1].key;
+ }
+ };
+ if (successCallback) {successCallback(resp, full)};
+ };
+ opts.success = successFun;
+
+ if (opts.descending) {
+ thisViewId = view + (opts.startkey ? JSON.stringify(opts.startkey) : "");
+ } else {
+ thisViewId = view + (opts.endkey ? JSON.stringify(opts.endkey) : "");
+ }
+ // $.log(["thisViewId",thisViewId])
+ // for query we'll set keys
+ if (thisViewId == $$(elem).lastViewId) {
+ // we only want the rows newer than changesKey
+ var hk = $$(elem).highKey;
+ if (hk !== undefined) {
+ if (opts.descending) {
+ opts.endkey = hk;
+ // opts.inclusive_end = false;
+ } else {
+ opts.startkey = hk;
+ }
+ }
+ // $.log("add view rows", opts)
+ if (!$$(elem).inFlight) {
+ $$(elem).inFlight = true;
+ app.view(view, opts);
+ }
+ } else {
+ // full refresh
+ // $.log("new view stuff")
+ full = true;
+ $$(elem).lastViewId = thisViewId;
+ $$(elem).highKey = undefined;
+ $$(elem).inFlight = true;
+ app.view(view, opts);
+ }
+ };
+
+ // only start one changes listener per db
+ function followChanges(app) {
+ var dbName = app.db.name, changeEvent = function(resp) {
+ $("body").trigger("evently-changes-"+dbName, [resp]);
+ };
+ if (!$.evently.changesDBs[dbName]) {
+ if (app.db.changes) {
+ // new api in jquery.couch.js 1.0
+ app.db.changes(null, $.evently.changesOpts).onChange(changeEvent);
+ } else {
+ // in case you are still on CouchDB 0.11 ;) deprecated.
+ connectToChanges(app, changeEvent);
+ }
+ $.evently.changesDBs[dbName] = true;
+ }
+ }
+ $.evently.followChanges = followChanges;
+ // deprecated. use db.changes() from jquery.couch.js
+ // this does not have an api for closing changes request.
+ function connectToChanges(app, fun, update_seq) {
+ function changesReq(seq) {
+ var url = app.db.uri+"_changes?heartbeat=10000&feed=longpoll&since="+seq;
+ if ($.evently.changesOpts.include_docs) {
+ url = url + "&include_docs=true";
+ }
+ $.ajax({
+ url: url,
+ contentType: "application/json",
+ dataType: "json",
+ complete: function(req) {
+ var resp = $.httpData(req, "json");
+ fun(resp);
+ connectToChanges(app, fun, resp.last_seq);
+ }
+ });
+ };
+ if (update_seq) {
+ changesReq(update_seq);
+ } else {
+ app.db.info({success: function(db_info) {
+ changesReq(db_info.update_seq);
+ }});
+ }
+ };
+
+})(jQuery);
View
346 website/vendor/couchapp/_attachments/jquery.mustache.js
@@ -0,0 +1,346 @@
+/*
+Shameless port of a shameless port
+@defunkt => @janl => @aq
+
+See http://github.com/defunkt/mustache for more info.
+*/
+
+;(function($) {
+
+/*
+ mustache.js — Logic-less templates in JavaScript
+
+ See http://mustache.github.com/ for more info.
+*/
+
+var Mustache = function() {
+ var Renderer = function() {};
+
+ Renderer.prototype = {
+ otag: "{{",
+ ctag: "}}",
+ pragmas: {},
+ buffer: [],
+ pragmas_implemented: {
+ "IMPLICIT-ITERATOR": true
+ },
+ context: {},
+
+ render: function(template, context, partials, in_recursion) {
+ // reset buffer & set context
+ if(!in_recursion) {
+ this.context = context;
+ this.buffer = []; // TODO: make this non-lazy
+ }
+
+ // fail fast
+ if(!this.includes("", template)) {
+ if(in_recursion) {
+ return template;
+ } else {
+ this.send(template);
+ return;
+ }
+ }
+
+ template = this.render_pragmas(template);
+ var html = this.render_section(template, context, partials);
+ if(in_recursion) {
+ return this.render_tags(html, context, partials, in_recursion);
+ }
+
+ this.render_tags(html, context, partials, in_recursion);
+ },
+
+ /*
+ Sends parsed lines
+ */
+ send: function(line) {
+ if(line != "") {
+ this.buffer.push(line);
+ }
+ },
+
+ /*
+ Looks for %PRAGMAS
+ */
+ render_pragmas: function(template) {
+ // no pragmas
+ if(!this.includes("%", template)) {
+ return template;
+ }
+
+ var that = this;
+ var regex = new RegExp(this.otag + "%([\\w-]+) ?([\\w]+=[\\w]+)?" +
+ this.ctag);
+ return template.replace(regex, function(match, pragma, options) {
+ if(!that.pragmas_implemented[pragma]) {
+ throw({message:
+ "This implementation of mustache doesn't understand the '" +
+ pragma + "' pragma"});
+ }
+ that.pragmas[pragma] = {};
+ if(options) {
+ var opts = options.split("=");
+ that.pragmas[pragma][opts[0]] = opts[1];
+ }
+ return "";
+ // ignore unknown pragmas silently
+ });
+ },
+
+ /*
+ Tries to find a partial in the curent scope and render it
+ */
+ render_partial: function(name, context, partials) {
+ name = this.trim(name);
+ if(!partials || partials[name] === undefined) {
+ throw({message: "unknown_partial '" + name + "'"});
+ }
+ if(typeof(context[name]) != "object") {
+ return this.render(partials[name], context, partials, true);
+ }
+ return this.render(partials[name], context[name], partials, true);
+ },
+
+ /*
+ Renders inverted (^) and normal (#) sections
+ */
+ render_section: function(template, context, partials) {
+ if(!this.includes("#", template) && !this.includes("^", template)) {
+ return template;
+ }
+
+ var that = this;
+ // CSW - Added "+?" so it finds the tighest bound, not the widest
+ var regex = new RegExp(this.otag + "(\\^|\\#)\\s*(.+)\\s*" + this.ctag +
+ "\n*([\\s\\S]+?)" + this.otag + "\\/\\s*\\2\\s*" + this.ctag +
+ "\\s*", "mg");
+
+ // for each {{#foo}}{{/foo}} section do...
+ return template.replace(regex, function(match, type, name, content) {
+ var value = that.find(name, context);
+ if(type == "^") { // inverted section
+ if(!value || that.is_array(value) && value.length === 0) {
+ // false or empty list, render it
+ return that.render(content, context, partials, true);
+ } else {
+ return "";
+ }
+ } else if(type == "#") { // normal section
+ if(that.is_array(value)) { // Enumerable, Let's loop!
+ return that.map(value, function(row) {
+ return that.render(content, that.create_context(row),
+ partials, true);
+ }).join("");
+ } else if(that.is_object(value)) { // Object, Use it as subcontext!
+ return that.render(content, that.create_context(value),
+ partials, true);
+ } else if(typeof value === "function") {
+ // higher order section
+ return value.call(context, content, function(text) {
+ return that.render(text, context, partials, true);
+ });
+ } else if(value) { // boolean section
+ return that.render(content, context, partials, true);
+ } else {
+ return "";
+ }
+ }
+ });
+ },
+
+ /*
+ Replace {{foo}} and friends with values from our view
+ */
+ render_tags: function(template, context, partials, in_recursion) {
+ // tit for tat
+ var that = this;
+
+ var new_regex = function() {
+ return new RegExp(that.otag + "(=|!|>|\\{|%)?([^\\/#\\^]+?)\\1?" +
+ that.ctag + "+", "g");
+ };
+
+ var regex = new_regex();
+ var tag_replace_callback = function(match, operator, name) {
+ switch(operator) {
+ case "!": // ignore comments
+ return "";
+ case "=": // set new delimiters, rebuild the replace regexp
+ that.set_delimiters(name);
+ regex = new_regex();
+ return "";
+ case ">": // render partial
+ return that.render_partial(name, context, partials);
+ case "{": // the triple mustache is unescaped
+ return that.find(name, context);
+ default: // escape the value
+ return that.escape(that.find(name, context));
+ }
+ };
+ var lines = template.split("\n");
+ for(var i = 0; i < lines.length; i++) {
+ lines[i] = lines[i].replace(regex, tag_replace_callback, this);
+ if(!in_recursion) {
+ this.send(lines[i]);
+ }
+ }
+
+ if(in_recursion) {
+ return lines.join("\n");
+ }
+ },
+
+ set_delimiters: function(delimiters) {
+ var dels = delimiters.split(" ");
+ this.otag = this.escape_regex(dels[0]);
+ this.ctag = this.escape_regex(dels[1]);
+ },
+
+ escape_regex: function(text) {
+ // thank you Simon Willison
+ if(!arguments.callee.sRE) {
+ var specials = [
+ '/', '.', '*', '+', '?', '|',
+ '(', ')', '[', ']', '{', '}', '\\'
+ ];
+ arguments.callee.sRE = new RegExp(
+ '(\\' + specials.join('|\\') + ')', 'g'
+ );
+ }
+ return text.replace(arguments.callee.sRE, '\\$1');
+ },
+
+ /*
+ find `name` in current `context`. That is find me a value
+ from the view object
+ */
+ find: function(name, context) {
+ name = this.trim(name);
+
+ // Checks whether a value is thruthy or false or 0
+ function is_kinda_truthy(bool) {
+ return bool === false || bool === 0 || bool;
+ }
+
+ var value;
+ if(is_kinda_truthy(context[name])) {
+ value = context[name];
+ } else if(is_kinda_truthy(this.context[name])) {
+ value = this.context[name];
+ }
+
+ if(typeof value === "function") {
+ return value.apply(context);
+ }
+ if(value !== undefined) {
+ return value;
+ }
+ // silently ignore unkown variables
+ return "";
+ },
+
+ // Utility methods
+
+ /* includes tag */
+ includes: function(needle, haystack) {
+ return haystack.indexOf(this.otag + needle) != -1;
+ },
+
+ /*
+ Does away with nasty characters
+ */
+ escape: function(s) {
+ s = String(s === null ? "" : s);
+ return s.replace(/&(?!\w+;)|["<>\\]/g, function(s) {
+ switch(s) {
+ case "&": return "&amp;";
+ case "\\": return "\\\\";
+ case '"': return '\"';
+ case "<": return "&lt;";
+ case ">": return "&gt;";
+ default: return s;
+ }
+ });
+ },
+
+ // by @langalex, support for arrays of strings
+ create_context: function(_context) {
+ if(this.is_object(_context)) {
+ return _context;
+ } else {
+ var iterator = ".";
+ if(this.pragmas["IMPLICIT-ITERATOR"]) {
+ iterator = this.pragmas["IMPLICIT-ITERATOR"].iterator;
+ }
+ var ctx = {};
+ ctx[iterator] = _context;
+ return ctx;
+ }
+ },
+
+ is_object: function(a) {
+ return a && typeof a == "object";
+ },
+
+ is_array: function(a) {
+ return Object.prototype.toString.call(a) === '[object Array]';
+ },
+
+ /*
+ Gets rid of leading and trailing whitespace
+ */
+ trim: function(s) {
+ return s.replace(/^\s*|\s*$/g, "");
+ },
+
+ /*
+ Why, why, why? Because IE. Cry, cry cry.
+ */
+ map: function(array, fn) {
+ if (typeof array.map == "function") {
+ return array.map(fn);
+ } else {
+ var r = [];
+ var l = array.length;
+ for(var i = 0; i < l; i++) {
+ r.push(fn(array[i]));
+ }
+ return r;
+ }
+ }
+ };
+
+ return({
+ name: "mustache.js",
+ version: "0.3.1-dev",
+
+ /*
+ Turns a template and view into HTML
+ */
+ to_html: function(template, view, partials, send_fun) {
+ var renderer = new Renderer();
+ if(send_fun) {
+ renderer.send = send_fun;
+ }
+ renderer.render(template, view, partials);
+ if(!send_fun) {
+ return renderer.buffer.join("\n");
+ }
+ },
+ escape : function(text) {
+ return new Renderer().escape(text);
+ }
+ });
+}();
+
+ $.mustache = function(template, view, partials) {
+ return Mustache.to_html(template, view, partials);
+ };
+
+ $.mustache.escape = function(text) {
+ return Mustache.escape(text);
+ };
+
+})(jQuery);
View
174 website/vendor/couchapp/_attachments/jquery.pathbinder.js
@@ -0,0 +1,174 @@
+(function($) {
+ // functions for handling the path
+ // thanks sammy.js
+ var PATH_REPLACER = "([^\/]+)",
+ PATH_NAME_MATCHER = /:([\w\d]+)/g,
+ QUERY_STRING_MATCHER = /\?([^#]*)$/,
+ SPLAT_MATCHER = /(\*)/,
+ SPLAT_REPLACER = "(.+)",
+ _currentPath,
+ _lastPath,
+ _pathInterval;
+
+ function hashChanged() {
+ _currentPath = getPath();
+ // if path is actually changed from what we thought it was, then react
+ if (_lastPath != _currentPath) {
+ _lastPath = _currentPath;
+ return triggerOnPath(_currentPath);
+ }
+ }
+
+ $.pathbinder = {
+ changeFuns : [],
+ paths : [],
+ begin : function(defaultPath) {
+ // this should trigger the defaultPath if there's not a path in the URL
+ // otherwise it should trigger the URL's path
+ $(function() {
+ var loadPath = getPath();
+ if (loadPath) {
+ triggerOnPath(loadPath);
+ } else {
+ goPath(defaultPath);
+ triggerOnPath(defaultPath);
+ }
+ })
+ },
+ go : function(path) {
+ goPath(path);
+ triggerOnPath(path);
+ },
+ currentPath : function() {
+ return getPath();
+ },
+ onChange : function (fun) {
+ $.pathbinder.changeFuns.push(fun);
+ }
+ };
+
+ function pollPath(every) {
+ function hashCheck() {
+ _currentPath = getPath();
+ // path changed if _currentPath != _lastPath
+ if (_lastPath != _currentPath) {
+ setTimeout(function() {
+ $(window).trigger('hashchange');
+ }, 1);
+ }
+ };
+ hashCheck();
+ _pathInterval = setInterval(hashCheck, every);
+ $(window).bind('unload', function() {
+ clearInterval(_pathInterval);
+ });
+ }
+
+ function triggerOnPath(path) {
+ path = path.replace(/^#/,'');
+ $.pathbinder.changeFuns.forEach(function(fun) {fun(path)});
+ var pathSpec, path_params, params = {}, param_name, param;
+ for (var i=0; i < $.pathbinder.paths.length; i++) {
+ pathSpec = $.pathbinder.paths[i];
+ // $.log("pathSpec", pathSpec);
+ if ((path_params = pathSpec.matcher.exec(path)) !== null) {
+ // $.log("path_params", path_params);
+ path_params.shift();
+ for (var j=0; j < path_params.length; j++) {
+ param_name = pathSpec.param_names[j];
+ param = decodeURIComponent(path_params[j]);
+ if (param_name) {
+ params[param_name] = param;
+ } else {
+ if (!params.splat) params.splat = [];
+ params.splat.push(param);
+ }
+ };
+ pathSpec.callback(params);
+ // return true; // removed this to allow for multi match
+ }
+ };
+ };
+
+ // bind the event
+ $(function() {
+ if ('onhashchange' in window) {
+ // we have a native event
+ } else {
+ pollPath(10);
+ }
+ // setTimeout(hashChanged,50);
+ $(window).bind('hashchange', hashChanged);
+ });
+
+ function registerPath(pathSpec) {
+ $.pathbinder.paths.push(pathSpec);
+ };
+
+ function setPath(pathSpec, params) {
+ var newPath = $.mustache(pathSpec.template, params);
+ goPath(newPath);
+ };
+
+ function goPath(newPath) {
+ if (newPath) {
+ // $.log("goPath", newPath)
+ window.location = '#'+newPath;
+ }
+ _lastPath = getPath();
+ };
+
+ function getPath() {
+ var matches = window.location.toString().match(/^[^#]*(#.+)$/);
+ return matches ? matches[1] : '';
+ };
+
+ function makePathSpec(path, callback) {
+ var param_names = [];
+ var template = "";
+
+ PATH_NAME_MATCHER.lastIndex = 0;
+
+ while ((path_match = PATH_NAME_MATCHER.exec(path)) !== null) {
+ param_names.push(path_match[1]);
+ }
+
+ return {
+ param_names : param_names,
+ matcher : new RegExp("^" + path.replace(
+ PATH_NAME_MATCHER, PATH_REPLACER).replace(
+ SPLAT_MATCHER, SPLAT_REPLACER) + "/?$"),
+ template : path.replace(PATH_NAME_MATCHER, function(a, b) {
+ return '{{'+b+'}}';
+ }).replace(SPLAT_MATCHER, '{{splat}}'),
+ callback : callback
+ };
+ };
+
+ $.fn.pathbinder = function(name, paths, options) {
+ options = options || {};
+ var self = $(this), pathList = paths.split(/\n/);
+ $.each(pathList, function() {
+ var path = this;
+ if (path) {
+ // $.log("bind path", path);
+ var pathSpec = makePathSpec(path, function(params) {
+ // $.log("path cb", name, path, self)
+ // $.log("trigger path: "+path+" params: ", params);
+ self.trigger(name, [params]);
+ });
+ // set the path when the event triggered through other means
+ if (options.bindPath) {
+ self.bind(name, function(ev, params) {
+ params = params || {};
+ // $.log("set path", name, pathSpec)
+ setPath(pathSpec, params);
+ });
+ }
+ // trigger when the path matches
+ registerPath(pathSpec);
+ }
+ });
+ };
+})(jQuery);
+
View
17 website/vendor/couchapp/_attachments/loader.js
@@ -0,0 +1,17 @@
+
+function couchapp_load(scripts) {
+ for (var i=0; i < scripts.length; i++) {
+ document.write('<script src="'+scripts[i]+'"><\/script>')
+ };
+};
+
+couchapp_load([
+ "/_utils/script/sha1.js",
+ "/_utils/script/json2.js",
+ "/_utils/script/jquery.js",
+ "/_utils/script/jquery.couch.js",
+ "vendor/couchapp/jquery.couch.app.js",
+ "vendor/couchapp/jquery.couch.app.util.js",
+ "vendor/couchapp/jquery.mustache.js",
+ "vendor/couchapp/jquery.evently.js"
+]);
View
22 website/vendor/couchapp/evently/README.md
@@ -0,0 +1,22 @@
+## Starting the Document this code challenge
+
+I need help on this code. I only have so many hours in the day. Please be liberal about patching and hacking (and sharing code!) so we can all benefit.
+
+Docs patches are deeply appreciated. For now you can just stick Markdown files in the Docs directory.
+
+# Evently
+
+These are some vendor Evently widgets that are running on the CouchApp system.
+
+## Account
+ This is how you signup, login and logout without worry about the code.
+ Todo, we could have this work against remote APIs like that Facebook stuff or whatever.
+
+
+## Profile
+ Use this to load the local users profile for the logged in user. Useful if you're going to be posting new messages. Most applications end up customizing `profile.profileReady` to render the primary data-entry form. This gets you benefits like refreshing on login / logout, etc, automatically.
+
+
+## Docs
+ This needs to be moved to it's own app.
+ I have this vision of a docs app designed for offline editing, that involves each Markdown paragraph being it's own document, with automatic use of Bespin for code samples. Any help on this would be thanked much.
View
16 website/vendor/couchapp/evently/account/_init.js
@@ -0,0 +1,16 @@
+function() {
+ var elem = $(this);
+ $$(this).userCtx = null;
+ $.couch.session({
+ success : function(r) {
+ var userCtx = r.userCtx;
+ if (userCtx.name) {
+ elem.trigger("loggedIn", [r]);
+ } else if (userCtx.roles.indexOf("_admin") != -1) {
+ elem.trigger("adminParty");
+ } else {
+ elem.trigger("loggedOut");
+ };
+ }
+ });
+}
View
1  website/vendor/couchapp/evently/account/adminParty/mustache.html
@@ -0,0 +1 @@
+<p><strong>Admin party, everyone is admin!</strong> Fix this in <a href="/_utils/index.html">Futon</a> before proceeding.</p>
View
10 website/vendor/couchapp/evently/account/doLogin.js
@@ -0,0 +1,10 @@
+function(e, name, pass) {
+ var elem = $(this);
+ $.couch.login({
+ name : name,
+ password : pass,
+ success : function(r) {
+ elem.trigger("_init")
+ }
+ });
+}
View
8 website/vendor/couchapp/evently/account/doLogout.js
@@ -0,0 +1,8 @@
+function() {
+ var elem = $(this);
+ $.couch.logout({
+ success : function() {
+ elem.trigger("_init");
+ }
+ });
+}
View
10 website/vendor/couchapp/evently/account/doSignup.js
@@ -0,0 +1,10 @@
+function(e, name, pass) {
+ var elem = $(this);
+ $.couch.signup({
+ name : name
+ }, pass, {
+ success : function() {
+ elem.trigger("doLogin", [name, pass]);
+ }
+ });
+}
View
4 website/vendor/couchapp/evently/account/loggedIn/after.js
@@ -0,0 +1,4 @@
+function(e, r) {
+ $$(this).userCtx = r.userCtx;
+ $$(this).info = r.info;
+};
View
7 website/vendor/couchapp/evently/account/loggedIn/data.js
@@ -0,0 +1,7 @@
+function(e, r) {
+ return {
+ name : r.userCtx.name,
+ uri_name : encodeURIComponent(r.userCtx.name),
+ auth_db : encodeURIComponent(r.info.authentication_db)
+ };
+}
View
4 website/vendor/couchapp/evently/account/loggedIn/mustache.html
@@ -0,0 +1,4 @@
+<span>Welcome
+<a target="_new" href="/_utils/document.html?{{auth_db}}/org.couchdb.user%3A{{uri_name}}">{{name}}</a>!
+<a href="#logout">Logout?</a>
+</span>
View
3  website/vendor/couchapp/evently/account/loggedIn/selectors.json
@@ -0,0 +1,3 @@
+{
+ "a[href=#logout]" : {"click" : ["doLogout"]}
+}
View
1  website/vendor/couchapp/evently/account/loggedOut/mustache.html
@@ -0,0 +1 @@
+<a href="#signup">Signup</a> or <a href="#login">Login</a>
View
4 website/vendor/couchapp/evently/account/loggedOut/selectors.json
@@ -0,0 +1,4 @@
+{
+ "a[href=#signup]" : {"click" : ["signupForm"]},
+ "a[href=#login]" : {"click" : ["loginForm"]}
+}
View
3  website/vendor/couchapp/evently/account/loginForm/after.js
@@ -0,0 +1,3 @@
+function() {
+ $("input[name=name]", this).focus();
+}
View
6 website/vendor/couchapp/evently/account/loginForm/mustache.html
@@ -0,0 +1,6 @@
+<form>
+ <label for="name">Name</label> <input type="text" name="name" value="" autocapitalize="off" autocorrect="off">
+ <label for="password">Password</label> <input type="password" name="password" value="">
+ <input type="submit" value="Login">
+ <a href="#signup">or Signup</a>
+</form>
View
1  website/vendor/couchapp/evently/account/loginForm/selectors/a[href=#signup].json
@@ -0,0 +1 @@
+{"click" : ["signupForm"]}
View
6 website/vendor/couchapp/evently/account/loginForm/selectors/form/submit.js
@@ -0,0 +1,6 @@
+function(e) {
+ var name = $('input[name=name]', this).val(),
+ pass = $('input[name=password]', this).val();
+ $(this).trigger('doLogin', [name, pass]);
+ return false;
+}
View
3  website/vendor/couchapp/evently/account/signupForm/after.js
@@ -0,0 +1,3 @@
+function() {
+ $("input[name=name]", this).focus();
+}
View
6 website/vendor/couchapp/evently/account/signupForm/mustache.html
@@ -0,0 +1,6 @@
+<form>
+ <label for="name">Name</label> <input type="text" name="name" value="" autocapitalize="off" autocorrect="off">
+ <label for="password">Password</label> <input type="password" name="password" value="">
+ <input type="submit" value="Signup">
+ <a href="#login">or Login</a>
+</form>
View
1  website/vendor/couchapp/evently/account/signupForm/selectors/a[href=#login].json
@@ -0,0 +1 @@
+{"click" : ["loginForm"]}
View
6 website/vendor/couchapp/evently/account/signupForm/selectors/form/submit.js
@@ -0,0 +1,6 @@
+function(e) {
+ var name = $('input[name=name]', this).val(),
+ pass = $('input[name=password]', this).val();
+ $(this).trigger('doSignup', [name, pass]);
+ return false;
+}
View
21 website/vendor/couchapp/evently/profile/loggedIn.js
@@ -0,0 +1,21 @@
+function(e, r) {
+ var userCtx = r.userCtx;
+ var widget = $(this);
+ // load the profile from the user doc
+ var db = $.couch.db(r.info.authentication_db);
+ var userDocId = "org.couchdb.user:"+userCtx.name;
+ db.openDoc(userDocId, {
+ success : function(userDoc) {
+ var profile = userDoc["couch.app.profile"];
+ if (profile) {
+ // we copy the name to the profile so it can be used later
+ // without publishing the entire userdoc (roles, pass, etc)
+ profile.name = userDoc.name;
+ $$(widget).profile = profile;
+ widget.trigger("profileReady", [profile]);
+ } else {
+ widget.trigger("noProfile", [userCtx]);
+ }
+ }
+ });
+}
View
3  website/vendor/couchapp/evently/profile/loggedOut/after.js
@@ -0,0 +1,3 @@
+function() {
+ $$(this).profile = null;
+};
View
1  website/vendor/couchapp/evently/profile/loggedOut/mustache.html
@@ -0,0 +1 @@
+<p>Please log in to see your profile.</p>
View
3  website/vendor/couchapp/evently/profile/noProfile/data.js
@@ -0,0 +1,3 @@
+function(e, userCtx) {
+ return userCtx;
+}
View
11 website/vendor/couchapp/evently/profile/noProfile/mustache.html
@@ -0,0 +1,11 @@
+<form>
+ <p>Hello {{name}}, Please setup your user profile.</p>
+ <label for="nickname">Nickname
+ <input type="text" name="nickname" value=""></label>
+ <label for="email">Email (<em>for <a href="http://gravatar.com">Gravatar</a></em>)
+ <input type="text" name="email" value=""></label>
+ <label for="url">URL
+ <input type="text" name="url" value=""></label>
+ <input type="submit" value="Go &rarr;">
+ <input type="hidden" name="userCtxName" value="{{name}}" id="userCtxName">
+</form>
View
36 website/vendor/couchapp/evently/profile/noProfile/selectors/form/submit.js
@@ -0,0 +1,36 @@
+function() {
+ var md5 = $$(this).app.require("vendor/couchapp/lib/md5");
+
+ // TODO this can be cleaned up with docForm?
+ // it still needs the workflow to edit an existing profile
+ var name = $("input[name=userCtxName]",this).val();
+ var newProfile = {
+ rand : Math.random().toString(),
+ nickname : $("input[name=nickname]",this).val(),
+ email : $("input[name=email]",this).val(),
+ url : $("input[name=url]",this).val()
+ }, widget = $(this);
+
+ // setup gravatar_url
+ if (md5) {
+ newProfile.gravatar_url = 'http://www.gravatar.com/avatar/'+md5.hex(newProfile.email || newProfile.rand)+'.jpg?s=40&d=identicon';
+ }
+
+ // store the user profile on the user account document
+ $.couch.userDb(function(db) {
+ var userDocId = "org.couchdb.user:"+name;
+ db.openDoc(userDocId, {
+ success : function(userDoc) {
+ userDoc["couch.app.profile"] = newProfile;
+ db.saveDoc(userDoc, {
+ success : function() {
+ newProfile.name = userDoc.name;
+ $$(widget).profile = newProfile;
+ widget.trigger("profileReady", [newProfile]);
+ }
+ });
+ }
+ });
+ });
+ return false;
+}
View
3  website/vendor/couchapp/evently/profile/profileReady/after.js
@@ -0,0 +1,3 @@
+function(e, p) {
+ $$(this).profile = p;
+};
View
3  website/vendor/couchapp/evently/profile/profileReady/data.js
@@ -0,0 +1,3 @@
+function(e, p) {
+ return p
+}
View
8 website/vendor/couchapp/evently/profile/profileReady/mustache.html
@@ -0,0 +1,8 @@
+<div class="avatar">
+ {{#gravatar_url}}<img src="{{gravatar_url}}"/>{{/gravatar_url}}
+ <div class="name">
+ {{nickname}}
+ </div>
+</div>
+<p>Hello {{nickname}}!</p>
+<div style="clear:left;"></div>
View
39 website/vendor/couchapp/lib/atom.js
@@ -0,0 +1,39 @@
+// atom feed generator
+// requries E4X support.
+
+function f(n) { // Format integers to have at least two digits.
+ return n < 10 ? '0' + n : n;
+}
+
+function rfc3339(date) {
+ return date.getUTCFullYear() + '-' +
+ f(date.getUTCMonth() + 1) + '-' +
+ f(date.getUTCDate()) + 'T' +
+ f(date.getUTCHours()) + ':' +
+ f(date.getUTCMinutes()) + ':' +
+ f(date.getUTCSeconds()) + 'Z';
+};
+
+exports.header = function(data) {
+ var f = <feed xmlns="http://www.w3.org/2005/Atom"/>;
+ f.title = data.title;
+ f.id = data.feed_id;
+ f.link.@href = data.feed_link;
+ f.link.@rel = "self";
+ f.generator = "CouchApp on CouchDB";
+ f.updated = rfc3339(data.updated);
+ return f.toXMLString().replace(/\<\/feed\>/,'');
+};
+
+exports.entry = function(data) {
+ var entry = <entry/>;
+ entry.id = data.entry_id;
+ entry.title = data.title;
+ entry.content = data.content;
+ entry.content.@type = (data.content_type || 'html');
+ entry.updated = rfc3339(data.updated);
+ entry.author = <author><name>{data.author}</name></author>;
+ entry.link.@href = data.alternate;
+ entry.link.@rel = "alternate";
+ return entry;
+}
View
25 website/vendor/couchapp/lib/cache.js
@@ -0,0 +1,25 @@
+exports.get = function(db, docid, setFun, getFun) {
+ db.openDoc(docid, {
+ success : function(doc) {
+ getFun(doc.cache);
+ },
+ error : function() {
+ setFun(function(cache) {
+ db.saveDoc({
+ _id : docid,
+ cache : cache
+ });
+ getFun(cache);
+ });
+ }
+ });
+};
+
+exports.clear = function(db, docid) {
+ db.openDoc(docid, {
+ success : function(doc) {
+ db.removeDoc(doc);
+ },
+ error : function() {}
+ });
+};
View
20 website/vendor/couchapp/lib/code.js
@@ -0,0 +1,20 @@
+exports.ddoc = function(ddoc) {
+ // only return the parts of the app that we use
+ var i, j, path, key, obj, ref, out = {},
+ resources = ddoc.couchapp && ddoc.couchapp.load && ddoc.couchapp.load.app || [];
+ for (i=0; i < resources.length; i++) {
+ path = resources[i].split('/');
+ obj = ddoc;
+ ref = out;
+ for (j=0; j < path.length; j++) {
+ key = path[j];
+ ref[key] = ref[key] || {};
+ if (j < path.length - 1) {
+ obj = obj[key];
+ ref = ref[key];
+ }
+ };
+ ref[key] = obj[key];
+ };
+ return out;
+};
View
121 website/vendor/couchapp/lib/docform.js
@@ -0,0 +1,121 @@
+// Licensed under the Apache License, Version 2.0 (the "License"); you may not
+// use this file except in compliance with the License. You may obtain a copy
+// of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations under