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