-
Notifications
You must be signed in to change notification settings - Fork 3
/
coffee_filter.coffee
340 lines (283 loc) · 10.9 KB
/
coffee_filter.coffee
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
# = Coffee Filter =
#
# _______________________
# \ ///
# \ ///
# \ ///
# \ ///
# \ ///
# \////////////
#
#
# A collection of coffeescript helpers I've used across projects
# By Michael P. Geraci, 2012
# mgeraci.com
#
# MIT License
# show a tag on hover (e.g., a username when hovering over an avatar)
# add the desired text as a data attribute on the element, e.g.:
# <img src='/images/avatar.png' data-hover-tag='michael geraci'>
# optionally, add data-hover-tag-top and/or data-hover-tag-left
# to override the default offset
window.hover_tags = ->
# position and show the tag on mouseover of element
$('body').on 'mouseover', '[data-hover-tag]', (e)->
target = $(this).closest('[data-hover-tag]')
# Clean out hover tag title so browser hover doesn't show
old_title = target.attr('title')
target.attr('title', '')
name = unescape(target.data('hover-tag')) # unescape in case you use line breaks
# use title attr if no hover-tag defined
if name == ''
name = old_title
target.data('hover-tag', old_title)
position = target.offset()
# append tag and styles if it doesn't exist
if $('.hover_tag').length == 0
# styles for the tag wrapper
tag_styles =
position: 'absolute'
display: 'none'
padding: '4px 6px 2px'
background: 'black'
background: 'rgba(0,0,0,0.8)'
color: 'white'
fontSize: '13px'
textTransform: 'lowercase'
zIndex: 800
textShadow: 'none'
# styles for the triangle pointing towards the element
pointer_styles =
position: 'absolute'
top: '-6px'
left: '50%'
marginLeft: '-3px'
width: '8px'
height: '6px'
background: 'url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAGCAYAAAD+Bd/7AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAElJREFUeNpiYEAFxVCMFRgD8RkoNoYJMkJpXiBeBsSSUP5zII4C4s/MUIFcILZEMg2kgR2IjzNCjZuJw9p0kILNSEajg+cAAQYA1jkKUj6WaH4AAAAASUVORK5CYII=) no-repeat'
# add the html structure
$('body').append "
<div class='hover_tag'>
<div class='text'></div>
<div class='tag_pointer'></div>
</div>"
# add the styles
$('.hover_tag').css tag_styles
$('.hover_tag .tag_pointer').css pointer_styles
tag = $('.hover_tag')
# set the text
tag.find('.text').html(name)
top_offset = 8
left_offset = 0
# look into data attributes to override the default offset
if target.data('hover-tag-top')
top_offset = target.data('hover-tag-top')
if target.data('hover-tag-left')
left_offset = target.data('hover-tag-left')
# position and show the tag
tag.css(
top: position.top + target.outerHeight() + top_offset
left: position.left - tag.outerWidth() / 2 + target.width() / 2 + left_offset
).show()
# hide the tag on mouseout of element
$('body').on 'mouseout', '[data-hover-tag]', (e)->
$('.hover_tag').hide()
# manually handles input placeholder text for browsers that don't support the html5 spec
# requires modernizr
# http://webdesignerwall.com/tutorials/cross-browser-html5-placeholder-text
window.set_placeholder_text = ->
return unless window.Modernizr?
unless window.Modernizr.input.placeholder
$('[placeholder]').focus(->
input = $(this)
if input.val() == input.attr('placeholder')
input.val('')
input.removeClass('placeholder')
).blur(->
input = $(this)
if input.val() == '' || input.val() == input.attr('placeholder')
input.addClass('placeholder')
input.val(input.attr('placeholder'))
).blur()
$('[placeholder]').parents('form').submit ->
$(this).find('[placeholder]').each ->
input = $(this)
if input.val() == input.attr('placeholder')
input.val('')
# call on a textarea to have it grow when you type in it.
# 2nd arg. is an optional function to run on change
# 3rd arg. tells the function to focus and start watching now
#
# requires the class ie8 on <html> to keep this function from running.
# based on code from http://tore.darell.no/posts/auto_expanding_textarea
jQuery.fn.autoexpand = (on_change = false, force = false)->
# don't run on ie8
unless $('html').hasClass('ie8')
elements = $(this)
# get the textarea and its value
elements.each (i, textarea)=>
textarea = $(textarea)
interval = null
oldValue = textarea.val()
oldLines = textarea.attr('rows') || 0
# The observer function for auto textarea resizing
observer = =>
# save the value
newValue = textarea.val()
# if the value has changed
if newValue != oldValue
char_per_line = textarea.width() / 6
current_length = newValue.length
lines = Math.floor(current_length / char_per_line)
returns = newValue.match(/\n/g)?.length || 0 # number of user-entered new lines
# Set the "rows" attribute to the number of lines + 2
textarea.attr('rows', lines + returns + 2)
oldValue = newValue
# if we've changed the textarea's size
if oldLines != textarea.attr('rows')
oldLines = textarea.attr('rows')
# run on change function if it exists
on_change() if on_change
# When the user focuses the textarea, create the observer interval
textarea.focus ->
# Assign the interval to a variable so it can be removed later
# Check every 0.5s
interval = setInterval(observer, 250)
# When the user is finished editing, remove the interval
textarea.blur ->
clearInterval(interval)
# set the watcher if using force
textarea.focus() if force
# given an image of arbitrary dimensions,
# center it within a square wrapper
# wrapper must have a defined width and height
jQuery.fn.square_image = ->
wrapper = $(this)
img = wrapper.find('img')
w = img.width()
h = img.height()
wrapper.css position: 'relative', overflow: 'hidden'
img.css position: 'absolute'
if w < h
smaller = w
larger = h
else
smaller = h
larger = w
# the percentage by which we'll offset the image
percent = (((larger - smaller) / 2) * 100 / larger * -1) / 100
# make the smaller dimension 100% and the other auto
# and position the image in the center
if h > w # portrait
img.css width: '100%', height: 'auto'
img.css top: Math.floor(percent * img.height())
else if h < w # landscape
img.css width: 'auto', height: '100%'
img.css left: Math.floor(percent * img.width())
else # square
img.css width: '100%', height: '100%'
# center an image a box, keeping its original
# aspect ratio
jQuery.fn.center_image = ->
wrapper = $(this)
img = wrapper.find('img')
wrapper.css position: 'relative', overflow: 'hidden'
w = img.width()
h = img.height()
max_size = if wrapper.height() > wrapper.width() then wrapper.width() else wrapper.height()
# if the image is smaller than the box,
# center it within the box
# otherwise, make the larger dimension 100%
if w <= max_size && h <= max_size # img is smaller than the box
img.width(w)
img.height(h)
margin_top = (max_size - h) / 2
margin_left = (max_size - w) / 2
img.css margin: "#{margin_top}px 0 0 #{margin_left}px"
else # if the image is larger than the box
# handle each case (w>h, h>w, w==h)
if w == h
img.width(max_size)
img.height(max_size)
else
if w > h
new_max = max_size / (w / h)
margin = (max_size - new_max) / 2
img.css width: max_size, height: new_max, margin: "#{margin}px 0 0 0"
else
new_max = max_size / (h / w)
margin = (max_size - new_max) / 2
img.css width: new_max, height: max_size, margin: "0 0 0 #{margin}px"
## these next two handle "save" and "done saving" states for buttons
## expects a data attribute for the button's text: 'data-saving-text'
## optionally, you can add 'data-saved-text' to show a message when
## you call unsave_state
# begin save state
jQuery.fn.save_state = ->
# set the "original-text" data attribute to the current text
$(this).attr('data-original-text', if $(this).is('input') then $(this).val() else $(this).text())
saving_text = $(this).attr('data-saving-text')
$(this).addClass('saving').css opacity: 0.5
$(this).set_button_text saving_text
# return to default state
jQuery.fn.unsave_state = ->
original_text = $(this).attr('data-original-text')
saved_text = $(this).attr('data-saved-text')
# either show the saved note and then the original text
# or just the original text
if saved_text
$(this).set_button_text saved_text
setTimeout(=>
$(this).removeClass('saving').css({opacity: 1})
$(this).set_button_text original_text
, 1000)
else
$(this).removeClass('saving').css({opacity: 1})
$(this).set_button_text original_text
# set the text on an anchor or button tag
jQuery.fn.set_button_text = (text)->
if $(this).is('input')
$(this).val text
else
$(this).text text
# replace text links with html links in a block of text
jQuery.fn.link_urls = (cutoff = null)->
$(this).each ->
unless $(this).hasClass('autolinked')
# regular expression is from http://www.regexguru.com/2008/11/detecting-urls-in-a-block-of-text/
exp = ///
\b(?:(?:https?|ftp|file)://|www\.|ftp\.)
(?:\([-A-Z0-9+&@\#/%=~_|$?!:,.]*\)|[-A-Z0-9+&@\#/%=~_|$?!:,.])*
(?:\([-A-Z0-9+&@\#/%=~_|$?!:,.]*\)|[A-Z0-9+&@\#/%=~_|$])
///ig
for url in $(this).text().match exp
# add a protocol if it just starts with www
href = if url.match(/^www.+/) then "http://#{url}" else url
# break the url with zero-width spaces
cutoff_regex = new RegExp "(^.{#{cutoff}}).+"
visual_url = url.replace(cutoff_regex, '$1...').split('').join('​')
# replace the url with a link
$(this).html $(this).html().replace(/\&\;/g, '&').replace(url, "<a href='#{href}' target='_blank'>#{visual_url}</a>")
$(this).addClass('autolinked')
## Set a custom tabindex on elements:
# remove tabindex on all items
# iterate through passed array of jQuery elements
# incrementing their tabindexes
window.set_tabindex = (items)->
$('[tabindex]').removeAttr('tabindex')
$.each items, (i, item)->
$(item).attr('tabindex', i+1)
# get the html of an object, including its wrapper
# found this one on stackoverflow somewhere, don't remember
# the original author :(
jQuery.fn.outerHTML = ()->
$(this).clone().wrap('<div>').parent().html()
# should keyboard shortcuts be allowed? returns true or false.
# sees that you are not in an input or textarea
# requires <html> to have the class "ie8" when appropriate because
# ie8 doesn't support the :focus pseudoelement
window.should_allow_keyboard_shortcuts = ->
if $(':focus').is('input') || $(':focus').is('textarea') || $(':focus').is('select') || $('html').hasClass('ie8')
return false
else
return true