summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--inc/config.php10
-rw-r--r--js/oekaki.js203
-rw-r--r--templates/post_form.html14
3 files changed, 227 insertions, 0 deletions
diff --git a/inc/config.php b/inc/config.php
index e889d682..ca208209 100644
--- a/inc/config.php
+++ b/inc/config.php
@@ -627,6 +627,16 @@
// Allow html in board subtitle. This is useful for placing icons and links.
$config['allow_subtitle_html'] = false;
+ /*
+ * Enable oekaki (お絵描き). This puts a drawing field on your board. You can either
+ * enable it globally or enable it on a per-board basis. Oekaki's color field
+ * can be greatly enhanced by installing jscolor from http://jscolor.com/ and installing it in js/
+ * If you enable oekaki, please remember to also enable its script with:
+ * $config['additional_javascript'][] = 'js/oekaki.js';
+ */
+
+ $config['oekaki'] = false;
+
/*
* ====================
* Display settings
diff --git a/js/oekaki.js b/js/oekaki.js
new file mode 100644
index 00000000..7bea3832
--- /dev/null
+++ b/js/oekaki.js
@@ -0,0 +1,203 @@
+$(function() {
+var canvas = $("#cvs");
+var context = canvas[0].getContext("2d");
+var is_drawing = false;
+var text = "";
+var eraser = getcolor = fill = false;
+context.strokeStyle = context.fillStyle = "black";
+
+//http://stackoverflow.com/a/5624139/1901658
+function hexToRgb(hex) {
+ var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
+ return result ? {
+ r: parseInt(result[1], 16),
+ g: parseInt(result[2], 16),
+ b: parseInt(result[3], 16)
+ } : null;
+}
+
+//http://stackoverflow.com/a/4025958/1901658
+function arraysEqual(arr1, arr2) {
+ if(arr1.length !== arr2.length)
+ return false;
+ for(var i = arr1.length; i--;) {
+ if(arr1[i] !== arr2[i])
+ return false;
+ }
+
+ return true;
+}
+
+function getmousepos(e){
+ var r = canvas[0].getBoundingClientRect();
+ mouseX = Math.round(e.clientX - r.left);
+ mouseY = Math.round(e.clientY - r.top);
+}
+
+function setcolor(){
+ context.strokeStyle = context.fillStyle = "#"+$(".color").val();
+}
+
+function flood_fill(x, y, target){
+ var pixel = context.createImageData(1,1);
+ var color = hexToRgb("#"+$(".color").val());
+ pixel.data[0] = color.r; pixel.data[1] = color.g; pixel.data[2] = color.b; pixel.data[3] = 255;
+ var queue = [];
+ var node = [x, y];
+ queue.push(node);
+ //var iters = 0;
+ while (queue.length > 0) {
+ var n = queue.pop();
+ var data = context.getImageData(n[0], n[1], 1, 1).data;
+ var d = [data[0], data[1], data[2], data[3]];
+ var t = [target[0], target[1], target[2], target[3]];
+ if (arraysEqual(d, t) && n[0] < 500 && n[1] < 250 && n[0] > -1 && n[1] > -1){
+ context.putImageData(pixel, n[0], n[1]);
+ queue.push([n[0], n[1]-1]);
+ queue.push([n[0], n[1]+1]);
+ queue.push([n[0]-1, n[1]]);
+ queue.push([n[0]+1, n[1]]);
+ //iters++;
+ }
+ //if (iters%100===0){console.log(n[0]);console.log(n[1])}
+
+ }
+ return;
+}
+
+function color_under_pixel(x, y){
+ return context.getImageData(x, y, 1, 1).data;
+}
+
+canvas.on("mousedown", function(e){
+ getmousepos(e);
+ $(this).css("cursor","none");
+ if (getcolor) {
+ var imagedata = color_under_pixel(mouseX, mouseY);
+ $("#color")[0].color.fromRGB(imagedata[0], imagedata[1], imagedata[2]);
+ getcolor = false;
+ setcolor();
+ }
+ else if (fill && !eraser) {
+ flood_fill(mouseX, mouseY, color_under_pixel(mouseX, mouseY));
+ fill = false;
+ }
+ else {
+ is_drawing = true;
+ context.beginPath();
+ context.moveTo(mouseX,mouseY);
+ context.fillText(text,mouseX,mouseY);
+ $("#confirm_oekaki").attr("checked",true);
+ }
+
+});
+
+canvas.on("mousemove", function(e){
+ getmousepos(e);
+ if (is_drawing) {
+ context.lineTo(mouseX,mouseY);
+ context.stroke()
+ }
+});
+
+canvas.on("mouseup mouseout", function(e){
+ context.stroke()
+ $(this).css("cursor","auto");
+ is_drawing = false;
+});
+
+$("#brushsize").on("click",function(){
+ var size = prompt("Enter brush size");
+ if (parseInt(size) == NaN) {
+ return
+ }
+ else {
+ context.lineWidth = size;
+ }
+});
+
+$(".color").on("change", setcolor);
+
+$("#text").on("click", function(e){
+ text = prompt("Enter some text") || "";
+ context.font = prompt("Enter font or leave alone", context.font)
+});
+
+function clear(){
+ context.beginPath();
+ context.clearRect(0,0,canvas.width(),canvas.height());
+ $("#confirm_oekaki").attr("checked",false)
+};
+
+$("#clear").on("click", clear);
+
+$("#save").on("click",function(){
+ $("#savebox").val(canvas[0].toDataURL());
+});
+
+$("#load").on("click", function(){
+ clear();
+ var img = new Image();
+ img.src = $("#savebox").val();
+ img.onload = function(){context.drawImage(img,0,0);};
+ $("#confirm_oekaki").attr("checked",true)
+});
+
+$("#eraser").on("click", function(){
+ if (eraser) {
+ eraser = false;
+ context.strokeStyle = context.fillStyle = "#"+$(".color").val();
+ context.globalCompositeOperation = old_gco;
+ }
+ else {
+ eraser = true;
+ old_gco = context.globalCompositeOperation;
+ context.globalCompositeOperation = "destination-out";
+ context.strokeStyle = "rgba(0,0,0,1)";
+ }
+});
+
+$("#getcolor").on("click", function(){
+ getcolor = true;
+});
+
+$("#fill").on("click", function(){
+ fill = true;
+});
+
+function dataURItoBlob(dataURI) {
+ var binary = atob(dataURI.split(',')[1]);
+ var array = new Array(binary.length);
+ for(var i = 0; i < binary.length; i++) {
+ array[i] = binary.charCodeAt(i);
+ }
+ return new Blob([new Uint8Array(array)], {type: 'image/jpeg'});
+}
+
+$("form[name='post']").on("submit", function(e){
+ if ($("#confirm_oekaki").is(":checked")) {
+ e.preventDefault();
+ $("input[type='file']").remove();
+ var dataURL = canvas[0].toDataURL();
+ var blob = dataURItoBlob(dataURL);
+ var fd = new FormData(document.forms[0]);
+ fd.append("file", blob, "Oekaki.png");
+ fd.append("post", $("input[name='post']").val());
+ $.ajax({
+ type: "POST",
+ url: "/post.php",
+ data: fd,
+ processData: false,
+ contentType: false,
+ success: function(data) {
+ location.reload();
+ },
+ error: function(data) {alert("Something went wrong!"); console.log(data)}
+ });
+ }
+
+ else {
+ return true;
+ };
+});
+});
diff --git a/templates/post_form.html b/templates/post_form.html
index fad81e85..d7f5cadb 100644
--- a/templates/post_form.html
+++ b/templates/post_form.html
@@ -84,6 +84,20 @@
{{ antibot.html() }}
</td>
</tr>
+ {% if config.oekaki %}
+ <tr id="oekaki">
+ <th>
+ {% trans %}Oekaki{% endtrans %}
+ </th>
+ <td>
+ <canvas width="500" height="250" id="cvs" style="border:1px solid black;-webkit-user-select: none;-moz-user-select: none;">lol what you looking at the source for nerd</canvas>
+ <p><button type="button" id="brushsize">Brush size</button><input class="color" id="color" value="000000" placeholder="Color"/><button type="button" id="text">Set text</button><button type="button" id="clear">Clear</button><button type="button" id="save">Save</button><button type="button" id="load">Load</button><br/>
+ <button type="button" id="eraser">Toggle eraser</button><button type="button" id="getcolor">Get color</button><button type="button" id="fill">Fill</button>
+ </p><p><textarea id="savebox"></textarea><label><input id="confirm_oekaki" type="checkbox"/> Use oekaki instead of file?</label></p>
+ <img id="saved" style="display:none">
+ </td>
+ </tr>
+ {% endif %}
{% if config.enable_embedding %}
<tr>
<th>