Skip to content

Instantly share code, notes, and snippets.

@tigerhawkvok
Last active August 29, 2015 14:22
Show Gist options
  • Save tigerhawkvok/557653994a6e2f5c4a5a to your computer and use it in GitHub Desktop.
Save tigerhawkvok/557653994a6e2f5c4a5a to your computer and use it in GitHub Desktop.
Core Coffeescript helper functions
# Set up basic URI parameters
# Uses
# https://github.com/allmarkedup/purl
try
uri = new Object()
uri.o = $.url()
uri.urlString = uri.o.attr('protocol') + '://' + uri.o.attr('host') + uri.o.attr("directory")
uri.query = uri.o.attr("fragment")
catch e
console.warn("PURL not installed!")
window.locationData = new Object()
locationData.params =
enableHighAccuracy: true
locationData.last = undefined
window.debounce_timer = null
isBool = (str,strict = false) ->
if strict
return typeof str is "boolean"
try
if typeof str is "boolean"
return str is true or str is false
if typeof str is "string"
return str.toLowerCase() is "true" or str.toLowerCase() is "false"
if typeof str is "number"
return str is 1 or str is 0
false
catch e
return false
isEmpty = (str) -> not str or str.length is 0
isBlank = (str) -> not str or /^\s*$/.test(str)
isNull = (str) ->
try
if isEmpty(str) or isBlank(str) or not str?
unless str is false or str is 0 then return true
catch e
return false
false
isJson = (str) ->
if typeof str is 'object' then return true
try
JSON.parse(str)
return true
catch e
return false
false
isNumber = (n) -> not isNaN(parseFloat(n)) and isFinite(n)
toFloat = (str) ->
if not isNumber(str) or isNull(str) then return 0
parseFloat(str)
toInt = (str) ->
if not isNumber(str) or isNull(str) then return 0
parseInt(str)
String::toBool = -> @toString() is 'true'
Boolean::toBool = -> @toString() is 'true'
Number::toBool = -> @toString() is "1"
String::addSlashes = ->
`this.replace(/[\\"']/g, '\\$&').replace(/\u0000/g, '\\0')`
Object.size = (obj) ->
if typeof obj isnt "object"
try
return obj.length
catch e
console.error("Passed argument isn't an object and doesn't have a .length parameter")
console.warn(e.message)
size = 0
size++ for key of obj when obj.hasOwnProperty(key)
size
delay = (ms,f) -> setTimeout(f,ms)
roundNumber = (number,digits = 0) ->
multiple = 10 ** digits
Math.round(number * multiple) / multiple
roundNumberSigfig = (number, digits = 0) ->
newNumber = roundNumber(number, digits).toString()
digArr = newNumber.split(".")
if digArr.length is 1
return "#{newNumber}.#{Array(digits + 1).join("0")}"
trailingDigits = digArr.pop()
significand = "#{digArr[0]}."
if trailingDigits.length is digits
return newNumber
needDigits = digits - trailingDigits.length
trailingDigits += Array(needDigits + 1).join("0")
"#{significand}#{trailingDigits}"
jsonTo64 = (obj) ->
if typeof obj is "array"
obj = toObject(arr)
objString = JSON.stringify(obj)
encodeURIComponent(encode64(objString))
encode64 = (string) ->
try
Base64.encode(string)
catch e
console.warn("Bad encode string provided")
string
decode64 = (string) ->
try
Base64.decode(string)
catch e
console.warn("Bad decode string provided")
string
jQuery.fn.polymerSelected = (setSelected = undefined, attrLookup = "attrForSelected") ->
###
# See
# https://elements.polymer-project.org/elements/paper-menu
# https://elements.polymer-project.org/elements/paper-radio-group
#
# @param attrLookup is based on
# https://elements.polymer-project.org/elements/iron-selector?active=Polymer.IronSelectableBehavior
###
attr = $(this).attr(attrLookup)
if setSelected?
if not isBool(setSelected)
try
$(this).get(0).select(setSelected)
catch e
return false
else
$(this).parent().children().removeAttribute("aria-selected")
$(this).parent().children().removeAttribute("active")
$(this).parent().children().removeClass("iron-selected")
$(this).prop("selected",setSelected)
$(this).prop("active",setSelected)
$(this).prop("aria-selected",setSelected)
if setSelected is true
$(this).addClass("iron-selected")
else
val = undefined
try
val = $(this).get(0).selected
if isNumber(val) and not isNull(attr)
itemSelector = $(this).find("paper-item")[toInt(val)]
val = $(itemSelector).attr(attr)
catch e
return false
if val is "null" or not val?
val = undefined
val
jQuery.fn.polymerChecked = (setChecked = undefined) ->
# See
# https://www.polymer-project.org/docs/elements/paper-elements.html#paper-dropdown-menu
if setChecked?
jQuery(this).prop("checked",setChecked)
else
val = jQuery(this)[0].checked
if val is "null" or not val?
val = undefined
val
isHovered = (selector) ->
$("#{selector}:hover").length > 0
jQuery.fn.exists = -> jQuery(this).length > 0
jQuery.fn.isVisible = ->
jQuery(this).is(":visible") and jQuery(this).css("visibility") isnt "hidden"
jQuery.fn.hasChildren = ->
Object.size(jQuery(this).children()) > 3
byteCount = (s) => encodeURI(s).split(/%..|./).length - 1
`function shuffle(o) { //v1.0
for (var j, x, i = o.length; i; j = Math.floor(Math.random() * i), x = o[--i], o[i] = o[j], o[j] = x);
return o;
}`
toObject = (array) ->
rv = new Object()
for index, element of array
if element isnt undefined then rv[index] = element
rv
loadJS = (src, callback = new Object(), doCallbackOnError = true) ->
###
# Load a new javascript file
#
# If it's already been loaded, jump straight to the callback
#
# @param string src The source URL of the file
# @param function callback Function to execute after the script has
# been loaded
# @param bool doCallbackOnError Should the callback be executed if
# loading the script produces an error?
###
if $("script[src='#{src}']").exists()
if typeof callback is "function"
try
callback()
catch e
console.error "Script is already loaded, but there was an error executing the callback function - #{e.message}"
# Whether or not there was a callback, end the script
return true
# Create a new DOM selement
s = document.createElement("script")
# Set all the attributes. We can be a bit redundant about this
s.setAttribute("src",src)
s.setAttribute("async","async")
s.setAttribute("type","text/javascript")
s.src = src
s.async = true
# Onload function
onLoadFunction = ->
state = s.readyState
try
if not callback.done and (not state or /loaded|complete/.test(state))
callback.done = true
if typeof callback is "function"
try
callback()
catch e
console.error "Postload callback error - #{e.message}"
catch e
console.error "Onload error - #{e.message}"
# Error function
errorFunction = ->
console.warn "There may have been a problem loading #{src}"
try
unless callback.done
callback.done = true
if typeof callback is "function" and doCallbackOnError
try
callback()
catch e
console.error "Post error callback error - #{e.message}"
catch e
console.error "There was an error in the error handler! #{e.message}"
# Set the attributes
s.setAttribute("onload",onLoadFunction)
s.setAttribute("onreadystate",onLoadFunction)
s.setAttribute("onerror",errorFunction)
s.onload = s.onreadystate = onLoadFunction
s.onerror = errorFunction
document.getElementsByTagName('head')[0].appendChild(s)
true
String::toTitleCase = ->
# From http://stackoverflow.com/a/6475125/1877527
str =
@replace /([^\W_]+[^\s-]*) */g, (txt) ->
txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase()
# Certain minor words should be left lowercase unless
# they are the first or last words in the string
lowers = [
"A"
"An"
"The"
"And"
"But"
"Or"
"For"
"Nor"
"As"
"At"
"By"
"For"
"From"
"In"
"Into"
"Near"
"Of"
"On"
"Onto"
"To"
"With"
]
for lower in lowers
lowerRegEx = new RegExp("\\s#{lower}\\s","g")
str = str.replace lowerRegEx, (txt) -> txt.toLowerCase()
# Certain words such as initialisms or acronyms should be left
# uppercase
uppers = [
"Id"
"Tv"
]
for upper in uppers
upperRegEx = new RegExp("\\b#{upper}\\b","g")
str = str.replace upperRegEx, upper.toUpperCase()
str
Function::debounce = (threshold = 300, execAsap = false, timeout = debounce_timer, args...) ->
# Borrowed from http://coffeescriptcookbook.com/chapters/functions/debounce
# Only run the prototyped function once per interval.
func = this
delayed = ->
func.apply(func, args) unless execAsap
console.log("Debounce applied")
if timeout?
try
clearTimeout(timeout)
catch e
# just do nothing
else if execAsap
func.apply(obj, args)
console.log("Executed immediately")
setTimeout(delayed, threshold)
randomInt = (lower = 0, upper = 1) ->
start = Math.random()
if not lower?
[lower, upper] = [0, lower]
if lower > upper
[lower, upper] = [upper, lower]
return Math.floor(start * (upper - lower + 1) + lower)
# Animations
animateLoad = (elId = "loader") ->
###
# Suggested CSS to go with this:
#
# #loader {
# position:fixed;
# top:50%;
# left:50%;
# }
# #loader.good::shadow .circle {
# border-color: rgba(46,190,17,0.9);
# }
# #loader.bad::shadow .circle {
# border-color:rgba(255,0,0,0.9);
# }
#
# Uses Polymer 1.0
###
if isNumber(elId) then elId = "loader"
if elId.slice(0,1) is "#"
selector = elId
elId = elId.slice(1)
else
selector = "##{elId}"
try
if not $(selector).exists()
$("body").append("<paper-spinner id=\"#{elId}\" active></paper-spinner")
else
$(selector).attr("active",true)
false
catch e
console.log('Could not animate loader', e.message)
startLoad = animateLoad
stopLoad = (elId = "loader", fadeOut = 1000) ->
if elId.slice(0,1) is "#"
selector = elId
elId = elId.slice(1)
else
selector = "##{elId}"
try
if $(selector).exists()
$(selector).addClass("good")
delay fadeOut, ->
$(selector).removeClass("good")
$(selector).removeAttr("active")
catch e
console.log('Could not stop load animation', e.message)
stopLoadError = (message, elId = "loader", fadeOut = 5000) ->
if elId.slice(0,1) is "#"
selector = elId
elId = elId.slice(1)
else
selector = "##{elId}"
try
if $(selector).exists()
$(selector).addClass("bad")
if message? then toastStatusMessage(message,"",fadeOut)
delay fadeOut, ->
$(selector).removeClass("bad")
$(selector).removeAttr("active")
catch e
console.log('Could not stop load error animation', e.message)
toastStatusMessage = (message, className = "", duration = 3000, selector = "#status-message") ->
###
# Pop up a status message
###
unless window.metaTracker?.isToasting?
unless window.metaTracker?
window.metaTracker = new Object()
window.metaTracker.isToasting = false
if window.metaTracker.isToasting
delay 250, ->
# Wait and call again
toastStatusMessage(message, className, duration, selector)
return false
window.metaTracker.isToasting = true
if not isNumber(duration)
duration = 3000
if selector.slice(0,1) is not "#"
selector = "##{selector}"
if not $(selector).exists()
html = "<paper-toast id=\"#{selector.slice(1)}\" duration=\"#{duration}\"></paper-toast>"
$(html).appendTo("body")
$(selector)
.attr("text",message)
.text(message)
.addClass(className)
$(selector).get(0).show()
delay duration + 500, ->
# A short time after it hides, clean it up
$(selector).empty()
$(selector).removeClass(className)
$(selector).attr("text","")
window.metaTracker.isToasting = false
openLink = (url) ->
if not url? then return false
window.open(url)
false
openTab = (url) ->
openLink(url)
goTo = (url) ->
if not url? then return false
window.location.href = url
false
mapNewWindows = (stopPropagation = true) ->
# Do new windows
$(".newwindow").each ->
# Add a click and keypress listener to
# open links with this class in a new window
curHref = $(this).attr("href")
if not curHref?
# Support non-standard elements
curHref = $(this).attr("data-href")
openInNewWindow = (url) ->
if not url? then return false
window.open(url)
return false
$(this).click (e) ->
if stopPropagation
e.preventDefault()
e.stopPropagation()
openInNewWindow(curHref)
$(this).keypress ->
openInNewWindow(curHref)
deepJQuery = (selector) ->
###
# Do a shadow-piercing selector
#
# Cross-browser, works with Chrome, Firefox, Opera, Safari, and IE
# Falls back to standard jQuery selector when everything fails.
###
try
# Chrome uses /deep/ which has been deprecated
# See http://dev.w3.org/csswg/css-scoping/#deep-combinator
# https://w3c.github.io/webcomponents/spec/shadow/#composed-trees
# This is current as of Chrome 44.0.2391.0 dev-m
# See https://code.google.com/p/chromium/issues/detail?id=446051
unless $("html /deep/ #{selector}").exists()
throw("Bad /deep/ selector")
return $("html /deep/ #{selector}")
catch e
try
# Firefox uses >>> instead of "deep"
# https://developer.mozilla.org/en-US/docs/Web/Web_Components/Shadow_DOM
# This is actually the correct selector
unless $("html >>> #{selector}").exists()
throw("Bad >>> selector")
return $("html >>> #{selector}")
catch e
# These don't match at all -- do the normal jQuery selector
return $(selector)
d$ = (selector) ->
deepJQuery(selector)
bindClicks = (selector = ".click") ->
###
# Helper function. Bind everything with a selector
# to execute a function data-function or to go to a
# URL data-href.
###
$(selector).each ->
try
url = $(this).attr("data-href")
if not isNull(url)
$(this).unbind()
# console.log("Binding a url to ##{$(this).attr("id")}")
try
if url is uri.o.attr("path") and $(this).prop("tagName").toLowerCase() is "paper-tab"
$(this).parent().prop("selected",$(this).index())
catch e
console.warn("tagname lower case error")
$(this).click ->
if $(this).attr("newTab").toBool() or $(this).attr("newtab").toBool()
openTab(url)
else
goTo(url)
return url
else
# Check for onclick function
callable = $(this).attr("data-function")
if callable?
$(this).unbind()
# console.log("Binding #{callable}() to ##{$(this).attr("id")}")
$(this).click ->
try
console.log("Executing bound function #{callable}()")
window[callable]()
catch e
console.error("'#{callable}()' is a bad function - #{e.message}")
catch e
console.error("There was a problem binding to ##{$(this).attr("id")} - #{e.message}")
false
getPosterFromSrc = (srcString) ->
###
# Take the "src" attribute of a video and get the
# "png" screencap from it, and return the value.
###
try
split = srcString.split(".")
dummy = split.pop()
split.push("png");
return split.join(".")
catch e
return ""
doCORSget = (url, args, callback = undefined, callbackFail = undefined) ->
corsFail = ->
if typeof callbackFail is "function"
callbackFail()
else
throw new Error("There was an error performing the CORS request")
# First try the jquery way
settings =
url: url
data: args
type: "get"
crossDomain: true
try
$.ajax(settings)
.done (result) ->
if typeof callback is "function"
callback()
return false
console.log(response)
.fail (result,status) ->
console.warn("Couldn't perform jQuery AJAX CORS. Attempting manually.")
catch e
console.warn("There was an error using jQuery to perform the CORS request. Attemping manually.")
# Then try the long way
url = "#{url}?#{args}"
createCORSRequest = (method = "get", url) ->
# From http://www.html5rocks.com/en/tutorials/cors/
xhr = new XMLHttpRequest()
if "withCredentials" of xhr
# Check if the XMLHttpRequest object has a "withCredentials"
# property.
# "withCredentials" only exists on XMLHTTPRequest2 objects.
xhr.open(method,url,true)
else if typeof XDomainRequest isnt "undefined"
# Otherwise, check if XDomainRequest.
# XDomainRequest only exists in IE, and is IE's way of making CORS requests.
xhr = new XDomainRequest()
xhr.open(method,url)
else
xhr = null
return xhr
# Now execute it
xhr = createCORSRequest("get",url)
if !xhr
throw new Error("CORS not supported")
xhr.onload = ->
response = xhr.responseText
if typeof callback is "function"
callback(response)
console.log(response)
return false
xhr.onerror = ->
console.warn("Couldn't do manual XMLHttp CORS request")
# Place this in the last error
corsFail()
xhr.send()
false
lightboxImages = (selector = ".lightboximage", lookDeeply = false) ->
###
# Lightbox images with this selector
#
# If the image has it, wrap it in an anchor and bind;
# otherwise just apply to the selector.
#
# Requires ImageLightbox
# https://github.com/rejas/imagelightbox
###
# The options!
options =
onStart: ->
overlayOn()
onEnd: ->
overlayOff()
activityIndicatorOff()
onLoadStart: ->
activityIndicatorOn()
onLoadEnd: ->
activityIndicatorOff()
allowedTypes: 'png|jpg|jpeg|gif|bmp|webp'
quitOnDocClick: true
quitOnImgClick: true
jqo = if lookDeeply then d$(selector) else $(selector)
jqo
.click (e) ->
try
$(this).imageLightbox(options).startImageLightbox()
# We want to stop the events propogating up for these
e.preventDefault()
e.stopPropagation()
console.warn("Event propagation was stopped when clicking on this.")
catch e
console.error("Unable to lightbox this image!")
# Set up the items
.each ->
console.log("Using selectors '#{selector}' / '#{this}' for lightboximages")
try
if $(this).prop("tagName").toLowerCase() is "img" and $(this).parent().prop("tagName").toLowerCase() isnt "a"
tagHtml = $(this).removeClass("lightboximage").prop("outerHTML")
imgUrl = switch
when not isNull($(this).attr("data-layzr-retina"))
$(this).attr("data-layzr-retina")
when not isNull($(this).attr("data-layzr"))
$(this).attr("data-layzr")
else
$(this).attr("src")
$(this).replaceWith("<a href='#{imgUrl}' class='lightboximage'>#{tagHtml}</a>")
catch e
console.log("Couldn't parse through the elements")
activityIndicatorOn = ->
$('<div id="imagelightbox-loading"><div></div></div>' ).appendTo('body')
activityIndicatorOff = ->
$('#imagelightbox-loading').remove()
$("#imagelightbox-overlay").click ->
# Clicking anywhere on the overlay clicks on the image
# It loads too late to let the quitOnDocClick work
$("#imagelightbox").click()
overlayOn = ->
$('<div id="imagelightbox-overlay"></div>').appendTo('body')
overlayOff = ->
$('#imagelightbox-overlay').remove()
formatScientificNames = (selector = ".sciname") ->
$(".sciname").each ->
# Is it italic?
nameStyle = if $(this).css("font-style") is "italic" then "normal" else "italic"
$(this).css("font-style",nameStyle)
prepURI = (string) ->
string = encodeURIComponent(string)
string.replace(/%20/g,"+")
window.locationData = new Object()
locationData.params =
enableHighAccuracy: true
locationData.last = undefined
getLocation = (callback = undefined) ->
geoSuccess = (pos,callback) ->
window.locationData.lat = pos.coords.latitude
window.locationData.lng = pos.coords.longitude
window.locationData.acc = pos.coords.accuracy
window.locationData.last = Date.now() # ms, unix time
if callback?
callback(window.locationData)
false
geoFail = (error,callback) ->
locationError = switch error.code
when 0 then "There was an error while retrieving your location: #{error.message}"
when 1 then "The user prevented this page from retrieving a location"
when 2 then "The browser was unable to determine your location: #{error.message}"
when 3 then "The browser timed out retrieving your location."
console.error(locationError)
if callback?
callback(false)
false
if navigator.geolocation
navigator.geolocation.getCurrentPosition(geoSuccess,geoFail,window.locationData.params)
else
console.warn("This browser doesn't support geolocation!")
if callback?
callback(false)
getMaxZ = ->
mapFunction = ->
$.map $("body *"), (e,n) ->
if $(e).css("position") isnt "static"
return parseInt $(e).css("z-index") or 1
Math.max.apply null, mapFunction()
foo = ->
toastStatusMessage("Sorry, this feature is not yet finished")
stopLoad()
false
$ ->
bindClicks()
formatScientificNames()
try
$('[data-toggle="tooltip"]').tooltip()
catch e
console.warn("Tooltips were attempted to be set up, but do not exist")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment