From ac764fd102c4cca2ddb0e4a42bc8b910dd6c1d72 Mon Sep 17 00:00:00 2001 From: Zixaphir Date: Sat, 31 Aug 2013 20:13:41 -0700 Subject: [PATCH] Redo image uploading Reduce size before saving, hopefully should reduce issues with quotas. This will only affect images selected via shift-click. Also, try to handle 404'd mascots. --- CHANGELOG.md | 4 +- builds/appchan-x.user.js | 156 +++++++++++++++++++++---------- builds/crx/script.js | 156 +++++++++++++++++++++---------- src/Linkification/Linkify.coffee | 2 +- src/Theming/Mascots.coffee | 124 +++++++++++++++--------- 5 files changed, 304 insertions(+), 138 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 152908309..647fa73a4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,12 @@ ### v2.3.10 *2013-08-31* +**Zixaphir**: +- Catalog bugfix + ### v2.3.9 *2013-08-30* - **MayhemYDG**: - New desktop notification: * The QR will now warn you when you are running low on cached captchas while auto-posting. diff --git a/builds/appchan-x.user.js b/builds/appchan-x.user.js index 66b8c1d28..c2b0c0954 100644 --- a/builds/appchan-x.user.js +++ b/builds/appchan-x.user.js @@ -12074,9 +12074,8 @@ innerHTML: "" }), change: function(mascot) { - var el; + var el, img; - el = this.el.firstElementChild; if (Conf['Mascot Position'] === 'default') { $.rmClass(doc, 'mascot-position-above-post-form'); $.rmClass(doc, 'mascot-position-bottom'); @@ -12092,9 +12091,34 @@ } else if (!Conf['Silhouettize Mascots']) { $.rmClass(doc, 'silhouettize-mascots'); } + el = $.el('img'); + $.on(el, 'error', MascotTools.error); el.src = Array.isArray(mascot.image) ? Style.lightTheme ? mascot.image[1] : mascot.image[0] : mascot.image; + $.off(img = this.el.firstElementChild, 'error', MascotTools.error); + $.replace(img, el); return Style.mascot.textContent = "#mascot img {\nheight: " + (mascot.height && isNaN(parseFloat(mascot.height)) ? mascot.height : mascot.height ? parseInt(mascot.height, 10) + 'px' : 'auto') + ";\nwidth: " + (mascot.width && isNaN(parseFloat(mascot.width)) ? mascot.width : mascot.width ? parseInt(mascot.width, 10) + 'px' : 'auto') + ";\n}\n#mascot {\nmargin: " + (mascot.vOffset || 0) + "px " + (mascot.hOffset || 0) + "px;\n}\n.sidebar-large #mascot {\nleft: " + (mascot.center ? 25 : 0) + "px;\nright: " + (mascot.center ? 25 : 0) + "px;\n}\n.mascot-position-above-post-form.post-form-style-fixed #mascot {\ntransform: translateY(-" + (QR.nodes ? QR.nodes.el.getBoundingClientRect().height : 0) + "px);\n}"; }, + error: function() { + var ctx, el, + _this = this; + + if (MascotTools.imageError) { + this.src = MascotTools.imageError; + } + el = $.el('canvas', { + width: 248, + height: 100 + }); + ctx = el.getContext('2d'); + ctx.font = "50px " + Conf['Font']; + ctx.fillStyle = "#" + Style.colorToHex((Themes[Conf['theme']] || Themes['Yotsuba B'])['Text']); + ctx.textAlign = 'center'; + ctx.textBaseline = 'middle'; + ctx.fillText("Mascot 404", 124, 50); + return el.toBlob(function(blob) { + return _this.src = MascotTools.imageError = URL.createObjectURL(blob); + }); + }, toggle: function() { var enabled, i, len, mascot, name, string; @@ -12149,41 +12173,47 @@ case "text": div = this.input(item, name); input = $('input', div); - if (name === 'image') { - $.on(input, 'blur', function() { - editMascot[this.name] = this.value; - return MascotTools.change(editMascot); - }); - fileInput = $.el('input', { - type: "file", - accept: "image/*", - title: "imagefile", - hidden: "hidden" - }); - $.on(input, 'click', function(evt) { - if (evt.shiftKey) { - return this.nextSibling.click(); - } - }); - $.on(fileInput, 'change', function(evt) { - return MascotTools.uploadImage(evt, this); - }); - $.after(input, fileInput); - } - if (name === 'name') { - $.on(input, 'blur', function() { - this.value = this.value.replace(/[^a-z-_0-9]/ig, "_"); - if ((this.value !== "") && !/^[a-z]/i.test(this.value)) { - return alert("Mascot names must start with a letter."); - } - editMascot[this.name] = this.value; - return MascotTools.change(editMascot); - }); - } else { - $.on(input, 'blur', function() { - editMascot[this.name] = this.value; - return MascotTools.change(editMascot); - }); + switch (name) { + case 'image': + $.on(input, 'blur', function() { + if (MascotTools.URL === this.value) { + return MascotTools.change(editMascot); + } else if (MascotTools.URL) { + URL.revokeObjectURL(MascotTools.URL); + delete MascotTools.URL; + } + editMascot[this.name] = this.value; + return MascotTools.change(editMascot); + }); + fileInput = $.el('input', { + type: "file", + accept: "image/*", + title: "imagefile", + hidden: "hidden" + }); + $.on(input, 'click', function(e) { + if (e.shiftKey) { + return this.nextSibling.click(); + } + }); + $.on(fileInput, 'change', MascotTools.uploadImage); + $.after(input, fileInput); + break; + case 'name': + $.on(input, 'blur', function() { + this.value = this.value.replace(/[^a-z-_0-9]/ig, "_"); + if ((this.value !== "") && !/^[a-z]/i.test(this.value)) { + return alert("Mascot names must start with a letter."); + } + editMascot[this.name] = this.value; + return MascotTools.change(editMascot); + }); + break; + default: + $.on(input, 'blur', function() { + editMascot[this.name] = this.value; + return MascotTools.change(editMascot); + }); } break; case "number": @@ -12250,19 +12280,51 @@ innerHTML: "
" + item[0] + "
" }); }, - uploadImage: function(evt, el) { - var file, reader; + uploadImage: function() { + var file, fileURL, img, + _this = this; - file = evt.target.files[0]; - reader = new FileReader(); - reader.onload = function(evt) { - var val; + if (!(this.files && (file = this.files[0]))) { + return; + } + if (MascotTools.URL) { + URL.revokeObjectURL(MascotTools.URL); + } + img = $.el('img'); + img.onload = function() { + var cv, height, s, width; - val = evt.target.result; - el.previousSibling.value = val; - return editMascot.image = val; + s = 400; + width = img.width, height = img.height; + if (width <= s) { + MascotTools.setImage(fileURL); + _this.previousElementSibling.value = fileURL; + return; + } + cv = $.el('canvas'); + cv.height = height = s / width * height; + cv.width = width = s; + cv.getContext('2d').drawImage(img, 0, 0, width, height); + URL.revokeObjectURL(fileURL); + return cv.toBlob(function(blob) { + var fileURL; + + MascotTools.URL = fileURL = URL.createObjectURL(MascotTools.file = blob); + MascotTools.setImage(fileURL); + return _this.previousElementSibling.value = fileURL; + }); }; - return reader.readAsDataURL(file); + MascotTools.URL = fileURL = URL.createObjectURL(MascotTools.file = file); + return img.src = fileURL; + }, + setImage: function(fileURL) { + var reader; + + reader = new FileReader(); + reader.onload = function() { + return editMascot.image = reader.result; + }; + return reader.readAsDataURL(MascotTools.file); }, save: function(mascot) { var image, name; diff --git a/builds/crx/script.js b/builds/crx/script.js index 4f443819f..e28d6e103 100644 --- a/builds/crx/script.js +++ b/builds/crx/script.js @@ -12070,9 +12070,8 @@ innerHTML: "" }), change: function(mascot) { - var el; + var el, img; - el = this.el.firstElementChild; if (Conf['Mascot Position'] === 'default') { $.rmClass(doc, 'mascot-position-above-post-form'); $.rmClass(doc, 'mascot-position-bottom'); @@ -12088,9 +12087,34 @@ } else if (!Conf['Silhouettize Mascots']) { $.rmClass(doc, 'silhouettize-mascots'); } + el = $.el('img'); + $.on(el, 'error', MascotTools.error); el.src = Array.isArray(mascot.image) ? Style.lightTheme ? mascot.image[1] : mascot.image[0] : mascot.image; + $.off(img = this.el.firstElementChild, 'error', MascotTools.error); + $.replace(img, el); return Style.mascot.textContent = "#mascot img {\nheight: " + (mascot.height && isNaN(parseFloat(mascot.height)) ? mascot.height : mascot.height ? parseInt(mascot.height, 10) + 'px' : 'auto') + ";\nwidth: " + (mascot.width && isNaN(parseFloat(mascot.width)) ? mascot.width : mascot.width ? parseInt(mascot.width, 10) + 'px' : 'auto') + ";\n}\n#mascot {\nmargin: " + (mascot.vOffset || 0) + "px " + (mascot.hOffset || 0) + "px;\n}\n.sidebar-large #mascot {\nleft: " + (mascot.center ? 25 : 0) + "px;\nright: " + (mascot.center ? 25 : 0) + "px;\n}\n.mascot-position-above-post-form.post-form-style-fixed #mascot {\n-webkit-transform: translateY(-" + (QR.nodes ? QR.nodes.el.getBoundingClientRect().height : 0) + "px);\n}"; }, + error: function() { + var ctx, el, + _this = this; + + if (MascotTools.imageError) { + this.src = MascotTools.imageError; + } + el = $.el('canvas', { + width: 248, + height: 100 + }); + ctx = el.getContext('2d'); + ctx.font = "50px " + Conf['Font']; + ctx.fillStyle = "#" + Style.colorToHex((Themes[Conf['theme']] || Themes['Yotsuba B'])['Text']); + ctx.textAlign = 'center'; + ctx.textBaseline = 'middle'; + ctx.fillText("Mascot 404", 124, 50); + return el.toBlob(function(blob) { + return _this.src = MascotTools.imageError = URL.createObjectURL(blob); + }); + }, toggle: function() { var enabled, i, len, mascot, name, string; @@ -12145,41 +12169,47 @@ case "text": div = this.input(item, name); input = $('input', div); - if (name === 'image') { - $.on(input, 'blur', function() { - editMascot[this.name] = this.value; - return MascotTools.change(editMascot); - }); - fileInput = $.el('input', { - type: "file", - accept: "image/*", - title: "imagefile", - hidden: "hidden" - }); - $.on(input, 'click', function(evt) { - if (evt.shiftKey) { - return this.nextSibling.click(); - } - }); - $.on(fileInput, 'change', function(evt) { - return MascotTools.uploadImage(evt, this); - }); - $.after(input, fileInput); - } - if (name === 'name') { - $.on(input, 'blur', function() { - this.value = this.value.replace(/[^a-z-_0-9]/ig, "_"); - if ((this.value !== "") && !/^[a-z]/i.test(this.value)) { - return alert("Mascot names must start with a letter."); - } - editMascot[this.name] = this.value; - return MascotTools.change(editMascot); - }); - } else { - $.on(input, 'blur', function() { - editMascot[this.name] = this.value; - return MascotTools.change(editMascot); - }); + switch (name) { + case 'image': + $.on(input, 'blur', function() { + if (MascotTools.URL === this.value) { + return MascotTools.change(editMascot); + } else if (MascotTools.URL) { + URL.revokeObjectURL(MascotTools.URL); + delete MascotTools.URL; + } + editMascot[this.name] = this.value; + return MascotTools.change(editMascot); + }); + fileInput = $.el('input', { + type: "file", + accept: "image/*", + title: "imagefile", + hidden: "hidden" + }); + $.on(input, 'click', function(e) { + if (e.shiftKey) { + return this.nextSibling.click(); + } + }); + $.on(fileInput, 'change', MascotTools.uploadImage); + $.after(input, fileInput); + break; + case 'name': + $.on(input, 'blur', function() { + this.value = this.value.replace(/[^a-z-_0-9]/ig, "_"); + if ((this.value !== "") && !/^[a-z]/i.test(this.value)) { + return alert("Mascot names must start with a letter."); + } + editMascot[this.name] = this.value; + return MascotTools.change(editMascot); + }); + break; + default: + $.on(input, 'blur', function() { + editMascot[this.name] = this.value; + return MascotTools.change(editMascot); + }); } break; case "number": @@ -12246,19 +12276,51 @@ innerHTML: "
" + item[0] + "
" }); }, - uploadImage: function(evt, el) { - var file, reader; + uploadImage: function() { + var file, fileURL, img, + _this = this; - file = evt.target.files[0]; - reader = new FileReader(); - reader.onload = function(evt) { - var val; + if (!(this.files && (file = this.files[0]))) { + return; + } + if (MascotTools.URL) { + URL.revokeObjectURL(MascotTools.URL); + } + img = $.el('img'); + img.onload = function() { + var cv, height, s, width; - val = evt.target.result; - el.previousSibling.value = val; - return editMascot.image = val; + s = 400; + width = img.width, height = img.height; + if (width <= s) { + MascotTools.setImage(fileURL); + _this.previousElementSibling.value = fileURL; + return; + } + cv = $.el('canvas'); + cv.height = height = s / width * height; + cv.width = width = s; + cv.getContext('2d').drawImage(img, 0, 0, width, height); + URL.revokeObjectURL(fileURL); + return cv.toBlob(function(blob) { + var fileURL; + + MascotTools.URL = fileURL = URL.createObjectURL(MascotTools.file = blob); + MascotTools.setImage(fileURL); + return _this.previousElementSibling.value = fileURL; + }); }; - return reader.readAsDataURL(file); + MascotTools.URL = fileURL = URL.createObjectURL(MascotTools.file = file); + return img.src = fileURL; + }, + setImage: function(fileURL) { + var reader; + + reader = new FileReader(); + reader.onload = function() { + return editMascot.image = reader.result; + }; + return reader.readAsDataURL(MascotTools.file); }, save: function(mascot) { var image, name; diff --git a/src/Linkification/Linkify.coffee b/src/Linkification/Linkify.coffee index f886e3b5c..b4d23ed97 100755 --- a/src/Linkification/Linkify.coffee +++ b/src/Linkification/Linkify.coffee @@ -95,7 +95,7 @@ Linkify = return makeRange: (startNode, endNode, startOffset, endOffset) -> - range = document.createRange(); + range = document.createRange() range.setStart startNode, startOffset range.setEnd endNode, endOffset range diff --git a/src/Theming/Mascots.coffee b/src/Theming/Mascots.coffee index 33252cffb..c79c4e3fe 100644 --- a/src/Theming/Mascots.coffee +++ b/src/Theming/Mascots.coffee @@ -17,8 +17,6 @@ MascotTools = innerHTML: "" change: (mascot) -> - el = @el.firstElementChild - if Conf['Mascot Position'] is 'default' $.rmClass doc, 'mascot-position-above-post-form' $.rmClass doc, 'mascot-position-bottom' @@ -34,7 +32,10 @@ MascotTools = else unless Conf['Silhouettize Mascots'] $.rmClass doc, 'silhouettize-mascots' - el.src = + el = $.el 'img' + $.on el, 'error', MascotTools.error + + el.src = if Array.isArray mascot.image if Style.lightTheme mascot.image[1] @@ -43,8 +44,26 @@ MascotTools = else mascot.image + $.off img = @el.firstElementChild, 'error', MascotTools.error + + $.replace img, el + Style.mascot.textContent = """<%= grunt.file.read('src/General/css/mascot.css') %>""" + error: -> + @src = MascotTools.imageError if MascotTools.imageError + el = $.el 'canvas', + width: 248 + height: 100 + ctx = el.getContext('2d') + ctx.font = "50px #{Conf['Font']}" + ctx.fillStyle = "#" + Style.colorToHex (Themes[Conf['theme']] or Themes['Yotsuba B'])['Text'] + ctx.textAlign = 'center' + ctx.textBaseline = 'middle' + ctx.fillText "Mascot 404", 124, 50 + el.toBlob (blob) => + @src = MascotTools.imageError = URL.createObjectURL blob + toggle: -> string = g.MASCOTSTRING enabled = Conf[string] @@ -140,40 +159,43 @@ MascotTools = div = @input item, name input = $ 'input', div - if name is 'image' - $.on input, 'blur', -> - editMascot[@name] = @value - MascotTools.change editMascot + switch name + when 'image' + $.on input, 'blur', -> + if MascotTools.URL is @value + return MascotTools.change editMascot + else if MascotTools.URL + URL.revokeObjectURL MascotTools.URL + delete MascotTools.URL + editMascot[@name] = @value + MascotTools.change editMascot - fileInput = $.el 'input', - type: "file" - accept: "image/*" - title: "imagefile" - hidden: "hidden" + fileInput = $.el 'input', + type: "file" + accept: "image/*" + title: "imagefile" + hidden: "hidden" - $.on input, 'click', (evt) -> - if evt.shiftKey - @.nextSibling.click() + $.on input, 'click', (e) -> + if e.shiftKey + @nextSibling.click() - $.on fileInput, 'change', (evt) -> - MascotTools.uploadImage evt, @ + $.on fileInput, 'change', MascotTools.uploadImage - $.after input, fileInput + $.after input, fileInput - if name is 'name' + when 'name' + $.on input, 'blur', -> + @value = @value.replace /[^a-z-_0-9]/ig, "_" + if (@value isnt "") and !/^[a-z]/i.test @value + return alert "Mascot names must start with a letter." + editMascot[@name] = @value + MascotTools.change editMascot - $.on input, 'blur', -> - @value = @value.replace /[^a-z-_0-9]/ig, "_" - if (@value isnt "") and !/^[a-z]/i.test @value - return alert "Mascot names must start with a letter." - editMascot[@name] = @value - MascotTools.change editMascot - - else - - $.on input, 'blur', -> - editMascot[@name] = @value - MascotTools.change editMascot + else + $.on input, 'blur', -> + editMascot[@name] = @value + MascotTools.change editMascot when "number" div = @input item, name @@ -230,17 +252,36 @@ MascotTools = className: "mascotvar" innerHTML: "
#{item[0]}
" - uploadImage: (evt, el) -> - file = evt.target.files[0] + uploadImage: -> + return unless @files and file = @files[0] + URL.revokeObjectURL MascotTools.URL if MascotTools.URL + img = $.el 'img' + img.onload = => + s = 400 + {width, height} = img + if width <= s + MascotTools.setImage fileURL + @previousElementSibling.value = fileURL + return + + cv = $.el 'canvas' + cv.height = height = s / width * height + cv.width = width = s + cv.getContext('2d').drawImage img, 0, 0, width, height + URL.revokeObjectURL fileURL + cv.toBlob (blob) => + MascotTools.URL = fileURL = URL.createObjectURL MascotTools.file = blob + MascotTools.setImage fileURL + @previousElementSibling.value = fileURL + + MascotTools.URL = fileURL = URL.createObjectURL MascotTools.file = file + img.src = fileURL + + setImage: (fileURL) -> reader = new FileReader() - - reader.onload = (evt) -> - val = evt.target.result - - el.previousSibling.value = val - editMascot.image = val - - reader.readAsDataURL file + reader.onload = -> + editMascot.image = reader.result + reader.readAsDataURL MascotTools.file save: (mascot) -> {name, image} = mascot @@ -296,7 +337,6 @@ MascotTools = reader = new FileReader() reader.onload = (e) -> - try imported = JSON.parse e.target.result catch err