diff --git a/docs/DEVELOPMENT.md b/docs/DEVELOPMENT.md
index b713d34..d9a9394 100644
--- a/docs/DEVELOPMENT.md
+++ b/docs/DEVELOPMENT.md
@@ -39,7 +39,7 @@ Compile SCSS to CSS:
Install JS components:
~> cd isso/js
- ~> bower install almond q requirejs requirejs-domready requirejs-text
+ ~> bower install almond requirejs requirejs-domready requirejs-text
Integration
diff --git a/docs/index.html b/docs/index.html
index 18437b8..1664e0a 100644
--- a/docs/index.html
+++ b/docs/index.html
@@ -30,7 +30,7 @@
client-side JavaScript
- Embed a single JS file, 54kb (18kb gzipped) and you are
+
Embed a single JS file, 52kb (16kb gzipped) and you are
done.
Supports Firefox, Safari, Chrome and IE10.
diff --git a/isso/js/app/api.js b/isso/js/app/api.js
index d13cbca..24de165 100644
--- a/isso/js/app/api.js
+++ b/isso/js/app/api.js
@@ -1,41 +1,14 @@
-define(["q"], function(Q) {
+define(["app/lib/promise"], function(Q) {
"use strict";
- Q.stopUnhandledRejectionTracking();
- Q.longStackSupport = true;
-
var salt = "Eech7co8Ohloopo9Ol6baimi",
location = window.location.pathname;
- var rules = {
- "/": [200, 404],
- "/new": [201, 202],
- "/id/\\d+": [200, 403, 404],
- "/id/\\d+/(like/dislike)": [200],
- "/count": [200]
- };
-
- /*
- * Detect Isso API endpoint. There are typically two use cases:
- *
- * 1. use minified, single-file JavaScript. The browser interprets
- * scripts sequentially, thus we can safely use the last script
- * tag. Then, we chop off some characters -- /js/embed.min.s --
- * and we're done.
- *
- * If the script is not served by Isso directly, a custom data
- * attribute can be used to override the default detection
- * mechanism:
- *
- * .. code-block:: html
- *
- *
- */
-
var script, endpoint,
js = document.getElementsByTagName("script");
+ // prefer `data-isso="//host/api/endpoint"` if provided
for (var i = 0; i < js.length; i++) {
if (js[i].hasAttribute("data-isso")) {
endpoint = js[i].getAttribute("data-isso");
@@ -43,6 +16,7 @@ define(["q"], function(Q) {
}
}
+ // if no async-script is embedded, use the last script tag of `js`
if (! endpoint) {
for (i = 0; i < js.length; i++) {
if (js[i].getAttribute("async") || js[i].getAttribute("defer")) {
@@ -61,24 +35,22 @@ define(["q"], function(Q) {
endpoint = endpoint.substring(0, endpoint.length - 1);
}
- var curl = function(method, url, data) {
+ var curl = function(method, url, data, resolve, reject) {
var xhr = new XMLHttpRequest();
- var response = Q.defer();
function onload() {
- var rule = url.replace(endpoint, "").split("?", 1)[0];
var cookie = xhr.getResponseHeader("X-Set-Cookie");
if (cookie && cookie.match(/^isso-/)) {
document.cookie = cookie;
}
- if (rule in rules && rules[rule].indexOf(xhr.status) === -1) {
- response.reject(xhr.responseText);
+ if (xhr.status >= 500) {
+ reject(xhr.body);
} else {
- response.resolve({status: xhr.status, body: xhr.responseText});
+ resolve({status: xhr.status, body: xhr.responseText});
}
}
@@ -93,11 +65,10 @@ define(["q"], function(Q) {
}
};
} catch (exception) {
- response.reject(exception.message);
+ (reject || console.log)(exception.message);
}
xhr.send(data);
- return response.promise;
};
var qs = function(params) {
@@ -112,70 +83,104 @@ define(["q"], function(Q) {
};
var create = function(tid, data) {
- return curl("POST", endpoint + "/new?" + qs({uri: tid || location}), JSON.stringify(data)).then(
- function (rv) { return JSON.parse(rv.body); });
+ var deferred = Q.defer();
+ curl("POST", endpoint + "/new?" + qs({uri: tid || location}), JSON.stringify(data),
+ function (rv) { deferred.resolve(JSON.parse(rv.body)); });
+ return deferred.promise;
};
var modify = function(id, data) {
- return curl("PUT", endpoint + "/id/" + id, JSON.stringify(data)).then(
- function (rv) { return JSON.parse(rv.body); });
+ var deferred = Q.defer();
+ curl("PUT", endpoint + "/id/" + id, JSON.stringify(data), function (rv) {
+ if (rv.status === 403) {
+ deferred.reject("Not authorized to modify this comment!");
+ } else if (rv.status === 200) {
+ deferred.resolve(JSON.parse(rv.body));
+ } else {
+ deferred.reject(rv.body);
+ }
+ });
+ return deferred.promise;
};
var remove = function(id) {
- return curl("DELETE", endpoint + "/id/" + id, null).then(function(rv) {
+ var deferred = Q.defer();
+ curl("DELETE", endpoint + "/id/" + id, null, function(rv) {
if (rv.status === 403) {
- throw "Not authorized to remove this comment!";
+ deferred.reject("Not authorized to remove this comment!");
+ } else if (rv.status === 200) {
+ deferred.resolve(JSON.parse(rv.body) === null);
+ } else {
+ deferred.reject(rv.body);
}
-
- return JSON.parse(rv.body) === null;
});
+ return deferred.promise;
};
var view = function(id, plain) {
- return curl("GET", endpoint + "/id/" + id + "?" + qs({plain: plain}), null).then(function (rv) {
- return JSON.parse(rv.body);
- });
+ var deferred = Q.defer();
+ curl("GET", endpoint + "/id/" + id + "?" + qs({plain: plain}), null,
+ function(rv) { deferred.resolve(JSON.parse(rv.body)); });
+ return deferred.promise;
};
var fetch = function(tid) {
-
- return curl("GET", endpoint + "/?" + qs({uri: tid || location}), null).then(function (rv) {
+ var deferred = Q.defer();
+ curl("GET", endpoint + "/?" + qs({uri: tid || location}), null, function(rv) {
if (rv.status === 200) {
- return JSON.parse(rv.body);
+ deferred.resolve(JSON.parse(rv.body));
+ } else if (rv.status === 404) {
+ deferred.resolve([]);
} else {
- return [];
+ deferred.reject(rv.body);
}
});
+ return deferred.promise;
};
var count = function(tid) {
- return curl("GET", endpoint + "/count?" + qs({uri: tid || location}), null).then(function(rv) {
- return JSON.parse(rv.body);
+ var deferred = Q.defer();
+ curl("GET", endpoint + "/count?" + qs({uri: tid || location}), null, function(rv) {
+ if (rv.status === 200) {
+ deferred.resolve(JSON.parse(rv.body));
+ } else if (rv.status === 404) {
+ deferred.resolve(0);
+ } else {
+ deferred.reject(rv.body);
+ }
});
+ return deferred.promise;
};
var like = function(id) {
- return curl("POST", endpoint + "/id/" + id + "/like", null).then(function(rv) {
- return JSON.parse(rv.body);
- });
+ var deferred = Q.defer();
+ curl("POST", endpoint + "/id/" + id + "/like", null,
+ function(rv) { deferred.resolve(JSON.parse(rv.body)); });
+ return deferred.promise;
};
var dislike = function(id) {
- return curl("POST", endpoint + "/id/" + id + "/dislike", null).then(function(rv) {
- return JSON.parse(rv.body);
- });
+ var deferred = Q.defer();
+ curl("POST", endpoint + "/id/" + id + "/dislike", null,
+ function(rv) { deferred.resolve(JSON.parse(rv.body)); });
+ return deferred.promise;
};
var remote_addr = function() {
- return curl("GET", endpoint + "/check-ip", null).then(function(rv) {
- return rv.body;
+ var deferred = Q.defer();
+ curl("GET", endpoint + "/check-ip", null, function(rv) {
+ if (rv.status === 200) {
+ deferred.resolve(rv.body);
+ } else {
+ deferred.reject(rv.body);
+ }
});
+ return deferred.promise;
};
return {
endpoint: endpoint,
salt: salt,
-
remote_addr: remote_addr,
create: create,
diff --git a/isso/js/app/lib/identicons.js b/isso/js/app/lib/identicons.js
index 6e462db..2ce41aa 100644
--- a/isso/js/app/lib/identicons.js
+++ b/isso/js/app/lib/identicons.js
@@ -4,7 +4,7 @@
Inspired by http://codepen.io/gschier/pen/GLvAy
*/
-define(["q"], function(Q) {
+define(["app/lib/promise"], function(Q) {
"use strict";
diff --git a/isso/js/app/lib/pbkdf2.js b/isso/js/app/lib/pbkdf2.js
index 1f8f165..db15c1f 100644
--- a/isso/js/app/lib/pbkdf2.js
+++ b/isso/js/app/lib/pbkdf2.js
@@ -1,4 +1,4 @@
-define(["q", "app/lib/sha1"], function(Q, sha1) {
+define(["app/lib/promise", "app/lib/sha1"], function(Q, sha1) {
/*
* JavaScript implementation of Password-Based Key Derivation Function 2
* (PBKDF2) as defined in RFC 2898.
@@ -191,11 +191,11 @@ define(["q", "app/lib/sha1"], function(Q, sha1) {
Q.when(text, function(text) {
var pbkdf2 = new PBKDF2(text, salt, iterations, size);
- pbkdf2.deriveKey(function(bla) {}, function(rv) {
+ pbkdf2.deriveKey(function() {}, function(rv) {
deferred.resolve(rv);
});
- })
+ });
return deferred.promise;
}
-})
\ No newline at end of file
+})
diff --git a/isso/js/app/lib/promise.js b/isso/js/app/lib/promise.js
new file mode 100644
index 0000000..4f23be9
--- /dev/null
+++ b/isso/js/app/lib/promise.js
@@ -0,0 +1,57 @@
+define(function() {
+
+ "use strict";
+
+ var stderr = function(text) { console.log(text); };
+
+ var Promise = function() {
+ this.success = [];
+ this.errors = [];
+ };
+
+ Promise.prototype.then = function(onSuccess, onError) {
+ this.success.push(onSuccess);
+ if (onError) {
+ this.errors.push(onError);
+ } else {
+ this.errors.push(stderr);
+ }
+ };
+
+ var defer = function() {
+ this.promise = new Promise();
+ };
+
+ defer.prototype = {
+ promise: Promise,
+ resolve: function(rv) {
+ this.promise.success.forEach(function(callback) {
+ window.setTimeout(function() {
+ callback(rv);
+ }, 0);
+ });
+ },
+
+ reject: function(error) {
+ this.promise.errors.forEach(function(callback) {
+ window.setTimeout(function() {
+ callback(error);
+ }, 0);
+ });
+ }
+ };
+
+ var when = function(obj, func) {
+ if (obj instanceof Promise) {
+ return obj.then(func);
+ } else {
+ return func(obj);
+ }
+ };
+
+ return {
+ defer: function() { return new defer(); },
+ when: when
+ };
+
+});
diff --git a/isso/js/config.js b/isso/js/config.js
index d6e9efc..7abcb01 100644
--- a/isso/js/config.js
+++ b/isso/js/config.js
@@ -1,6 +1,5 @@
var requirejs = {
paths: {
- q: "components/q/q",
text : "components/requirejs-text/text",
ready: "components/requirejs-domready/domReady"
},
diff --git a/isso/js/embed.js b/isso/js/embed.js
index 0ad860d..59f825f 100644
--- a/isso/js/embed.js
+++ b/isso/js/embed.js
@@ -26,23 +26,25 @@ require(["ready", "app/config", "app/api", "app/isso", "app/count", "app/dom", "
$("#isso-thread").append(new isso.Postbox(null));
$("#isso-thread").append('');
- api.fetch($("#isso-thread").getAttribute("data-isso-id")).then(function(rv) {
+ api.fetch($("#isso-thread").getAttribute("data-isso-id")).then(
+ function(rv) {
+ if (! rv.length) {
+ $("#isso-thread > h4").textContent = Mark.up("{{ i18n-no-comments }}");
+ return;
+ }
- if (! rv.length) {
- $("#isso-thread > h4").textContent = Mark.up("{{ i18n-no-comments }}");
- return;
- }
+ $("#isso-thread > h4").textContent = Mark.up("{{ i18n-num-comments | pluralize : `n` }}", {n: rv.length});
+ for (var i=0; i < rv.length; i++) {
+ isso.insert(rv[i], false);
+ }
- $("#isso-thread > h4").textContent = Mark.up("{{ i18n-num-comments | pluralize : `n` }}", {n: rv.length});
- for (var i=0; i < rv.length; i++) {
- isso.insert(rv[i], false);
+ if (window.location.hash.length > 0) {
+ $(window.location.hash).scrollIntoView();
+ }
+ },
+ function(err) {
+ console.log(err);
}
- }).fail(function(err) {
- console.log(err);
- }).done(function() {
- if (window.location.hash.length > 0) {
- $(window.location.hash).scrollIntoView();
- }
- });
+ );
});
});