| 1204 | There's another small detail we'll need to care of: in our `@template` decorator, we're automatically adding a `<!DOCTYPE>` declaration to any template output stream that is being serialized to HTML. For AJAX responses containing HTML fragments, we don't really want to add any kind of DOCTYPE, so we'll need to adjust the implementation of the decorator. |
| 1205 | |
| 1206 | To do that, first add an import of our `geddit/lib/ajax.py` file to the `geddit/lib/template.py` file: |
| 1207 | |
| 1208 | {{{ |
| 1209 | #!python |
| 1210 | from geddit.lib import ajax |
| 1211 | }}} |
| 1212 | |
| 1213 | Then, replace the implementation of the `output()` function with the following: |
| 1214 | |
| 1215 | {{{ |
| 1216 | #!python |
| 1217 | def output(filename, method='html', encoding='utf-8', **options): |
| 1218 | """Decorator for exposed methods to specify what template the should use |
| 1219 | for rendering, and which serialization method and options should be |
| 1220 | applied. |
| 1221 | """ |
| 1222 | def decorate(func): |
| 1223 | def wrapper(*args, **kwargs): |
| 1224 | cherrypy.thread_data.template = loader.load(filename) |
| 1225 | opt = options.copy() |
| 1226 | if not ajax.is_xhr() and method == 'html': |
| 1227 | opt.setdefault('doctype', 'html') |
| 1228 | serializer = get_serializer(method, **opt) |
| 1229 | stream = func(*args, **kwargs) |
| 1230 | if not isinstance(stream, Stream): |
| 1231 | return stream |
| 1232 | return encode(serializer(stream), method=serializer, |
| 1233 | encoding=encoding) |
| 1234 | return wrapper |
| 1235 | return decorate |
| 1236 | }}} |
| 1237 | |
| 1238 | Note how we're now only adding the `doctype='html'` serialization option when we're not handling an AJAX request. |
| 1239 | |
| 1240 | Finally, we need to add the actual Javascript logic needed to orchestrate all this. Add the following code at the bottom of the `<head>` element in the `geddit/templates/info.html` template: |
| 1241 | |
| 1242 | {{{ |
| 1243 | #!genshi |
| 1244 | <script type="text/javascript"> |
| 1245 | function loadCommentForm(a) { |
| 1246 | $.get("${url('/comment/%s/' % link.id)}", {}, function(html) { |
| 1247 | var form = a.hide().parent().after(html).next(); |
| 1248 | function closeForm() { |
| 1249 | form.slideUp("fast", function() { a.fadeIn(); form.remove() }); |
| 1250 | return false; |
| 1251 | } |
| 1252 | function initForm() { |
| 1253 | form.find("input[@name='cancel']").click(closeForm); |
| 1254 | form.submit(function() { |
| 1255 | var data = form.find("input[@type='text'], textarea").serialize(); |
| 1256 | $.post("${url('/comment/%s/' % link.id)}", data, function(html) { |
| 1257 | var elem = $(html).get(0); |
| 1258 | if (/form/i.test(elem.tagName)) { |
| 1259 | form.after(elem).remove(); |
| 1260 | form = $(elem); |
| 1261 | initForm(); |
| 1262 | } else { |
| 1263 | if ($("ul.comments").length == 0) { |
| 1264 | a.parent().before('<ul class="comments"></ul>'); |
| 1265 | } |
| 1266 | $("ul.comments") |
| 1267 | .find("li.hilite").removeClass("hilite").end() |
| 1268 | .append($(elem).addClass("hilite")).slideDown(); |
| 1269 | closeForm(); |
| 1270 | } |
| 1271 | }); |
| 1272 | return false; |
| 1273 | }); |
| 1274 | } |
| 1275 | initForm(); |
| 1276 | }); |
| 1277 | } |
| 1278 | $(document).ready(function() { |
| 1279 | $("a.action").click(function() { |
| 1280 | loadCommentForm($(this)); |
| 1281 | return false; |
| 1282 | }); |
| 1283 | }); |
| 1284 | </script> |
| 1285 | }}} |