##// END OF EJS Templates
Fixed form file upload
neko259 -
r534:9622eebe default
parent child Browse files
Show More
@@ -0,0 +1,7 b''
1 /*
2 * jQuery Form Plugin; v20131228
3 * http://jquery.malsup.com/form/
4 * Copyright (c) 2013 M. Alsup; Dual licensed: MIT/GPL
5 * https://github.com/malsup/form#copyright-and-license
6 */
7 ;!function(a){"use strict";"function"==typeof define&&define.amd?define(["jquery"],a):a("undefined"!=typeof jQuery?jQuery:window.Zepto)}(function(a){"use strict";function b(b){var c=b.data;b.isDefaultPrevented()||(b.preventDefault(),a(b.target).ajaxSubmit(c))}function c(b){var c=b.target,d=a(c);if(!d.is("[type=submit],[type=image]")){var e=d.closest("[type=submit]");if(0===e.length)return;c=e[0]}var f=this;if(f.clk=c,"image"==c.type)if(void 0!==b.offsetX)f.clk_x=b.offsetX,f.clk_y=b.offsetY;else if("function"==typeof a.fn.offset){var g=d.offset();f.clk_x=b.pageX-g.left,f.clk_y=b.pageY-g.top}else f.clk_x=b.pageX-c.offsetLeft,f.clk_y=b.pageY-c.offsetTop;setTimeout(function(){f.clk=f.clk_x=f.clk_y=null},100)}function d(){if(a.fn.ajaxSubmit.debug){var b="[jquery.form] "+Array.prototype.join.call(arguments,"");window.console&&window.console.log?window.console.log(b):window.opera&&window.opera.postError&&window.opera.postError(b)}}var e={};e.fileapi=void 0!==a("<input type='file'/>").get(0).files,e.formdata=void 0!==window.FormData;var f=!!a.fn.prop;a.fn.attr2=function(){if(!f)return this.attr.apply(this,arguments);var a=this.prop.apply(this,arguments);return a&&a.jquery||"string"==typeof a?a:this.attr.apply(this,arguments)},a.fn.ajaxSubmit=function(b){function c(c){var d,e,f=a.param(c,b.traditional).split("&"),g=f.length,h=[];for(d=0;g>d;d++)f[d]=f[d].replace(/\+/g," "),e=f[d].split("="),h.push([decodeURIComponent(e[0]),decodeURIComponent(e[1])]);return h}function g(d){for(var e=new FormData,f=0;f<d.length;f++)e.append(d[f].name,d[f].value);if(b.extraData){var g=c(b.extraData);for(f=0;f<g.length;f++)g[f]&&e.append(g[f][0],g[f][1])}b.data=null;var h=a.extend(!0,{},a.ajaxSettings,b,{contentType:!1,processData:!1,cache:!1,type:i||"POST"});b.uploadProgress&&(h.xhr=function(){var c=a.ajaxSettings.xhr();return c.upload&&c.upload.addEventListener("progress",function(a){var c=0,d=a.loaded||a.position,e=a.total;a.lengthComputable&&(c=Math.ceil(d/e*100)),b.uploadProgress(a,d,e,c)},!1),c}),h.data=null;var j=h.beforeSend;return h.beforeSend=function(a,c){c.data=b.formData?b.formData:e,j&&j.call(this,a,c)},a.ajax(h)}function h(c){function e(a){var b=null;try{a.contentWindow&&(b=a.contentWindow.document)}catch(c){d("cannot get iframe.contentWindow document: "+c)}if(b)return b;try{b=a.contentDocument?a.contentDocument:a.document}catch(c){d("cannot get iframe.contentDocument: "+c),b=a.document}return b}function g(){function b(){try{var a=e(r).readyState;d("state = "+a),a&&"uninitialized"==a.toLowerCase()&&setTimeout(b,50)}catch(c){d("Server abort: ",c," (",c.name,")"),h(A),w&&clearTimeout(w),w=void 0}}var c=l.attr2("target"),f=l.attr2("action"),g="multipart/form-data",j=l.attr("enctype")||l.attr("encoding")||g;x.setAttribute("target",o),(!i||/post/i.test(i))&&x.setAttribute("method","POST"),f!=m.url&&x.setAttribute("action",m.url),m.skipEncodingOverride||i&&!/post/i.test(i)||l.attr({encoding:"multipart/form-data",enctype:"multipart/form-data"}),m.timeout&&(w=setTimeout(function(){v=!0,h(z)},m.timeout));var k=[];try{if(m.extraData)for(var n in m.extraData)m.extraData.hasOwnProperty(n)&&(a.isPlainObject(m.extraData[n])&&m.extraData[n].hasOwnProperty("name")&&m.extraData[n].hasOwnProperty("value")?k.push(a('<input type="hidden" name="'+m.extraData[n].name+'">').val(m.extraData[n].value).appendTo(x)[0]):k.push(a('<input type="hidden" name="'+n+'">').val(m.extraData[n]).appendTo(x)[0]));m.iframeTarget||q.appendTo("body"),r.attachEvent?r.attachEvent("onload",h):r.addEventListener("load",h,!1),setTimeout(b,15);try{x.submit()}catch(p){var s=document.createElement("form").submit;s.apply(x)}}finally{x.setAttribute("action",f),x.setAttribute("enctype",j),c?x.setAttribute("target",c):l.removeAttr("target"),a(k).remove()}}function h(b){if(!s.aborted&&!F){if(E=e(r),E||(d("cannot access response document"),b=A),b===z&&s)return s.abort("timeout"),y.reject(s,"timeout"),void 0;if(b==A&&s)return s.abort("server abort"),y.reject(s,"error","server abort"),void 0;if(E&&E.location.href!=m.iframeSrc||v){r.detachEvent?r.detachEvent("onload",h):r.removeEventListener("load",h,!1);var c,f="success";try{if(v)throw"timeout";var g="xml"==m.dataType||E.XMLDocument||a.isXMLDoc(E);if(d("isXml="+g),!g&&window.opera&&(null===E.body||!E.body.innerHTML)&&--G)return d("requeing onLoad callback, DOM not available"),setTimeout(h,250),void 0;var i=E.body?E.body:E.documentElement;s.responseText=i?i.innerHTML:null,s.responseXML=E.XMLDocument?E.XMLDocument:E,g&&(m.dataType="xml"),s.getResponseHeader=function(a){var b={"content-type":m.dataType};return b[a.toLowerCase()]},i&&(s.status=Number(i.getAttribute("status"))||s.status,s.statusText=i.getAttribute("statusText")||s.statusText);var j=(m.dataType||"").toLowerCase(),k=/(json|script|text)/.test(j);if(k||m.textarea){var l=E.getElementsByTagName("textarea")[0];if(l)s.responseText=l.value,s.status=Number(l.getAttribute("status"))||s.status,s.statusText=l.getAttribute("statusText")||s.statusText;else if(k){var o=E.getElementsByTagName("pre")[0],p=E.getElementsByTagName("body")[0];o?s.responseText=o.textContent?o.textContent:o.innerText:p&&(s.responseText=p.textContent?p.textContent:p.innerText)}}else"xml"==j&&!s.responseXML&&s.responseText&&(s.responseXML=H(s.responseText));try{D=J(s,j,m)}catch(t){f="parsererror",s.error=c=t||f}}catch(t){d("error caught: ",t),f="error",s.error=c=t||f}s.aborted&&(d("upload aborted"),f=null),s.status&&(f=s.status>=200&&s.status<300||304===s.status?"success":"error"),"success"===f?(m.success&&m.success.call(m.context,D,"success",s),y.resolve(s.responseText,"success",s),n&&a.event.trigger("ajaxSuccess",[s,m])):f&&(void 0===c&&(c=s.statusText),m.error&&m.error.call(m.context,s,f,c),y.reject(s,"error",c),n&&a.event.trigger("ajaxError",[s,m,c])),n&&a.event.trigger("ajaxComplete",[s,m]),n&&!--a.active&&a.event.trigger("ajaxStop"),m.complete&&m.complete.call(m.context,s,f),F=!0,m.timeout&&clearTimeout(w),setTimeout(function(){m.iframeTarget?q.attr("src",m.iframeSrc):q.remove(),s.responseXML=null},100)}}}var j,k,m,n,o,q,r,s,t,u,v,w,x=l[0],y=a.Deferred();if(y.abort=function(a){s.abort(a)},c)for(k=0;k<p.length;k++)j=a(p[k]),f?j.prop("disabled",!1):j.removeAttr("disabled");if(m=a.extend(!0,{},a.ajaxSettings,b),m.context=m.context||m,o="jqFormIO"+(new Date).getTime(),m.iframeTarget?(q=a(m.iframeTarget),u=q.attr2("name"),u?o=u:q.attr2("name",o)):(q=a('<iframe name="'+o+'" src="'+m.iframeSrc+'" />'),q.css({position:"absolute",top:"-1000px",left:"-1000px"})),r=q[0],s={aborted:0,responseText:null,responseXML:null,status:0,statusText:"n/a",getAllResponseHeaders:function(){},getResponseHeader:function(){},setRequestHeader:function(){},abort:function(b){var c="timeout"===b?"timeout":"aborted";d("aborting upload... "+c),this.aborted=1;try{r.contentWindow.document.execCommand&&r.contentWindow.document.execCommand("Stop")}catch(e){}q.attr("src",m.iframeSrc),s.error=c,m.error&&m.error.call(m.context,s,c,b),n&&a.event.trigger("ajaxError",[s,m,c]),m.complete&&m.complete.call(m.context,s,c)}},n=m.global,n&&0===a.active++&&a.event.trigger("ajaxStart"),n&&a.event.trigger("ajaxSend",[s,m]),m.beforeSend&&m.beforeSend.call(m.context,s,m)===!1)return m.global&&a.active--,y.reject(),y;if(s.aborted)return y.reject(),y;t=x.clk,t&&(u=t.name,u&&!t.disabled&&(m.extraData=m.extraData||{},m.extraData[u]=t.value,"image"==t.type&&(m.extraData[u+".x"]=x.clk_x,m.extraData[u+".y"]=x.clk_y)));var z=1,A=2,B=a("meta[name=csrf-token]").attr("content"),C=a("meta[name=csrf-param]").attr("content");C&&B&&(m.extraData=m.extraData||{},m.extraData[C]=B),m.forceSync?g():setTimeout(g,10);var D,E,F,G=50,H=a.parseXML||function(a,b){return window.ActiveXObject?(b=new ActiveXObject("Microsoft.XMLDOM"),b.async="false",b.loadXML(a)):b=(new DOMParser).parseFromString(a,"text/xml"),b&&b.documentElement&&"parsererror"!=b.documentElement.nodeName?b:null},I=a.parseJSON||function(a){return window.eval("("+a+")")},J=function(b,c,d){var e=b.getResponseHeader("content-type")||"",f="xml"===c||!c&&e.indexOf("xml")>=0,g=f?b.responseXML:b.responseText;return f&&"parsererror"===g.documentElement.nodeName&&a.error&&a.error("parsererror"),d&&d.dataFilter&&(g=d.dataFilter(g,c)),"string"==typeof g&&("json"===c||!c&&e.indexOf("json")>=0?g=I(g):("script"===c||!c&&e.indexOf("javascript")>=0)&&a.globalEval(g)),g};return y}if(!this.length)return d("ajaxSubmit: skipping submit process - no element selected"),this;var i,j,k,l=this;"function"==typeof b?b={success:b}:void 0===b&&(b={}),i=b.type||this.attr2("method"),j=b.url||this.attr2("action"),k="string"==typeof j?a.trim(j):"",k=k||window.location.href||"",k&&(k=(k.match(/^([^#]+)/)||[])[1]),b=a.extend(!0,{url:k,success:a.ajaxSettings.success,type:i||a.ajaxSettings.type,iframeSrc:/^https/i.test(window.location.href||"")?"javascript:false":"about:blank"},b);var m={};if(this.trigger("form-pre-serialize",[this,b,m]),m.veto)return d("ajaxSubmit: submit vetoed via form-pre-serialize trigger"),this;if(b.beforeSerialize&&b.beforeSerialize(this,b)===!1)return d("ajaxSubmit: submit aborted via beforeSerialize callback"),this;var n=b.traditional;void 0===n&&(n=a.ajaxSettings.traditional);var o,p=[],q=this.formToArray(b.semantic,p);if(b.data&&(b.extraData=b.data,o=a.param(b.data,n)),b.beforeSubmit&&b.beforeSubmit(q,this,b)===!1)return d("ajaxSubmit: submit aborted via beforeSubmit callback"),this;if(this.trigger("form-submit-validate",[q,this,b,m]),m.veto)return d("ajaxSubmit: submit vetoed via form-submit-validate trigger"),this;var r=a.param(q,n);o&&(r=r?r+"&"+o:o),"GET"==b.type.toUpperCase()?(b.url+=(b.url.indexOf("?")>=0?"&":"?")+r,b.data=null):b.data=r;var s=[];if(b.resetForm&&s.push(function(){l.resetForm()}),b.clearForm&&s.push(function(){l.clearForm(b.includeHidden)}),!b.dataType&&b.target){var t=b.success||function(){};s.push(function(c){var d=b.replaceTarget?"replaceWith":"html";a(b.target)[d](c).each(t,arguments)})}else b.success&&s.push(b.success);if(b.success=function(a,c,d){for(var e=b.context||this,f=0,g=s.length;g>f;f++)s[f].apply(e,[a,c,d||l,l])},b.error){var u=b.error;b.error=function(a,c,d){var e=b.context||this;u.apply(e,[a,c,d,l])}}if(b.complete){var v=b.complete;b.complete=function(a,c){var d=b.context||this;v.apply(d,[a,c,l])}}var w=a("input[type=file]:enabled",this).filter(function(){return""!==a(this).val()}),x=w.length>0,y="multipart/form-data",z=l.attr("enctype")==y||l.attr("encoding")==y,A=e.fileapi&&e.formdata;d("fileAPI :"+A);var B,C=(x||z)&&!A;b.iframe!==!1&&(b.iframe||C)?b.closeKeepAlive?a.get(b.closeKeepAlive,function(){B=h(q)}):B=h(q):B=(x||z)&&A?g(q):a.ajax(b),l.removeData("jqxhr").data("jqxhr",B);for(var D=0;D<p.length;D++)p[D]=null;return this.trigger("form-submit-notify",[this,b]),this},a.fn.ajaxForm=function(e){if(e=e||{},e.delegation=e.delegation&&a.isFunction(a.fn.on),!e.delegation&&0===this.length){var f={s:this.selector,c:this.context};return!a.isReady&&f.s?(d("DOM not ready, queuing ajaxForm"),a(function(){a(f.s,f.c).ajaxForm(e)}),this):(d("terminating; zero elements found by selector"+(a.isReady?"":" (DOM not ready)")),this)}return e.delegation?(a(document).off("submit.form-plugin",this.selector,b).off("click.form-plugin",this.selector,c).on("submit.form-plugin",this.selector,e,b).on("click.form-plugin",this.selector,e,c),this):this.ajaxFormUnbind().bind("submit.form-plugin",e,b).bind("click.form-plugin",e,c)},a.fn.ajaxFormUnbind=function(){return this.unbind("submit.form-plugin click.form-plugin")},a.fn.formToArray=function(b,c){var d=[];if(0===this.length)return d;var f,g=this[0],h=this.attr("id"),i=b?g.getElementsByTagName("*"):g.elements;if(i&&(i=a(i).get()),h&&(f=a(":input[form="+h+"]").get(),f.length&&(i=(i||[]).concat(f))),!i||!i.length)return d;var j,k,l,m,n,o,p;for(j=0,o=i.length;o>j;j++)if(n=i[j],l=n.name,l&&!n.disabled)if(b&&g.clk&&"image"==n.type)g.clk==n&&(d.push({name:l,value:a(n).val(),type:n.type}),d.push({name:l+".x",value:g.clk_x},{name:l+".y",value:g.clk_y}));else if(m=a.fieldValue(n,!0),m&&m.constructor==Array)for(c&&c.push(n),k=0,p=m.length;p>k;k++)d.push({name:l,value:m[k]});else if(e.fileapi&&"file"==n.type){c&&c.push(n);var q=n.files;if(q.length)for(k=0;k<q.length;k++)d.push({name:l,value:q[k],type:n.type});else d.push({name:l,value:"",type:n.type})}else null!==m&&"undefined"!=typeof m&&(c&&c.push(n),d.push({name:l,value:m,type:n.type,required:n.required}));if(!b&&g.clk){var r=a(g.clk),s=r[0];l=s.name,l&&!s.disabled&&"image"==s.type&&(d.push({name:l,value:r.val()}),d.push({name:l+".x",value:g.clk_x},{name:l+".y",value:g.clk_y}))}return d},a.fn.formSerialize=function(b){return a.param(this.formToArray(b))},a.fn.fieldSerialize=function(b){var c=[];return this.each(function(){var d=this.name;if(d){var e=a.fieldValue(this,b);if(e&&e.constructor==Array)for(var f=0,g=e.length;g>f;f++)c.push({name:d,value:e[f]});else null!==e&&"undefined"!=typeof e&&c.push({name:this.name,value:e})}}),a.param(c)},a.fn.fieldValue=function(b){for(var c=[],d=0,e=this.length;e>d;d++){var f=this[d],g=a.fieldValue(f,b);null===g||"undefined"==typeof g||g.constructor==Array&&!g.length||(g.constructor==Array?a.merge(c,g):c.push(g))}return c},a.fieldValue=function(b,c){var d=b.name,e=b.type,f=b.tagName.toLowerCase();if(void 0===c&&(c=!0),c&&(!d||b.disabled||"reset"==e||"button"==e||("checkbox"==e||"radio"==e)&&!b.checked||("submit"==e||"image"==e)&&b.form&&b.form.clk!=b||"select"==f&&-1==b.selectedIndex))return null;if("select"==f){var g=b.selectedIndex;if(0>g)return null;for(var h=[],i=b.options,j="select-one"==e,k=j?g+1:i.length,l=j?g:0;k>l;l++){var m=i[l];if(m.selected){var n=m.value;if(n||(n=m.attributes&&m.attributes.value&&!m.attributes.value.specified?m.text:m.value),j)return n;h.push(n)}}return h}return a(b).val()},a.fn.clearForm=function(b){return this.each(function(){a("input,select,textarea",this).clearFields(b)})},a.fn.clearFields=a.fn.clearInputs=function(b){var c=/^(?:color|date|datetime|email|month|number|password|range|search|tel|text|time|url|week)$/i;return this.each(function(){var d=this.type,e=this.tagName.toLowerCase();c.test(d)||"textarea"==e?this.value="":"checkbox"==d||"radio"==d?this.checked=!1:"select"==e?this.selectedIndex=-1:"file"==d?/MSIE/.test(navigator.userAgent)?a(this).replaceWith(a(this).clone(!0)):a(this).val(""):b&&(b===!0&&/hidden/.test(d)||"string"==typeof b&&a(this).is(b))&&(this.value="")})},a.fn.resetForm=function(){return this.each(function(){("function"==typeof this.reset||"object"==typeof this.reset&&!this.reset.nodeType)&&this.reset()})},a.fn.enable=function(a){return void 0===a&&(a=!0),this.each(function(){this.disabled=!a})},a.fn.selected=function(b){return void 0===b&&(b=!0),this.each(function(){var c=this.type;if("checkbox"==c||"radio"==c)this.checked=b;else if("option"==this.tagName.toLowerCase()){var d=a(this).parent("select");b&&d[0]&&"select-one"==d[0].type&&d.find("option").selected(!1),this.selected=b}})},a.fn.ajaxSubmit.debug=!1}); No newline at end of file
@@ -1,242 +1,241 b''
1 /*
1 /*
2 @licstart The following is the entire license notice for the
2 @licstart The following is the entire license notice for the
3 JavaScript code in this page.
3 JavaScript code in this page.
4
4
5
5
6 Copyright (C) 2013 neko259
6 Copyright (C) 2013 neko259
7
7
8 The JavaScript code in this page is free software: you can
8 The JavaScript code in this page is free software: you can
9 redistribute it and/or modify it under the terms of the GNU
9 redistribute it and/or modify it under the terms of the GNU
10 General Public License (GNU GPL) as published by the Free Software
10 General Public License (GNU GPL) as published by the Free Software
11 Foundation, either version 3 of the License, or (at your option)
11 Foundation, either version 3 of the License, or (at your option)
12 any later version. The code is distributed WITHOUT ANY WARRANTY;
12 any later version. The code is distributed WITHOUT ANY WARRANTY;
13 without even the implied warranty of MERCHANTABILITY or FITNESS
13 without even the implied warranty of MERCHANTABILITY or FITNESS
14 FOR A PARTICULAR PURPOSE. See the GNU GPL for more details.
14 FOR A PARTICULAR PURPOSE. See the GNU GPL for more details.
15
15
16 As additional permission under GNU GPL version 3 section 7, you
16 As additional permission under GNU GPL version 3 section 7, you
17 may distribute non-source (e.g., minimized or compacted) forms of
17 may distribute non-source (e.g., minimized or compacted) forms of
18 that code without the copy of the GNU GPL normally required by
18 that code without the copy of the GNU GPL normally required by
19 section 4, provided you include this license notice and a URL
19 section 4, provided you include this license notice and a URL
20 through which recipients can access the Corresponding Source.
20 through which recipients can access the Corresponding Source.
21
21
22 @licend The above is the entire license notice
22 @licend The above is the entire license notice
23 for the JavaScript code in this page.
23 for the JavaScript code in this page.
24 */
24 */
25
25
26 var THREAD_UPDATE_DELAY = 10000;
26 var THREAD_UPDATE_DELAY = 10000;
27
27
28 var loading = false;
28 var loading = false;
29 var lastUpdateTime = null;
29 var lastUpdateTime = null;
30 var unreadPosts = 0
30 var unreadPosts = 0
31
31
32 function blink(node) {
32 function blink(node) {
33 var blinkCount = 2;
33 var blinkCount = 2;
34
34
35 var nodeToAnimate = node;
35 var nodeToAnimate = node;
36 for (var i = 0; i < blinkCount; i++) {
36 for (var i = 0; i < blinkCount; i++) {
37 nodeToAnimate = nodeToAnimate.fadeTo('fast', 0.5).fadeTo('fast', 1.0);
37 nodeToAnimate = nodeToAnimate.fadeTo('fast', 0.5).fadeTo('fast', 1.0);
38 }
38 }
39 }
39 }
40
40
41 function updateThread() {
41 function updateThread() {
42 if (loading) {
42 if (loading) {
43 return;
43 return;
44 }
44 }
45
45
46 loading = true;
46 loading = true;
47
47
48 var threadPosts = $('div.thread').children('.post');
48 var threadPosts = $('div.thread').children('.post');
49
49
50 var lastPost = threadPosts.last();
50 var lastPost = threadPosts.last();
51 var threadId = threadPosts.first().attr('id');
51 var threadId = threadPosts.first().attr('id');
52
52
53 var diffUrl = '/api/diff_thread/' + threadId + '/' + lastUpdateTime + '/';
53 var diffUrl = '/api/diff_thread/' + threadId + '/' + lastUpdateTime + '/';
54 $.getJSON(diffUrl)
54 $.getJSON(diffUrl)
55 .success(function(data) {
55 .success(function(data) {
56 var bottom = isPageBottom();
56 var bottom = isPageBottom();
57
57
58 var addedPosts = data.added;
58 var addedPosts = data.added;
59 for (var i = 0; i < addedPosts.length; i++) {
59 for (var i = 0; i < addedPosts.length; i++) {
60 var postText = addedPosts[i];
60 var postText = addedPosts[i];
61
61
62 var post = $(postText);
62 var post = $(postText);
63 post.appendTo(lastPost.parent());
63 post.appendTo(lastPost.parent());
64 addRefLinkPreview(post[0]);
64 addRefLinkPreview(post[0]);
65
65
66 lastPost = post;
66 lastPost = post;
67 blink(post);
67 blink(post);
68 }
68 }
69
69
70 var updatedPosts = data.updated;
70 var updatedPosts = data.updated;
71 for (var i = 0; i < updatedPosts.length; i++) {
71 for (var i = 0; i < updatedPosts.length; i++) {
72 var postText = updatedPosts[i];
72 var postText = updatedPosts[i];
73
73
74 var post = $(postText);
74 var post = $(postText);
75 var postId = post.attr('id');
75 var postId = post.attr('id');
76
76
77 var oldPost = $('div.thread').children('.post[id=' + postId + ']');
77 var oldPost = $('div.thread').children('.post[id=' + postId + ']');
78
78
79 oldPost.replaceWith(post);
79 oldPost.replaceWith(post);
80 addRefLinkPreview(post[0]);
80 addRefLinkPreview(post[0]);
81
81
82 blink(post);
82 blink(post);
83 }
83 }
84
84
85 // TODO Process deleted posts
85 // TODO Process deleted posts
86
86
87 lastUpdateTime = data.last_update;
87 lastUpdateTime = data.last_update;
88 loading = false;
88 loading = false;
89
89
90 if (bottom) {
90 if (bottom) {
91 var $target = $('html,body');
91 var $target = $('html,body');
92 $target.animate({scrollTop: $target.height()}, 1000);
92 $target.animate({scrollTop: $target.height()}, 1000);
93 }
93 }
94
94
95 $('#reply-count').text(getReplyCount());
95 $('#reply-count').text(getReplyCount());
96 $('#image-count').text(getImageCount());
96 $('#image-count').text(getImageCount());
97
97
98 updateBumplimitProgress(data.added.length);
98 updateBumplimitProgress(data.added.length);
99 updatePostBumpableStatus();
99 updatePostBumpableStatus();
100
100
101 if (data.added.length + data.updated.length > 0) {
101 if (data.added.length + data.updated.length > 0) {
102 showNewPostsTitle(data.added.length);
102 showNewPostsTitle(data.added.length);
103 }
103 }
104 })
104 })
105 .error(function(data) {
105 .error(function(data) {
106 // TODO Show error message that server is unavailable?
106 // TODO Show error message that server is unavailable?
107
107
108 loading = false;
108 loading = false;
109 });
109 });
110 }
110 }
111
111
112 function isPageBottom() {
112 function isPageBottom() {
113 var scroll = $(window).scrollTop() / ($(document).height()
113 var scroll = $(window).scrollTop() / ($(document).height()
114 - $(window).height())
114 - $(window).height())
115
115
116 return scroll == 1
116 return scroll == 1
117 }
117 }
118
118
119 function initAutoupdate() {
119 function initAutoupdate() {
120 loading = false;
120 loading = false;
121
121
122 lastUpdateTime = $('.metapanel').attr('data-last-update');
122 lastUpdateTime = $('.metapanel').attr('data-last-update');
123
123
124 setInterval(updateThread, THREAD_UPDATE_DELAY);
124 setInterval(updateThread, THREAD_UPDATE_DELAY);
125 }
125 }
126
126
127 function getReplyCount() {
127 function getReplyCount() {
128 return $('.thread').children('.post').length
128 return $('.thread').children('.post').length
129 }
129 }
130
130
131 function getImageCount() {
131 function getImageCount() {
132 return $('.thread').find('img').length
132 return $('.thread').find('img').length
133 }
133 }
134
134
135 /**
135 /**
136 * Update bumplimit progress bar
136 * Update bumplimit progress bar
137 */
137 */
138 function updateBumplimitProgress(postDelta) {
138 function updateBumplimitProgress(postDelta) {
139 var progressBar = $('#bumplimit_progress');
139 var progressBar = $('#bumplimit_progress');
140 if (progressBar) {
140 if (progressBar) {
141 var postsToLimitElement = $('#left_to_limit');
141 var postsToLimitElement = $('#left_to_limit');
142
142
143 var oldPostsToLimit = parseInt(postsToLimitElement.text());
143 var oldPostsToLimit = parseInt(postsToLimitElement.text());
144 var postCount = getReplyCount();
144 var postCount = getReplyCount();
145 var bumplimit = postCount - postDelta + oldPostsToLimit;
145 var bumplimit = postCount - postDelta + oldPostsToLimit;
146
146
147 var newPostsToLimit = bumplimit - postCount;
147 var newPostsToLimit = bumplimit - postCount;
148 if (newPostsToLimit <= 0) {
148 if (newPostsToLimit <= 0) {
149 $('.bar-bg').remove();
149 $('.bar-bg').remove();
150 } else {
150 } else {
151 postsToLimitElement.text(newPostsToLimit);
151 postsToLimitElement.text(newPostsToLimit);
152 progressBar.width((100 - postCount / bumplimit * 100.0) + '%');
152 progressBar.width((100 - postCount / bumplimit * 100.0) + '%');
153 }
153 }
154 }
154 }
155 }
155 }
156
156
157 /**
157 /**
158 * If the bumplimit is reached, add dead_post class to all posts
158 * If the bumplimit is reached, add dead_post class to all posts
159 */
159 */
160 function updatePostBumpableStatus() {
160 function updatePostBumpableStatus() {
161 var postCount = getReplyCount();
161 var postCount = getReplyCount();
162 var postsToLimitElement = $('#left_to_limit');
162 var postsToLimitElement = $('#left_to_limit');
163 var postsToLimit = parseInt(postsToLimitElement.text());
163 var postsToLimit = parseInt(postsToLimitElement.text());
164
164
165 if (postsToLimit <= 0) {
165 if (postsToLimit <= 0) {
166 $('.thread').find('.post').addClass('dead_post');
166 $('.thread').find('.post').addClass('dead_post');
167 }
167 }
168 }
168 }
169
169
170 var documentOriginalTitle = '';
170 var documentOriginalTitle = '';
171 /**
171 /**
172 * Show 'new posts' text in the title if the document is not visible to a user
172 * Show 'new posts' text in the title if the document is not visible to a user
173 */
173 */
174 function showNewPostsTitle(newPostCount) {
174 function showNewPostsTitle(newPostCount) {
175 if (document.hidden) {
175 if (document.hidden) {
176 if (documentOriginalTitle === '') {
176 if (documentOriginalTitle === '') {
177 documentOriginalTitle = document.title;
177 documentOriginalTitle = document.title;
178 }
178 }
179 unreadPosts = unreadPosts + newPostCount;
179 unreadPosts = unreadPosts + newPostCount;
180 document.title = '[' + unreadPosts + '] ' + documentOriginalTitle;
180 document.title = '[' + unreadPosts + '] ' + documentOriginalTitle;
181
181
182 document.addEventListener('visibilitychange', function() {
182 document.addEventListener('visibilitychange', function() {
183 if (documentOriginalTitle !== '') {
183 if (documentOriginalTitle !== '') {
184 document.title = documentOriginalTitle;
184 document.title = documentOriginalTitle;
185 documentOriginalTitle = '';
185 documentOriginalTitle = '';
186 unreadPosts = 0;
186 unreadPosts = 0;
187 }
187 }
188
188
189 document.removeEventListener('visibilitychange', null);
189 document.removeEventListener('visibilitychange', null);
190 });
190 });
191 }
191 }
192 }
192 }
193
193
194 /**
194 /**
195 * Clear all entered values in the form fields
195 * Clear all entered values in the form fields
196 */
196 */
197 function resetForm(form) {
197 function resetForm(form) {
198 form.find('input:text, input:password, input:file, select, textarea').val('');
198 form.find('input:text, input:password, input:file, select, textarea').val('');
199 form.find('input:radio, input:checkbox')
199 form.find('input:radio, input:checkbox')
200 .removeAttr('checked').removeAttr('selected');
200 .removeAttr('checked').removeAttr('selected');
201 }
201 }
202
202
203 // Post form data over AJAX
204 $("form").submit(function(e){
205 var threadId = $('div.thread').children('.post').first().attr('id');;
206
207 var form = $(this);
208 $.ajax({
209 url : '/api/add_post/' + threadId + '/',
210 type : form.attr('method'),
211 data : form.serialize(),
212 success: function(response){
213 var json = $.parseJSON(response);
214 var status = json.status;
215
216 var form = $('#form');
217 form.children('.form-errors').remove();
218
219 if (status === 'ok') {
220 resetForm(form);
221 updateThread();
222 } else {
223 var errors = json.errors;
224 for (var i = 0; i < errors.length; i++) {
225 var error = errors[i];
226
227 var fieldName = error.field;
228 var error = error.errors;
229
230 var errorList = $('<div class="form-errors">' + error
231 + '<div>');
232 errorList.appendTo(form);
233 }
234 }
235 }
236 });
237 return false;
238 });
239
203
240 $(document).ready(function(){
204 $(document).ready(function(){
241 initAutoupdate();
205 initAutoupdate();
206
207 // Post form data over AJAX
208 var threadId = $('div.thread').children('.post').first().attr('id');;
209
210 var form = $('#form');
211 var options = {
212 success: updateOnPost,
213 url: '/api/add_post/' + threadId + '/',
214 };
215
216 form.ajaxForm(options);
217
218 function updateOnPost(response, statusText, xhr, $form) {
219 var json = $.parseJSON(response);
220 var status = json.status;
221
222 form.children('.form-errors').remove();
223
224 if (status === 'ok') {
225 resetForm(form);
226 updateThread();
227 } else {
228 var errors = json.errors;
229 for (var i = 0; i < errors.length; i++) {
230 var error = errors[i];
231
232 var fieldName = error.field;
233 var error = error.errors;
234
235 var errorList = $('<div class="form-errors">' + error
236 + '<div>');
237 errorList.appendTo(form);
238 }
239 }
240 }
242 });
241 });
@@ -1,144 +1,145 b''
1 {% extends "boards/base.html" %}
1 {% extends "boards/base.html" %}
2
2
3 {% load i18n %}
3 {% load i18n %}
4 {% load cache %}
4 {% load cache %}
5 {% load static from staticfiles %}
5 {% load static from staticfiles %}
6 {% load board %}
6 {% load board %}
7
7
8 {% block head %}
8 {% block head %}
9 <title>{{ thread.get_opening_post.get_title|striptags|truncatewords:10 }} - Neboard</title>
9 <title>{{ thread.get_opening_post.get_title|striptags|truncatewords:10 }} - Neboard</title>
10 {% endblock %}
10 {% endblock %}
11
11
12 {% block content %}
12 {% block content %}
13 {% spaceless %}
13 {% spaceless %}
14 {% get_current_language as LANGUAGE_CODE %}
14 {% get_current_language as LANGUAGE_CODE %}
15
15
16 {% cache 600 thread_view thread.id thread.last_edit_time moderator LANGUAGE_CODE %}
16 {% cache 600 thread_view thread.id thread.last_edit_time moderator LANGUAGE_CODE %}
17
17
18 <div class="image-mode-tab">
18 <div class="image-mode-tab">
19 <a class="current_mode" href="{% url 'thread' thread.get_opening_post.id %}">{% trans 'Normal mode' %}</a>,
19 <a class="current_mode" href="{% url 'thread' thread.get_opening_post.id %}">{% trans 'Normal mode' %}</a>,
20 <a href="{% url 'thread_mode' thread.get_opening_post.id 'gallery' %}">{% trans 'Gallery mode' %}</a>
20 <a href="{% url 'thread_mode' thread.get_opening_post.id 'gallery' %}">{% trans 'Gallery mode' %}</a>
21 </div>
21 </div>
22
22
23 {% if bumpable %}
23 {% if bumpable %}
24 <div class="bar-bg">
24 <div class="bar-bg">
25 <div class="bar-value" style="width:{{ bumplimit_progress }}%" id="bumplimit_progress">
25 <div class="bar-value" style="width:{{ bumplimit_progress }}%" id="bumplimit_progress">
26 </div>
26 </div>
27 <div class="bar-text">
27 <div class="bar-text">
28 <span id="left_to_limit">{{ posts_left }}</span> {% trans 'posts to bumplimit' %}
28 <span id="left_to_limit">{{ posts_left }}</span> {% trans 'posts to bumplimit' %}
29 </div>
29 </div>
30 </div>
30 </div>
31 {% endif %}
31 {% endif %}
32 <div class="thread">
32 <div class="thread">
33 {% for post in posts %}
33 {% for post in posts %}
34 {% if bumpable %}
34 {% if bumpable %}
35 <div class="post" id="{{ post.id }}">
35 <div class="post" id="{{ post.id }}">
36 {% elif thread.archived %}
36 {% elif thread.archived %}
37 <div class="post archive_post" id="{{ post.id }}">
37 <div class="post archive_post" id="{{ post.id }}">
38 {% else %}
38 {% else %}
39 <div class="post dead_post" id="{{ post.id }}">
39 <div class="post dead_post" id="{{ post.id }}">
40 {% endif %}
40 {% endif %}
41 {% if post.image %}
41 {% if post.image %}
42 <div class="image">
42 <div class="image">
43 <a
43 <a
44 class="thumb"
44 class="thumb"
45 href="{{ post.image.url }}"><img
45 href="{{ post.image.url }}"><img
46 src="{{ post.image.url_200x150 }}"
46 src="{{ post.image.url_200x150 }}"
47 alt="{{ post.id }}"
47 alt="{{ post.id }}"
48 width="{{ post.image_pre_width }}"
48 width="{{ post.image_pre_width }}"
49 height="{{ post.image_pre_height }}"
49 height="{{ post.image_pre_height }}"
50 data-width="{{ post.image_width }}"
50 data-width="{{ post.image_width }}"
51 data-height="{{ post.image_height }}"/>
51 data-height="{{ post.image_height }}"/>
52 </a>
52 </a>
53 </div>
53 </div>
54 {% endif %}
54 {% endif %}
55 <div class="message">
55 <div class="message">
56 <div class="post-info">
56 <div class="post-info">
57 <span class="title">{{ post.title }}</span>
57 <span class="title">{{ post.title }}</span>
58 <a class="post_id" href="#{{ post.id }}">
58 <a class="post_id" href="#{{ post.id }}">
59 ({{ post.id }})</a>
59 ({{ post.id }})</a>
60 [{{ post.pub_time }}]
60 [{{ post.pub_time }}]
61 {% if not thread.archived %}
61 {% if not thread.archived %}
62 [<a href="#" onclick="javascript:addQuickReply('{{ post.id }}')
62 [<a href="#" onclick="javascript:addQuickReply('{{ post.id }}')
63 ; return false;">&gt;&gt;</a>]
63 ; return false;">&gt;&gt;</a>]
64 {% endif %}
64 {% endif %}
65
65
66 {% if moderator %}
66 {% if moderator %}
67 <span class="moderator_info">
67 <span class="moderator_info">
68 [<a href="{% url 'delete' post_id=post.id %}"
68 [<a href="{% url 'delete' post_id=post.id %}"
69 >{% trans 'Delete' %}</a>]
69 >{% trans 'Delete' %}</a>]
70 ({{ post.poster_ip }})
70 ({{ post.poster_ip }})
71 [<a href="{% url 'ban' post_id=post.id %}?next={{ request.path }}"
71 [<a href="{% url 'ban' post_id=post.id %}?next={{ request.path }}"
72 >{% trans 'Ban IP' %}</a>]
72 >{% trans 'Ban IP' %}</a>]
73 </span>
73 </span>
74 {% endif %}
74 {% endif %}
75 </div>
75 </div>
76 {% autoescape off %}
76 {% autoescape off %}
77 {{ post.text.rendered }}
77 {{ post.text.rendered }}
78 {% endautoescape %}
78 {% endautoescape %}
79 {% if post.is_referenced %}
79 {% if post.is_referenced %}
80 <div class="refmap">
80 <div class="refmap">
81 {% trans "Replies" %}:
81 {% trans "Replies" %}:
82 {% for ref_post in post.get_sorted_referenced_posts %}
82 {% for ref_post in post.get_sorted_referenced_posts %}
83 <a href="{% post_url ref_post.id %}">&gt;&gt;{{ ref_post.id }}</a
83 <a href="{% post_url ref_post.id %}">&gt;&gt;{{ ref_post.id }}</a
84 >{% if not forloop.last %},{% endif %}
84 >{% if not forloop.last %},{% endif %}
85 {% endfor %}
85 {% endfor %}
86 </div>
86 </div>
87 {% endif %}
87 {% endif %}
88 </div>
88 </div>
89 {% if forloop.first %}
89 {% if forloop.first %}
90 <div class="metadata">
90 <div class="metadata">
91 <span class="tags">
91 <span class="tags">
92 {% for tag in thread.get_tags %}
92 {% for tag in thread.get_tags %}
93 <a class="tag" href="{% url 'tag' tag.name %}">
93 <a class="tag" href="{% url 'tag' tag.name %}">
94 #{{ tag.name }}</a
94 #{{ tag.name }}</a
95 >{% if not forloop.last %},{% endif %}
95 >{% if not forloop.last %},{% endif %}
96 {% endfor %}
96 {% endfor %}
97 </span>
97 </span>
98 </div>
98 </div>
99 {% endif %}
99 {% endif %}
100 </div>
100 </div>
101 {% endfor %}
101 {% endfor %}
102 </div>
102 </div>
103 {% endcache %}
103 {% endcache %}
104
104
105 {% if not thread.archived %}
105 {% if not thread.archived %}
106
106
107 <div class="post-form-w">
107 <div class="post-form-w">
108 <script src="{% static 'js/panel.js' %}"></script>
108 <script src="{% static 'js/panel.js' %}"></script>
109 <div class="form-title">{% trans "Reply to thread" %} #{{ thread.get_opening_post.id }}</div>
109 <div class="form-title">{% trans "Reply to thread" %} #{{ thread.get_opening_post.id }}</div>
110 <div class="post-form">
110 <div class="post-form">
111 <form id="form" enctype="multipart/form-data" method="post"
111 <form id="form" enctype="multipart/form-data" method="post"
112 >{% csrf_token %}
112 >{% csrf_token %}
113 {{ form.as_div }}
113 {{ form.as_div }}
114 <div class="form-submit">
114 <div class="form-submit">
115 <input type="submit" value="{% trans "Post" %}"/>
115 <input type="submit" value="{% trans "Post" %}"/>
116 </div>
116 </div>
117 </form>
117 </form>
118 <div><a href="{% url "staticpage" name="help" %}">
118 <div><a href="{% url "staticpage" name="help" %}">
119 {% trans 'Text syntax' %}</a></div>
119 {% trans 'Text syntax' %}</a></div>
120 </div>
120 </div>
121 </div>
121 </div>
122
122
123 <script src="{% static 'js/thread_update.js' %}"></script>
123 <script src="{% static 'js/jquery.form.min.js' %}"></script>
124 <script src="{% static 'js/thread_update.js' %}"></script>
124 {% endif %}
125 {% endif %}
125
126
126 <script src="{% static 'js/thread.js' %}"></script>
127 <script src="{% static 'js/thread.js' %}"></script>
127
128
128 {% endspaceless %}
129 {% endspaceless %}
129 {% endblock %}
130 {% endblock %}
130
131
131 {% block metapanel %}
132 {% block metapanel %}
132
133
133 {% get_current_language as LANGUAGE_CODE %}
134 {% get_current_language as LANGUAGE_CODE %}
134
135
135 <span class="metapanel" data-last-update="{{ last_update }}">
136 <span class="metapanel" data-last-update="{{ last_update }}">
136 {% cache 600 thread_meta thread.last_edit_time moderator LANGUAGE_CODE %}
137 {% cache 600 thread_meta thread.last_edit_time moderator LANGUAGE_CODE %}
137 <span id="reply-count">{{ thread.get_reply_count }}</span> {% trans 'replies' %},
138 <span id="reply-count">{{ thread.get_reply_count }}</span> {% trans 'replies' %},
138 <span id="image-count">{{ thread.get_images_count }}</span> {% trans 'images' %}.
139 <span id="image-count">{{ thread.get_images_count }}</span> {% trans 'images' %}.
139 {% trans 'Last update: ' %}{{ thread.last_edit_time }}
140 {% trans 'Last update: ' %}{{ thread.last_edit_time }}
140 [<a href="rss/">RSS</a>]
141 [<a href="rss/">RSS</a>]
141 {% endcache %}
142 {% endcache %}
142 </span>
143 </span>
143
144
144 {% endblock %}
145 {% endblock %}
General Comments 0
You need to be logged in to leave comments. Login now