You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
doushio/client/posting.js

1039 lines
24 KiB

var saku, postForm;
var UPLOADING_MSG = 'Uploading...';
var PLACEHOLDER = '〉〉〉';
connSM.on('synced', postSM.feeder('sync'));
connSM.on('dropped', postSM.feeder('desync'));
connSM.on('desynced', postSM.feeder('desync'));
postSM.act('* + desync -> none', function () {
if (postForm) {
postForm.$el.removeClass('editing');
postForm.$input.val('');
postForm.finish();
}
$('aside').remove();
});
postSM.act('none + sync, draft, alloc + done -> ready', function () {
if (postForm) {
postForm.remove();
postForm = null;
saku = null;
}
insert_pbs();
var m = window.location.hash.match(/^#q(\d+)$/);
if (m) {
var id = parseInt(m[1], 10);
if ($('#' + id).hasClass('highlight')) {
window.location.hash = '#' + id;
open_post_box(id);
postForm.add_ref(id);
}
}
});
postSM.act('ready + new -> draft', function (aside) {
var op = null;
var $sec = aside.closest('section');
if ($sec.length) {
op = extract_num($sec);
}
else {
$sec = $('<section/>');
}
saku = new Saku({op: op});
postForm = new ComposerView({model: saku, dest: aside, thread: $sec});
});
postSM.preflight('draft', function (aside) {
return aside.is('aside');
});
postSM.act('draft + alloc -> alloc', function (msg) {
postForm.on_allocation(msg);
});
$DOC.on('click', 'aside a', _.wrap(function () {
postSM.feed('new', $(this).parent());
}, with_dom));
$DOC.on('keydown', handle_shortcut);
var vapor = 0, wombo = 0, eject = 0;
menuHandlers.Eject = function () {
vapor = wombo = eject = 0;
ComposerView.prototype.word_filter = function (w) { return w; };
flash_bg('white');
};
function handle_shortcut(event) {
var k = event.which;
if (vapor < 0 || wombo < 0) {
if (event.shiftKey && k == [69,74,69,67,84,49][eject]) {
if (++eject >= 6) {
menuHandlers.Eject();
event.stopImmediatePropagation();
event.preventDefault();
}
}
else
eject = 0;
}
else if (event.shiftKey && k > 85 && k < 88) {
if (k == 86 && ++vapor > 10) {
menuHandlers.Vapor();
event.stopImmediatePropagation();
event.preventDefault();
}
if (k == 87 && ++wombo > 10) {
wombo = -1;
$.getScript(mediaURL + 'js/wordfilter.js');
}
}
else
vapor = wombo = 0;
if (!event.altKey)
return;
var used = false;
switch (event.which) {
case shortcutKeys['new']:
var $aside = THREAD ? $('aside') : $ceiling.next();
if ($aside.is('aside') && $aside.length == 1) {
with_dom(function () {
postSM.feed('new', $aside);
});
used = true;
}
break;
case shortcutKeys.togglespoiler:
if (postForm) {
postForm.on_toggle(event);
used = true;
}
break;
case shortcutKeys.done:
if (postForm) {
if (!postForm.submit.attr('disabled')) {
postForm.finish_wrapped();
used = true;
}
}
break;
case shortcutKeys.flip:
menuHandlers.Flip();
used = true;
break;
}
if (used) {
event.stopImmediatePropagation();
event.preventDefault();
}
}
function open_post_box(num) {
var a = $('#' + num);
postSM.feed('new', a.is('section')
? a.children('aside') : a.siblings('aside'));
}
function make_reply_box() {
return $('<aside class="act"><a>Reply</a></aside>');
}
function insert_pbs() {
if (hot.get('readOnly') || readOnly.indexOf(BOARD) >= 0)
return;
if (THREAD ? $('aside').length : $ceiling.next().is('aside'))
return;
make_reply_box().appendTo('section');
if (!nashi.upload && BUMP)
$ceiling.after('<aside class="act"><a>New thread</a></aside>');
}
function get_nonces() {
var nonces;
if (window.localStorage) {
try {
nonces = JSON.parse(localStorage.postNonces);
}
catch (e) {}
}
else {
nonces = ComposerView.nonces;
}
return nonces || {};
}
function save_nonces(nonces) {
if (window.localStorage)
localStorage.postNonces = JSON.stringify(nonces);
else
ComposerView.nonces = nonces;
}
function today_id() {
return Math.floor(new Date().getTime() / (1000*60*60*24));
}
function create_nonce() {
var nonces = get_nonces();
var nonce = random_id();
nonces[nonce] = {
tab: TAB_ID,
day: today_id(),
};
save_nonces(nonces);
return nonce;
}
function expire_nonces() {
if (!window.localStorage)
return;
// we need a lock on postNonces really
var nonces = get_nonces();
// people messing with their system clock will mess with expiry, doh
var changed = false;
var yesterday = today_id() - 1;
for (var nonce in nonces) {
if (nonces[nonce].day >= yesterday)
continue;
delete nonces[nonce];
changed = true;
}
if (changed)
save_nonces(nonces);
}
setTimeout(expire_nonces, Math.floor(Math.random()*5000));
function destroy_nonce(nonce) {
var nonces = get_nonces();
if (!nonces[nonce])
return;
delete nonces[nonce];
save_nonces(nonces);
}
var Saku = Backbone.Model.extend({
idAttribute: 'num',
});
var ComposerView = Backbone.View.extend({
events: {
'input #subject': model_link('subject'),
'keydown #trans': 'on_key_down',
'click #done': 'finish_wrapped',
'click #toggle': 'on_toggle',
},
initialize: function (dest) {
this.listenTo(this.model, 'change', this.render_buttons);
this.listenTo(this.model, 'change:spoiler', this.render_spoiler_pane);
this.listenTo(this.model, 'change:floop', this.render_floop);
var attrs = this.model.attributes;
var op = attrs.op;
var post = op ? $('<article class="mine"/>') : this.options.thread;
this.setElement(post[0]);
this.buffer = $('<p/>');
this.line_buffer = $('<p/>');
this.meta = $('<header><a class="nope"><b/></a> <time/></header>');
this.$input = $('<textarea/>', {
name: 'body', id: 'trans', rows: '1', "class": 'themed',
});
this.submit = $('<input>', {
id: 'done', type: 'button', value: 'Done',
});
this.$subject = $('<input/>', {
id: 'subject',
'class': 'themed',
maxlength: config.SUBJECT_MAX_LENGTH,
width: '80%',
});
this.blockquote = $('<blockquote/>');
this.$sizer = $('<pre/>').appendTo('body');
this.pending = '';
this.line_count = 1;
this.char_count = 0;
this.imouto = new OneeSama(function (num) {
var $s = $('#' + num);
if (!$s.is('section'))
$s = $s.closest('section');
if ($s.is('section'))
this.callback(this.post_ref(num, extract_num($s)));
else
this.callback(safe('<a class="nope">&gt;&gt;' + num
+ '</a>'));
});
this.imouto.callback = inject;
this.imouto.op = THREAD;
this.imouto.state = initial_state();
this.imouto.buffer = this.buffer;
this.imouto.hook('spoilerTag', touchable_spoiler_tag);
oneeSama.trigger('imouto', this.imouto);
shift_replies(this.options.thread);
this.blockquote.append(this.buffer, this.line_buffer, this.$input);
post.append(this.meta, this.blockquote);
if (!op) {
post.append('<label for="subject">Subject: </label>',
this.$subject);
this.blockquote.hide();
}
this.uploadForm = this.make_upload_form();
post.append(this.uploadForm);
oneeSama.trigger('draft', post);
this.propagate_ident();
this.options.dest.replaceWith(post);
this.$input.input(this.on_input.bind(this, undefined));
this.$input.blur(this.on_blur.bind(this));
if (op) {
this.resize_input();
this.$input.focus();
}
else {
post.after('<hr/>');
this.$subject.focus();
}
$('aside').remove();
preload_panes();
this.model.set('floop', window.lastFloop);
},
propagate_ident: function () {
if (this.model.get('num'))
return;
var parsed = parse_name($name.val().trim());
var haveTrip = parsed[1] || parsed[2];
var meta = this.meta;
var $b = meta.find('b');
if (parsed[0])
$b.text(parsed[0] + ' ');
else
$b.text(haveTrip ? '' : ANON);
if (haveTrip)
$b.append($.parseHTML(' <code>!?</code>'));
oneeSama.trigger('fillMyName', $b);
var email = $email.val().trim();
if (is_noko(email))
email = '';
var tag = meta.children('a:first');
if (email)
tag.attr({href: 'mailto:' + email, target: '_blank',
'rel': 'nofollow noopener noreferrer', 'class': 'email'});
else
tag.removeAttr('href').removeAttr('target').attr('class',
'nope');
},
on_allocation: function (msg) {
var num = msg.num;
ownPosts[num] = true;
this.model.set({num: num});
this.flush_pending();
var header = $(flatten(oneeSama.atama(msg)).join(''));
this.meta.replaceWith(header);
this.meta = header;
var op = this.model.get('op');
if (op)
this.$el.addClass('editing');
else
spill_page();
this.$el.attr('id', num);
if (msg.image)
this.insert_uploaded(msg.image);
if (num == MILLION)
this.add_own_gravitas(msg);
if (this.uploadForm)
this.uploadForm.append(this.submit);
else
this.blockquote.after(this.submit);
if (!op) {
this.$subject.siblings('label').andSelf().remove();
this.blockquote.show();
this.resize_input();
this.$input.focus();
}
window.onbeforeunload = function () {
return "You have an unfinished post.";
};
},
on_image_alloc: function (msg) {
var attrs = this.model.attributes;
if (attrs.cancelled)
return;
if (!this.committed()) {
send([INSERT_POST, this.make_alloc_request(null, msg)]);
this.model.set({sentAllocRequest: true});
}
else {
send([INSERT_IMAGE, msg]);
}
},
entry_scroll_lock: function () {
/* NOPE */
if (lockTarget == PAGE_BOTTOM) {
/* Special keyup<->down case */
var height = $DOC.height();
if (height > lockKeyHeight)
window.scrollBy(0, height - lockKeyHeight + 1);
}
},
on_key_down: function (event) {
if (lockTarget == PAGE_BOTTOM) {
lockKeyHeight = $DOC.height();
_.defer($.proxy(this, 'entry_scroll_lock'));
}
switch (event.which) {
case 13:
event.preventDefault();
/* fall-through */
case 32:
var c = event.which == 13 ? '\n' : ' ';
// predict result
var input = this.$input[0];
var val = this.$input.val();
val = val.slice(0, input.selectionStart) + c +
val.slice(input.selectionEnd);
if (vapor >= 0 || c == '\n')
this.on_input(val);
break;
default:
handle_shortcut(event);
}
},
on_input: function (val) {
var $input = this.$input;
var start = $input[0].selectionStart, end = $input[0].selectionEnd;
if (val === undefined)
val = $input.val();
// dirty flag for writing back to the text box
var changed = false;
// character range we should not mangle
var ward = 0, ward_len = 0;
/* Turn YouTube links into proper refs */
while (true) {
var m = val.match(youtube_url_re);
if (!m)
break;
/* Substitute */
var t = m[4] || '';
t = this.find_time_arg(m[3]) || this.find_time_arg(m[1]) || t;
if (t[0] == '?')
t = '#' + t.substr(1);
var v = '>>>/watch?v=' + m[2] + t;
var old = m[0].length;
val = val.substr(0, m.index) + v + val.substr(m.index + old);
changed = true;
ward = m.index;
ward_len = v.length;
/* Compensate caret position */
if (m.index < start) {
var diff = old - v.length;
start -= diff;
end -= diff;
}
}
/* and SoundCloud links */
while (true) {
var m = val.match(soundcloud_url_re);
if (!m)
break;
var sc = '>>>/soundcloud/' + m[1];
var old = m[0].length;
val = val.substr(0, m.index) + sc + val.substr(m.index + old);
changed = true;
ward = m.index;
ward_len = sc.length;
if (m.index < start) {
var diff = old - sc.length;
start -= diff;
end -= diff;
}
}
/* and Twitter links */
while (true) {
var m = val.match(twitter_url_re);
if (!m)
break;
var tw = '>>>/@' + m[1] + '/' + m[2];
var old = m[0].length;
val = val.substr(0, m.index) + tw + val.substr(m.index + old);
changed = true;
ward = m.index;
ward_len = tw.length;
if (m.index < start) {
var diff = old - tw.length;
start -= diff;
end -= diff;
}
}
if (vapor < 0) {
if (!ward_len) {
// may have already converted from URL to >>ref, ward that too
var m = val.match(ref_re);
if (m) {
ward = m.index;
ward_len = m[0].length;
}
}
var vaped = this.vaporize(val, ward, ward+ward_len);
if (vaped != val) {
val = vaped;
changed = true;
}
}
if (changed)
$input.val(val);
if (this.$input.prop('placeholder'))
this.$input.prop('placeholder', '');
var len = val.length, lim = 0;
var nl = val.lastIndexOf('\n');
if (nl >= 0) {
var ok = val.substr(0, nl);
ok = this.word_filter(ok);
val = val.substr(nl+1);
$input.val(val);
if (this.model.get('sentAllocRequest') || /[^ ]/.test(ok))
this.commit(ok + '\n');
}
else if (vapor < 0 && !ward_len) {
// try to not break apart ##bigtext marker
if (len < 6 && /^/.test(val))
lim = 0;
else if (len > 3)
lim = len - 3;
if (lim > 0) {
// don't break surrogate pairs apart
// (javascript uses UCS-2... how terrible)
var u = val.charCodeAt(lim - 1);
if (0xd800 <= u && u < 0xdc00)
lim--;
// don't cut off variation selectors
// (hack; we need a grapheme library...)
u = val.charCodeAt(lim);
if (0xfe00 <= u && u < 0xfe10)
lim--;
}
}
else {
var rev = val.split('').reverse().join('');
var m = rev.match(/^(\s*\S+\s+\S+)\s+(?=\S)/);
if (m)
lim = len - m[1].length;
}
if (lim > 0) {
var destiny = val.substr(0, lim);
destiny = this.word_filter(destiny);
this.commit(destiny);
val = val.substr(lim);
start -= lim;
end -= lim;
$input.val(val);
$input[0].setSelectionRange(start, end);
}
$input.attr('maxlength', MAX_POST_CHARS - this.char_count);
this.resize_input(val);
},
vaporize: function (text, ward_start, ward_end) {
var aesthetic = '';
for (var i = 0; i < text.length; i++) {
var c = text.charCodeAt(i);
if (i >= ward_start && i < ward_end) {
}
else if (c > 32 && c < 127)
c += 0xfee0;
else if (c == 32)
c = 0x3000;
aesthetic += String.fromCharCode(c);
}
return aesthetic;
},
word_filter: function (words) {
return words;
},
add_ref: function (num) {
/* If a >>link exists, put this one on the next line */
var $input = this.$input;
var val = $input.val();
if (/^>>\d+$/.test(val)) {
$input.val(val + '\n');
this.on_input();
val = $input.val();
}
$input.val(val + '>>' + num);
$input[0].selectionStart = $input.val().length;
this.on_input();
$input.focus();
},
find_time_arg: function (params) {
if (!params || params.indexOf('t=') < 0)
return false;
params = params.split('&');
for (var i = 0; i < params.length; i++) {
var pair = '#' + params[i];
if (youtube_time_re.test(pair))
return pair;
}
return false;
},
resize_input: function (val) {
var $input = this.$input;
if (typeof val != 'string')
val = $input.val();
this.$sizer.text(val);
var left = $input.offset().left - this.$el.offset().left;
var size = this.$sizer.width() + INPUT_ROOM;
size = Math.max(size, inputMinSize - left);
$input.css('width', size + 'px');
},
show_placeholder: function () {
var ph = PLACEHOLDER;
if (this.char_count * 2 > MAX_POST_CHARS)
ph = ' ' + this.char_count + '/' + MAX_POST_CHARS;
var $input = this.$input;
if ($input.prop('placeholder') != ph) {
$input.prop('placeholder', ph);
// make sure placeholder shows up immediately
if (!$input.val()) {
$input.val(' ');
$input.val('');
}
}
},
on_blur: function () {
var self = this;
// minor delay to avoid flashing when finishing posts
setTimeout(function () {
if (!self.$input.is(':focus'))
self.show_placeholder();
}, 500);
},
dispatch: function (msg) {
var a = msg.arg;
switch (msg.t) {
case 'alloc':
this.on_image_alloc(a);
break;
case 'error':
this.upload_error(a);
break;
case 'status':
this.upload_status(a);
break;
}
},
upload_status: function (msg) {
if (this.model.get('cancelled'))
return;
this.model.set('uploadStatus', msg);
},
upload_error: function (msg) {
if (this.model.get('cancelled'))
return;
this.model.set({uploadStatus: msg, uploading: false});
if (this.uploadForm)
this.uploadForm.find('input[name=alloc]').remove();
},
upload_finished_fallback: function () {
// this is just a fallback message for when we can't tell
// if there was an error due to cross-origin restrictions
var a = this.model.attributes;
var stat = a.uploadStatus;
if (!a.cancelled && a.uploading && (!stat || stat == UPLOADING_MSG))
this.model.set('uploadStatus', 'Unknown result.');
},
insert_uploaded: function (info) {
var form = this.uploadForm, op = this.model.get('op');
insert_image(info, form.siblings('header'), !op);
this.$imageInput.siblings('strong').andSelf().add(this.$cancel
).remove();
form.find('#toggle').remove();
this.flush_pending();
this.model.set({uploading: false, uploaded: true,
sentAllocRequest: true});
/* Stop obnoxious wrap-around-image behaviour */
var $img = this.$el.find('img');
this.blockquote.css({
'margin-left': $img.css('margin-right'),
'padding-left': $img.width(),
});
this.resize_input();
},
make_alloc_request: function (text, image) {
var msg = {nonce: create_nonce()};
function opt(key, val) {
if (val)
msg[key] = val;
}
opt('name', $name.val().trim());
opt('email', $email.val().trim());
opt('subject', this.$subject.val().trim());
opt('frag', text);
opt('image', image);
opt('op', this.model.get('op'));
if (this.model.get('floop'))
msg.flavor = 'floop';
return msg;
},
commit: function (text) {
var lines;
if (text.indexOf('\n') >= 0) {
lines = text.split('\n');
this.line_count += lines.length - 1;
var breach = this.line_count - MAX_POST_LINES + 1;
if (breach > 0) {
for (var i = 0; i < breach; i++)
lines.pop();
text = lines.join('\n');
this.line_count = MAX_POST_LINES;
}
}
var left = MAX_POST_CHARS - this.char_count;
if (left < text.length)
text = text.substr(0, left);
if (!text)
return;
this.char_count += text.length;
/* Either get an allocation or send the committed text */
var attrs = this.model.attributes;
if (!this.committed()) {
send([INSERT_POST, this.make_alloc_request(text, null)]);
this.model.set({sentAllocRequest: true});
}
else if (attrs.num)
send(text);
else
this.pending += text;
/* Add it to the user's display */
var line_buffer = this.line_buffer;
if (lines) {
lines[0] = line_buffer.text() + lines[0];
line_buffer.text(lines.pop());
for (var i = 0; i < lines.length; i++)
this.imouto.fragment(lines[i] + '\n');
}
else {
line_buffer.append(document.createTextNode(text));
line_buffer[0].normalize();
}
},
committed: function () {
var a = this.model.attributes;
return !!(a.num || a.sentAllocRequest);
},
flush_pending: function () {
if (this.pending) {
send(this.pending);
this.pending = '';
}
},
cancel: function () {
if (this.model.get('uploading')) {
this.$iframe.remove();
this.$iframe = $('<iframe></iframe>', {
src: '', name: 'upload', id: 'hidden-upload',
}).appendTo('body');
this.upload_error('');
this.model.set({cancelled: true});
}
else
this.finish_wrapped();
},
finish: function () {
if (this.model.get('num')) {
this.flush_pending();
this.commit(this.word_filter(this.$input.val()));
this.$input.remove();
this.submit.remove();
if (this.uploadForm)
this.uploadForm.remove();
if (this.$iframe) {
this.$iframe.remove();
this.$iframe = null;
}
this.imouto.fragment(this.line_buffer.text());
this.buffer.replaceWith(this.buffer.contents());
this.line_buffer.remove();
this.blockquote.css({'margin-left': '', 'padding-left': ''});
send([FINISH_POST]);
this.preserve = true;
}
postSM.feed('done');
this.$el.removeClass('mine');
},
remove: function () {
if (!this.preserve) {
if (!this.model.get('op'))
this.$el.next('hr').remove();
this.$el.remove();
}
this.$sizer.remove();
if (this.$iframe) {
this.$iframe.remove();
this.$iframe = null;
}
this.stopListening();
window.onbeforeunload = null;
},
render_buttons: function () {
var attrs = this.model.attributes;
var allocWait = attrs.sentAllocRequest && !attrs.num;
var d = attrs.uploading || allocWait;
var self = this;
with_dom(function () {
/* Beware of undefined! */
self.submit.prop('disabled', !!d);
if (attrs.uploaded)
self.submit.css({'margin-left': '0'});
self.$cancel.prop('disabled', !!allocWait);
self.$cancel.toggle(!!(!attrs.num || attrs.uploading));
self.$imageInput.prop('disabled', !!attrs.uploading);
self.$uploadStatus.text(attrs.uploadStatus);
var auto = options.get('autocomplete') ? 'on' : 'off';
self.$input.attr({autocapitalize: auto, autocomplete: auto,
autocorrect: auto, spellcheck: auto == 'on'});
});
},
prep_upload: function () {
this.model.set('uploadStatus', UPLOADING_MSG);
this.$input.focus();
var attrs = this.model.attributes;
return {spoiler: attrs.spoiler, op: attrs.op || 0};
},
notify_uploading: function () {
this.model.set({uploading: true, cancelled: false});
this.$input.focus();
},
make_upload_form: function () {
var form = $('<form method="post" enctype="multipart/form-data" '
+ 'target="upload"></form>');
this.$cancel = $('<input>', {
type: 'button', value: 'Cancel',
click: $.proxy(this, 'cancel'),
});
var opts = {
type: 'file', id: 'image', name: 'image',
change: $.proxy(this, 'on_image_chosen'),
};
if (!imagerConfig.VIDEO)
opts.accept = 'image/*';
this.$imageInput = $('<input>', opts);
this.$toggle = $('<input>', {
type: 'button', id: 'toggle',
});
this.$uploadStatus = $('<strong/>');
form.append(this.$cancel, this.$imageInput, this.$toggle, ' ',
this.$uploadStatus);
this.$iframe = $('<iframe></iframe>', {
src: '', name: 'upload', id: 'hidden-upload',
}).appendTo('body');
if (nashi.upload) {
this.$imageInput.hide();
this.$toggle.hide();
}
this.model.set({spoiler: 0, nextSpoiler: -1});
return form;
},
on_image_chosen: function () {
if (this.model.get('uploading') || this.model.get('uploaded'))
return;
if (!this.$imageInput.val()) {
this.model.set('uploadStatus', '');
return;
}
var extra = this.prep_upload();
for (var k in extra)
$('<input type=hidden>').attr('name', k).val(extra[k]
).appendTo(this.uploadForm);
this.uploadForm.prop('action', image_upload_url());
this.uploadForm.submit();
this.$iframe.load(function (event) {
if (!postForm)
return;
var doc = this.contentWindow || this.contentDocument;
if (!doc)
return;
try {
var error = $(doc.document || doc).text();
// if it's a real response, it'll postMessage to us,
// so we don't have to do anything.
if (/legitimate imager response/.test(error))
return;
// sanity check for weird browser responses
if (error.length < 5 || error.length > 100)
error = 'Unknown upload error.';
postForm.upload_error(error);
}
catch (e) {
// likely cross-origin restriction
// wait before erroring in case the message shows up
setTimeout(function () {
postForm.upload_finished_fallback();
}, 500);
}
});
this.notify_uploading();
},
on_toggle: function (event) {
var attrs = this.model.attributes;
if (!attrs.uploading && !attrs.uploaded) {
event.preventDefault();
event.stopImmediatePropagation();
if (attrs.spoiler) {
this.model.set({spoiler: 0});
return;
}
var pick = pick_spoiler(attrs.nextSpoiler);
this.model.set({spoiler: pick.index, nextSpoiler: pick.next});
}
},
render_spoiler_pane: function (model, sp) {
var img = sp ? spoiler_pane_url(sp) : mediaURL + 'css/ui/pane.png';
this.$toggle.css('background-image', 'url("' + img + '")');
},
render_floop: function (model, floop) {
this.$el.toggleClass('floop', floop);
},
});
menuHandlers.Flip = function () {
var floop = !window.lastFloop;
window.lastFloop = floop;
if (floop)
$('<style/>', {
id: 'floop-aside-right',
text: 'section.full.floop aside { margin: -26px 0 2px auto; }',
}).appendTo('head');
else
$('#floop-aside-right').remove();
if (postForm && !postForm.committed())
postForm.model.set('floop', floop);
};
menuHandlers.Vapor = function () {
vapor = -1;
flash_bg('#f98aa5');
if (postForm && /^\s*V+$/.test(postForm.$input.val()))
postForm.$input.val('');
};
oneeSama.hook('menuOptions', function (info) {
if (!info.model && info.mine && !postForm.committed()) {
var $sec = info.$button.closest('section.floop');
if ($sec.length || !THREAD) {
var i = info.options.indexOf('Focus');
if (i >= 0)
info.options.splice(i, 1);
info.options.unshift('Flip');
}
}
if (info.mine) {
var active = vapor < 0 || wombo < 0;
info.options.push(active ? 'Eject' : 'Vapor');
}
});
function image_upload_url() {
var url = imagerConfig.UPLOAD_URL || '../upload/';
return url + '?id=' + CONN_ID
}
dispatcher[IMAGE_STATUS] = function (msg) {
if (postForm)
postForm.dispatch(msg[0]);
};
window.addEventListener('message', function (event) {
var uploadOrigin = imagerConfig.UPLOAD_ORIGIN;
if (uploadOrigin && uploadOrigin != '*') {
if (event.origin && event.origin !== uploadOrigin)
return;
}
var msg = event.data;
if (msg == 'OK')
return;
else if (postForm)
postForm.upload_error(msg);
}, false);
function spoiler_pane_url(sp) {
return mediaURL + 'kana/spoil' + sp + '.png';
}
function preload_panes() {
var all = spoilerImages.normal.concat(spoilerImages.trans);
for (var i = 0; i < all.length; i++)
new Image().src = spoiler_pane_url(all[i]);
}
(function () {
var CV = ComposerView.prototype;
CV.finish_wrapped = _.wrap(CV.finish, with_dom);
})();