commit
39aa9d3cb5
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,5 +1,4 @@
|
||||
builds/
|
||||
node_modules/
|
||||
tmp-crx/
|
||||
tmp-userjs/
|
||||
tmp-userscript/
|
||||
|
||||
@ -11,9 +11,8 @@ Reporting bugs:
|
||||
4. Your exported settings. If your settings contains sensible information (e.g. personas), edit the text file manually.
|
||||
|
||||
Open your console with:
|
||||
- `Ctrl + Shift + J` on Chrome.
|
||||
- `Ctrl + Shift + J` on Chrome and Opera.
|
||||
- `Ctrl + Shift + K` on Firefox.
|
||||
- `Ctrl + Shift + O` on Opera.
|
||||
|
||||
Respect these guidelines:
|
||||
- Describe the issue clearly, put some effort into it. A one-liner isn't a good enough description.
|
||||
|
||||
19
Gruntfile.js
19
Gruntfile.js
@ -56,15 +56,6 @@ module.exports = function(grunt) {
|
||||
]
|
||||
}
|
||||
},
|
||||
userjs: {
|
||||
options: concatOptions,
|
||||
src: [
|
||||
'src/Meta/metadata.js',
|
||||
'src/Meta/banner.js',
|
||||
'tmp-<%= pkg.type %>/script.js'
|
||||
],
|
||||
dest: 'builds/<%= pkg.name %>.js'
|
||||
},
|
||||
userscript: {
|
||||
options: concatOptions,
|
||||
files: {
|
||||
@ -102,7 +93,7 @@ module.exports = function(grunt) {
|
||||
}
|
||||
},
|
||||
concurrent: {
|
||||
build: ['build-crx', 'build-userjs', 'build-userscript']
|
||||
build: ['build-crx', 'build-userscript']
|
||||
},
|
||||
bump: {
|
||||
options: {
|
||||
@ -161,7 +152,6 @@ module.exports = function(grunt) {
|
||||
clean: {
|
||||
builds: 'builds',
|
||||
tmpcrx: 'tmp-crx',
|
||||
tmpuserjs: 'tmp-userjs',
|
||||
tmpuserscript: 'tmp-userscript'
|
||||
}
|
||||
});
|
||||
@ -193,13 +183,6 @@ module.exports = function(grunt) {
|
||||
'copy:crx',
|
||||
'clean:tmpcrx'
|
||||
]);
|
||||
grunt.registerTask('build-userjs', [
|
||||
'set-build:userjs',
|
||||
'concat:coffee',
|
||||
'coffee:script',
|
||||
'concat:userjs',
|
||||
'clean:tmpuserjs'
|
||||
]);
|
||||
grunt.registerTask('build-userscript', [
|
||||
'set-build:userscript',
|
||||
'concat:coffee',
|
||||
|
||||
@ -278,10 +278,8 @@ a[href="javascript:;"] {
|
||||
box-sizing: border-box;
|
||||
box-shadow: 0 0 15px rgba(0, 0, 0, .15);
|
||||
height: 600px;
|
||||
min-height: 0;
|
||||
max-height: 100%;
|
||||
width: 900px;
|
||||
min-width: 0;
|
||||
max-width: 100%;
|
||||
margin: auto;
|
||||
padding: 3px;
|
||||
@ -468,7 +466,8 @@ a.hide-announcement {
|
||||
.deadlink {
|
||||
text-decoration: none !important;
|
||||
}
|
||||
.backlink.deadlink:not(.forwardlink), .quotelink.deadlink:not(.forwardlink) {
|
||||
.backlink.deadlink:not(.forwardlink),
|
||||
.quotelink.deadlink:not(.forwardlink) {
|
||||
text-decoration: underline !important;
|
||||
}
|
||||
.inlined {
|
||||
@ -508,8 +507,6 @@ a.hide-announcement {
|
||||
padding: 2px 2px 5px;
|
||||
}
|
||||
#qp img {
|
||||
max-height: 300px;
|
||||
max-width: 500px;
|
||||
max-height: 80vh;
|
||||
max-width: 50vw;
|
||||
}
|
||||
@ -541,8 +538,7 @@ a.hide-announcement {
|
||||
:root.fit-width .full-image {
|
||||
max-width: 100%;
|
||||
}
|
||||
:root.gecko.fit-width .full-image,
|
||||
:root.presto.fit-width .full-image {
|
||||
:root.gecko.fit-width .full-image {
|
||||
width: 100%;
|
||||
}
|
||||
#ihover {
|
||||
@ -616,9 +612,6 @@ a.hide-announcement {
|
||||
color: #000;
|
||||
background-color: #F7F7F7;
|
||||
}
|
||||
.presto #qr select {
|
||||
height: 1em;
|
||||
}
|
||||
#qr .close {
|
||||
padding: 0 3px;
|
||||
}
|
||||
@ -649,13 +642,15 @@ a.hide-announcement {
|
||||
outline: none;
|
||||
width: 30px;
|
||||
}
|
||||
#dump-button:hover, #dump-button:focus {
|
||||
#dump-button:hover,
|
||||
#dump-button:focus {
|
||||
background: linear-gradient(#FFF, #DDD);
|
||||
}
|
||||
#dump-button:active, .dump #dump-button:not(:hover):not(:focus) {
|
||||
#dump-button:active,
|
||||
.dump #dump-button:not(:hover):not(:focus) {
|
||||
background: linear-gradient(#CCC, #DDD);
|
||||
}
|
||||
.gecko #dump-button {
|
||||
:root.gecko #dump-button {
|
||||
padding: 0;
|
||||
}
|
||||
#qr:not(.dump) #dump-list-container {
|
||||
@ -670,7 +665,10 @@ a.hide-announcement {
|
||||
}
|
||||
#dump-list {
|
||||
counter-reset: qrpreviews;
|
||||
top: 0; right: 0; bottom: 0; left: 0;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
white-space: nowrap;
|
||||
@ -696,8 +694,10 @@ a.hide-announcement {
|
||||
box-sizing: border-box;
|
||||
cursor: move;
|
||||
display: inline-block;
|
||||
height: 92px; width: 92px;
|
||||
margin: 4px; padding: 2px;
|
||||
height: 92px;
|
||||
width: 92px;
|
||||
margin: 4px;
|
||||
padding: 2px;
|
||||
opacity: .6;
|
||||
outline: none;
|
||||
overflow: hidden;
|
||||
@ -707,7 +707,8 @@ a.hide-announcement {
|
||||
vertical-align: top;
|
||||
white-space: pre;
|
||||
}
|
||||
.qr-preview:hover, .qr-preview:focus {
|
||||
.qr-preview:hover,
|
||||
.qr-preview:focus {
|
||||
opacity: .9;
|
||||
color: #FFF !important;
|
||||
}
|
||||
@ -720,7 +721,8 @@ a.hide-announcement {
|
||||
font-weight: 700;
|
||||
text-shadow: 0 0 3px #000, 0 0 5px #000;
|
||||
position: absolute;
|
||||
top: 3px; right: 3px;
|
||||
top: 3px;
|
||||
right: 3px;
|
||||
}
|
||||
.qr-preview.drag {
|
||||
border-color: red;
|
||||
@ -740,7 +742,9 @@ a.hide-announcement {
|
||||
}
|
||||
.qr-preview > label {
|
||||
background: rgba(0, 0, 0, .5);
|
||||
right: 0; bottom: 0; left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
position: absolute;
|
||||
text-align: center;
|
||||
}
|
||||
@ -756,7 +760,8 @@ a.hide-announcement {
|
||||
line-height: 1;
|
||||
text-align: center;
|
||||
position: absolute;
|
||||
right: 0; bottom: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 1;
|
||||
}
|
||||
#qr textarea {
|
||||
@ -822,7 +827,10 @@ a.hide-announcement {
|
||||
}
|
||||
#qr-filename {
|
||||
position: absolute;
|
||||
top: 0; right: 0; bottom: 0; left: 0;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
@ -838,10 +846,6 @@ a.hide-announcement {
|
||||
-webkit-order: 1;
|
||||
order: 1;
|
||||
}
|
||||
#qr input[type='file'] {
|
||||
position: absolute;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
/* Menu */
|
||||
.menu-button {
|
||||
|
||||
@ -31,7 +31,7 @@
|
||||
<a id="qr-filerm" href="javascript:;" title="Remove file">×</a>
|
||||
<input type="checkbox" id="qr-file-spoiler" title="Spoiler image">
|
||||
</div>
|
||||
<input type="file" multiple>
|
||||
<input type="file" multiple hidden>
|
||||
</form>
|
||||
<datalist id="list-name"></datalist>
|
||||
<datalist id="list-email"></datalist>
|
||||
|
||||
68
lib/$.coffee
68
lib/$.coffee
@ -208,26 +208,19 @@ $.bytesToString = (size) ->
|
||||
# Round to an integer otherwise.
|
||||
Math.round size
|
||||
"#{size} #{['B', 'KB', 'MB', 'GB'][unit]}"
|
||||
$.item = (key, val) ->
|
||||
item = {}
|
||||
item[key] = val
|
||||
item
|
||||
$.syncing = {}
|
||||
$.sync = do ->
|
||||
<% if (type === 'crx') { %>
|
||||
$.sync = do ->
|
||||
chrome.storage.onChanged.addListener (changes) ->
|
||||
for key of changes
|
||||
if cb = $.syncing[key]
|
||||
cb changes[key].newValue
|
||||
return
|
||||
(key, cb) -> $.syncing[key] = cb
|
||||
<% } else { %>
|
||||
$.on window, 'storage', (e) ->
|
||||
if cb = $.syncing[e.key]
|
||||
cb JSON.parse e.newValue
|
||||
(key, cb) -> $.syncing[g.NAMESPACE + key] = cb
|
||||
<% } %>
|
||||
$.item = (key, val) ->
|
||||
item = {}
|
||||
item[key] = val
|
||||
item
|
||||
<% if (type === 'crx') { %>
|
||||
$.localKeys = [
|
||||
# filters
|
||||
'name',
|
||||
@ -300,52 +293,13 @@ $.set = do ->
|
||||
else
|
||||
$.extend items, key
|
||||
set()
|
||||
<% } else if (type === 'userjs') { %>
|
||||
do ->
|
||||
# http://www.opera.com/docs/userjs/specs/#scriptstorage
|
||||
# http://www.opera.com/docs/userjs/using/#securepages
|
||||
# The scriptStorage object is available only during
|
||||
# the main User JavaScript thread, being therefore
|
||||
# accessible only in the main body of the user script.
|
||||
# To access the storage object later, keep a reference
|
||||
# to the object.
|
||||
{scriptStorage} = opera
|
||||
$.delete = (keys) ->
|
||||
unless keys instanceof Array
|
||||
keys = [keys]
|
||||
for key in keys
|
||||
key = g.NAMESPACE + key
|
||||
localStorage.removeItem key
|
||||
delete scriptStorage[key]
|
||||
return
|
||||
$.get = (key, val, cb) ->
|
||||
if typeof cb is 'function'
|
||||
items = $.item key, val
|
||||
else
|
||||
items = key
|
||||
cb = val
|
||||
$.queueTask ->
|
||||
for key of items
|
||||
if val = scriptStorage[g.NAMESPACE + key]
|
||||
items[key] = JSON.parse val
|
||||
cb items
|
||||
$.set = do ->
|
||||
set = (key, val) ->
|
||||
key = g.NAMESPACE + key
|
||||
val = JSON.stringify val
|
||||
if key of $.syncing
|
||||
# for `storage` events
|
||||
localStorage.setItem key, val
|
||||
scriptStorage[key] = val
|
||||
(keys, val) ->
|
||||
if typeof keys is 'string'
|
||||
set keys, val
|
||||
return
|
||||
for key, val of keys
|
||||
set key, val
|
||||
return
|
||||
<% } else { %>
|
||||
# http://wiki.greasespot.net/Main_Page
|
||||
# http://wiki.greasespot.net/Main_Page
|
||||
$.sync = do ->
|
||||
$.on window, 'storage', (e) ->
|
||||
if cb = $.syncing[e.key]
|
||||
cb JSON.parse e.newValue
|
||||
(key, cb) -> $.syncing[g.NAMESPACE + key] = cb
|
||||
$.delete = (keys) ->
|
||||
unless keys instanceof Array
|
||||
keys = [keys]
|
||||
|
||||
@ -1,9 +1,3 @@
|
||||
<% if (type === 'userjs') { %>
|
||||
# Opera doesn't support the @match metadata key,
|
||||
# return 4chan X here if we're not on 4chan.
|
||||
return unless /^(boards|images|sys)\.4chan\.org$/.test location.hostname
|
||||
<% } %>
|
||||
|
||||
Conf = {}
|
||||
c = console
|
||||
d = document
|
||||
|
||||
@ -127,8 +127,6 @@ Main =
|
||||
<% if (type === 'crx') { %>
|
||||
$.addClass doc, 'webkit'
|
||||
$.addClass doc, 'blink'
|
||||
<% } else if (type === 'userjs') { %>
|
||||
$.addClass doc, 'presto'
|
||||
<% } else { %>
|
||||
$.addClass doc, 'gecko'
|
||||
<% } %>
|
||||
@ -151,13 +149,9 @@ Main =
|
||||
$.addClass doc, style
|
||||
setStyle()
|
||||
return unless mainStyleSheet
|
||||
if window.MutationObserver
|
||||
observer = new MutationObserver setStyle
|
||||
observer.observe mainStyleSheet,
|
||||
attributes: true
|
||||
attributeFilter: ['href']
|
||||
else
|
||||
$.on mainStyleSheet, 'DOMAttrModified', setStyle
|
||||
new MutationObserver(setStyle).observe mainStyleSheet,
|
||||
attributes: true
|
||||
attributeFilter: ['href']
|
||||
|
||||
initReady: ->
|
||||
if d.title is '4chan - 404 Not Found'
|
||||
@ -252,12 +246,10 @@ Main =
|
||||
|
||||
checkUpdate: ->
|
||||
return unless Conf['Check for Updates'] and Main.isThisPageLegit()
|
||||
# Check for updates after:
|
||||
# - 6 hours since the last update on Opera because it lacks auto-updating.
|
||||
# - 7 days since the last update on Chrome/Firefox.
|
||||
# Check for updates after 7 days since the last update.
|
||||
# After that, check for updates every day if we still haven't updated.
|
||||
now = Date.now()
|
||||
freq = <% if (type === 'userjs') { %>6 * $.HOUR<% } else { %>7 * $.DAY<% } %>
|
||||
freq = 7 * $.DAY
|
||||
items =
|
||||
lastupdate: 0
|
||||
lastchecked: 0
|
||||
|
||||
@ -52,19 +52,6 @@ ImageExpand =
|
||||
return
|
||||
setFitness: ->
|
||||
(if @checked then $.addClass else $.rmClass) doc, @name.toLowerCase().replace /\s+/g, '-'
|
||||
<% if (type === 'userjs') { %>
|
||||
# XXX Opera doesn't support CSS vh.
|
||||
return unless @name is 'Fit height'
|
||||
if @checked
|
||||
$.on window, 'resize', ImageExpand.resize
|
||||
unless ImageExpand.style
|
||||
ImageExpand.style = $.addStyle null
|
||||
ImageExpand.resize()
|
||||
else
|
||||
$.off window, 'resize', ImageExpand.resize
|
||||
resize: ->
|
||||
ImageExpand.style.textContent = ":root.fit-height .full-image {max-height:#{doc.clientHeight}px}"
|
||||
<% } %>
|
||||
|
||||
toggle: (post) ->
|
||||
{thumb} = post.file
|
||||
|
||||
@ -4,12 +4,10 @@ Sauce =
|
||||
|
||||
links = []
|
||||
for link in Conf['sauces'].split '\n'
|
||||
continue if link[0] is '#'
|
||||
try
|
||||
links.push @createSauceLink link.trim()
|
||||
links.push @createSauceLink link.trim() if link[0] isnt '#'
|
||||
catch err
|
||||
# Don't add random text plz.
|
||||
continue
|
||||
return unless links.length
|
||||
@links = links
|
||||
@link = $.el 'a', target: '_blank'
|
||||
|
||||
@ -15,7 +15,8 @@
|
||||
"run_at": "document_start"
|
||||
}],
|
||||
"homepage_url": "<%= meta.page %>",
|
||||
"minimum_chrome_version": "26",
|
||||
"minimum_chrome_version": "27",
|
||||
"minimum_opera_version": "15",
|
||||
"permissions": [
|
||||
"storage"
|
||||
]
|
||||
|
||||
@ -114,8 +114,7 @@ ThreadUpdater =
|
||||
By sending the `If-Modified-Since` header we get a proper status code, and no response.
|
||||
This saves bandwidth for both the user and the servers and avoid unnecessary computation.
|
||||
###
|
||||
# XXX 304 -> 0 in Opera
|
||||
[text, klass] = if req.status in [0, 304]
|
||||
[text, klass] = if req.status is 304
|
||||
[null, null]
|
||||
else
|
||||
["#{req.statusText} (#{req.status})", 'warning']
|
||||
|
||||
@ -182,9 +182,7 @@ Unread =
|
||||
else
|
||||
Favicon.default
|
||||
|
||||
<% if (type !== 'crx') { %>
|
||||
<% if (type === 'userscript') { %>
|
||||
# `favicon.href = href` doesn't work on Firefox.
|
||||
# `favicon.href = href` isn't enough on Opera.
|
||||
# Opera won't always update the favicon if the href didn't change.
|
||||
$.add d.head, Favicon.el
|
||||
<% } %>
|
||||
|
||||
@ -330,7 +330,6 @@ QR =
|
||||
post = Get.postFromNode @
|
||||
text = ">>#{post}\n"
|
||||
if (s = sel.toString().trim()) and post is Get.postFromNode sel.anchorNode
|
||||
# XXX Opera doesn't retain `\n`s?
|
||||
s = s.replace /\n/g, '\n>'
|
||||
text += ">#{s}\n"
|
||||
|
||||
@ -501,7 +500,6 @@ QR =
|
||||
else if @ is QR.selected
|
||||
(QR.posts[index-1] or QR.posts[index+1]).select()
|
||||
QR.posts.splice index, 1
|
||||
return unless window.URL
|
||||
URL.revokeObjectURL @URL
|
||||
lock: (lock=true) ->
|
||||
@isLocked = lock
|
||||
@ -558,25 +556,14 @@ QR =
|
||||
@filename = "#{file.name} (#{$.bytesToString file.size})"
|
||||
@nodes.el.title = @filename
|
||||
@nodes.label.hidden = false if QR.spoiler
|
||||
URL.revokeObjectURL @URL if window.URL
|
||||
URL.revokeObjectURL @URL
|
||||
@showFileData()
|
||||
unless /^image/.test file.type
|
||||
@nodes.el.style.backgroundImage = null
|
||||
return
|
||||
@setThumbnail()
|
||||
setThumbnail: (fileURL) ->
|
||||
# XXX Opera does not support blob URL
|
||||
setThumbnail: ->
|
||||
# Create a redimensioned thumbnail.
|
||||
unless window.URL
|
||||
unless fileURL
|
||||
reader = new FileReader()
|
||||
reader.onload = (e) =>
|
||||
@setThumbnail e.target.result
|
||||
reader.readAsDataURL @file
|
||||
return
|
||||
else
|
||||
fileURL = URL.createObjectURL @file
|
||||
|
||||
img = $.el 'img'
|
||||
|
||||
img.onload = =>
|
||||
@ -588,7 +575,7 @@ QR =
|
||||
s *= 3 if @file.type is 'image/gif' # let them animate
|
||||
{height, width} = img
|
||||
if height < s or width < s
|
||||
@URL = fileURL if window.URL
|
||||
@URL = fileURL
|
||||
@nodes.el.style.backgroundImage = "url(#{@URL})"
|
||||
return
|
||||
if height <= width
|
||||
@ -601,10 +588,6 @@ QR =
|
||||
cv.height = img.height = height
|
||||
cv.width = img.width = width
|
||||
cv.getContext('2d').drawImage img, 0, 0, width, height
|
||||
unless window.URL
|
||||
@nodes.el.style.backgroundImage = "url(#{cv.toDataURL()})"
|
||||
delete @URL
|
||||
return
|
||||
URL.revokeObjectURL fileURL
|
||||
applyBlob = (blob) =>
|
||||
@URL = URL.createObjectURL blob
|
||||
@ -622,6 +605,7 @@ QR =
|
||||
|
||||
applyBlob new Blob [ui8a], type: 'image/png'
|
||||
|
||||
fileURL = URL.createObjectURL @file
|
||||
img.src = fileURL
|
||||
rmFile: ->
|
||||
delete @file
|
||||
@ -630,7 +614,6 @@ QR =
|
||||
@nodes.el.style.backgroundImage = null
|
||||
@nodes.label.hidden = true if QR.spoiler
|
||||
@showFileData()
|
||||
return unless window.URL
|
||||
URL.revokeObjectURL @URL
|
||||
showFileData: ->
|
||||
if @file
|
||||
@ -652,22 +635,17 @@ QR =
|
||||
QR.nodes.com.value = @com
|
||||
@nodes.span.textContent = @com
|
||||
reader.readAsText file
|
||||
dragStart: ->
|
||||
$.addClass @, 'drag'
|
||||
dragEnd: ->
|
||||
$.rmClass @, 'drag'
|
||||
dragEnter: ->
|
||||
$.addClass @, 'over'
|
||||
dragLeave: ->
|
||||
$.rmClass @, 'over'
|
||||
dragStart: -> $.addClass @, 'drag'
|
||||
dragEnd: -> $.rmClass @, 'drag'
|
||||
dragEnter: -> $.addClass @, 'over'
|
||||
dragLeave: -> $.rmClass @, 'over'
|
||||
dragOver: (e) ->
|
||||
e.preventDefault()
|
||||
e.dataTransfer.dropEffect = 'move'
|
||||
drop: ->
|
||||
el = $ '.drag', @parentNode
|
||||
$.rmClass el, 'drag' # Opera doesn't fire dragEnd if we drop it on something else
|
||||
$.rmClass @, 'over'
|
||||
$.rmClass @, 'over'
|
||||
return unless @draggable
|
||||
el = $ '.drag', @parentNode
|
||||
index = (el) -> [el.parentNode.children...].indexOf el
|
||||
oldIndex = index el
|
||||
newIndex = index @
|
||||
@ -700,12 +678,8 @@ QR =
|
||||
img: imgContainer.firstChild
|
||||
input: input
|
||||
|
||||
if window.MutationObserver
|
||||
observer = new MutationObserver @load.bind @
|
||||
observer.observe @nodes.challenge,
|
||||
childList: true
|
||||
else
|
||||
$.on @nodes.challenge, 'DOMNodeInserted', @load.bind @
|
||||
new MutationObserver(@load.bind @).observe @nodes.challenge,
|
||||
childList: true
|
||||
|
||||
$.on imgContainer, 'click', @reload.bind @
|
||||
$.on input, 'keydown', @keydown.bind @
|
||||
@ -836,10 +810,6 @@ QR =
|
||||
# Add empty mimeType to avoid errors with URLs selected in Window's file dialog.
|
||||
QR.mimeTypes.push ''
|
||||
nodes.fileInput.max = $('input[name=MAX_FILE_SIZE]').value
|
||||
<% if (type !== 'userjs') { %>
|
||||
# Opera's accept attribute is fucked up
|
||||
nodes.fileInput.accept = "text/*, #{mimeTypes}"
|
||||
<% } %>
|
||||
|
||||
QR.spoiler = !!$ 'input[name=spoiler]'
|
||||
nodes.spoiler.hidden = !QR.spoiler
|
||||
@ -1018,11 +988,6 @@ QR =
|
||||
QR.status()
|
||||
|
||||
response: ->
|
||||
<% if (type === 'userjs') { %>
|
||||
# The upload.onload callback is not called
|
||||
# or at least not in time with Opera.
|
||||
QR.req.upload.onload()
|
||||
<% } %>
|
||||
{req} = QR
|
||||
delete QR.req
|
||||
|
||||
|
||||
@ -28,21 +28,6 @@ QuotePreview =
|
||||
cb: QuotePreview.mouseout
|
||||
asapTest: -> qp.firstElementChild
|
||||
|
||||
<% if (type === 'userjs') { %>
|
||||
# XXX Opera workaround for "no mouseout fired" bug.
|
||||
# Remove it once Opera uses Blink.
|
||||
root = @
|
||||
workaround = (e) ->
|
||||
if @ is root
|
||||
e.stopPropagation()
|
||||
return
|
||||
$.event 'mouseout', null, root
|
||||
$.off d, 'mousemove', workaround
|
||||
$.off root, 'mousemove', workaround
|
||||
$.on d, 'mousemove', workaround
|
||||
$.on root, 'mousemove', workaround
|
||||
<% } %>
|
||||
|
||||
return unless origin = g.posts["#{boardID}.#{postID}"]
|
||||
|
||||
if Conf['Quote Highlighting']
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user