-
-
Save examinedliving/6934541 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var ED = ED || {}; | |
// Utility functions | |
ED.util = (function() { | |
// Data structure functions | |
function each(object, callback) { | |
if (object === null) return; | |
if (object instanceof Array) { | |
for (var i = 0, item; i < object.length; i++) { | |
callback(object[i], i); | |
} | |
} else { | |
for (var key in object) { | |
if (object.hasOwnProperty(key)) { | |
callback(object[key], key); | |
} | |
} | |
} | |
} | |
function keys(object) { | |
var objectKeys = []; | |
for (var key in object) { | |
if (object.hasOwnProperty(key)) { | |
objectKeys.push(key); | |
} | |
} | |
return objectKeys; | |
} | |
function values(object) { | |
var objectValues = []; | |
for (var key in object) { | |
if (object.hasOwnProperty(key)) { | |
objectValues.push(object[key]); | |
} | |
} | |
return objectValues; | |
} | |
function inArray(arr, val) { | |
if (!arr) return false; | |
for (var i = 0; i < arr.length; i++) { | |
if (arr[i] === val) { | |
return true; | |
} | |
} | |
return false; | |
} | |
function impl(parent, generator) { | |
var par = new parent(); | |
var pub; | |
pub = generator.call(par); | |
for (var name in pub) { | |
if (pub.hasOwnProperty(name)) { | |
par[name] = pub[name]; | |
} | |
} | |
return par; | |
} | |
// String functions | |
function toCamelCase(string) { | |
return string.replace(new RegExp('_(\\w)', 'g'), function(text, letter) { | |
return letter.toUpperCase(); | |
}); | |
} | |
function toUnderscore(string) { | |
return string.replace(new RegExp('([A-Z])', 'g'), function(text, letter) { | |
return '_' + letter.toLowerCase(); | |
}); | |
} | |
function linkifyText(string){ | |
if (string) { | |
string = string.replace( | |
/((https?\:\/\/)|(www\.))(\S+)(\w{2,4})(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?/gi, | |
function(url){ | |
var full_url = url; | |
if (!full_url.match('^https?:\/\/')) { | |
full_url = 'http://' + full_url; | |
} | |
return '<a target="_blank" href="' + full_url + '">' + url.substring(0, Math.min(full_url.length, 20)) + '...</a>'; | |
}); | |
} | |
return string; | |
} | |
function trimText(string) { | |
return $.trim(string); | |
} | |
function truncateText(string, nMaxChars) { | |
if (string.length <= nMaxChars) | |
return string; | |
var xMaxFit = nMaxChars - 3; | |
var xTruncateAt = string.lastIndexOf(' ', xMaxFit); | |
if (xTruncateAt == -1 || xTruncateAt < nMaxChars / 2) | |
xTruncateAt = xMaxFit; | |
return string.substr(0, xTruncateAt) + "..."; | |
} | |
function stripHtml(html) { | |
return html.replace(/<.*?>/g, ''); | |
} | |
// Browser/feature detection functions | |
function isIOS() { | |
return ((navigator.userAgent.match(/iPhone/i)) || (navigator.userAgent.match(/iPod/i))); | |
} | |
function isAndroid() { | |
var ua = navigator.userAgent.toLowerCase(); | |
return ua.indexOf("android") > -1; | |
} | |
function isSafari() { | |
return ($.browser.webkit && !(/chrome/.test(navigator.userAgent.toLowerCase()))); | |
} | |
function isTouchDevice() { | |
return ('ontouchstart' in window); | |
} | |
function isSmallScreen() { | |
if (!window.orientation) return false; | |
if (window.orientation === 0) { // portrait | |
return screen.width < 400; | |
} else { // landscape | |
return screen.height < 400; | |
} | |
} | |
// Window & DOM functions | |
function changePage(url) { | |
window.location.href = url; | |
} | |
function getUrlParam(name) { | |
name = name.replace(/[\[]/, '\\[').replace(/[\]]/, '\\]'); | |
var regexS = "[\\?&]"+name+"=([^&#]*)"; | |
var regex = new RegExp( regexS ); | |
var results = regex.exec(unescape(window.location.href)); | |
if( results === null ) | |
return null; | |
else | |
return results[1]; | |
} | |
function getHashParam(name) { | |
var regexS = name + "=([^&#]*)"; | |
var regex = new RegExp(regexS); | |
var splitUrl = unescape(window.location.href).split('#'); | |
var hash = ((splitUrl.length > 1) && splitUrl[1]) || ''; | |
var results = regex.exec(hash); | |
if( results === null ) | |
return null; | |
else | |
return results[1]; | |
} | |
function changeHashParam(name, value) { | |
var regexS = name + "=([^&#]*)"; | |
var regex = new RegExp(regexS); | |
var hash = window.location.hash; | |
var results = regex.exec(hash); | |
if (getHashParam(name)) { | |
window.location.hash = window.location.hash.replace(regex, name + '=' + value); | |
} else { | |
if (window.location.hash.indexOf('=') > -1) { | |
window.location.hash += '&'; | |
} | |
window.location.hash += name + '=' + value; | |
} | |
} | |
function getBrowserInfo() { | |
if (window.device) { | |
return device.name + ' | ' + device.phonegap + ' | ' + device.platform + ' | ' + device.uuid + ' | ' + device.version; | |
} else { | |
return navigator.userAgent; | |
} | |
} | |
// Abstract on top of Zepto/jQuery differences | |
function isVisible(elem) { | |
if ($(elem).isVisible) { | |
return $(elem).isVisible(); | |
} else { | |
return $(elem).is(':visible'); | |
} | |
} | |
function inView(elem, nearThreshold) { | |
var viewportHeight = getViewportHeight(); | |
var scrollTop = (document.documentElement.scrollTop ? | |
document.documentElement.scrollTop : | |
document.body.scrollTop); | |
var elemTop = elem.offset().top; | |
var elemHeight = elem.height(); | |
nearThreshold = nearThreshold || 0; | |
if ((scrollTop + viewportHeight + nearThreshold) > (elemTop + elemHeight)) { | |
return true; | |
} | |
return false; | |
} | |
function getViewportHeight() { | |
var height = window.innerHeight; // Safari, Opera | |
var mode = document.compatMode; | |
if ( (mode || !$.support.boxModel) ) { // IE, Gecko | |
height = (mode == 'CSS1Compat') ? | |
document.documentElement.clientHeight : // Standards | |
document.body.clientHeight; // Quirks | |
} | |
return height; | |
} | |
function resetScroll(top) { | |
top = top || 0; | |
$(document).scrollTop(top); | |
window.setTimeout(function() { | |
$(document).scrollTop(top); | |
}, 10); | |
} | |
function detectHash() { | |
function maybeScrollToHash() { | |
if (window.location.hash && $(window.location.hash).length) { | |
var newTop = $(window.location.hash).offset().top - 40; | |
$(window).scrollTop(newTop); | |
} | |
} | |
$(window).bind('hashchange', function() { | |
maybeScrollToHash(); | |
}); | |
maybeScrollToHash(); | |
} | |
function putCursorAtEnd(textarea) { | |
$(textarea).focus(); | |
if (textarea.setSelectionRange) { | |
// ... then use it | |
// (Doesn't work in IE) | |
// Double the length because Opera is inconsistent about whether a carriage return is one character or two. Sigh. | |
var len = $(textarea).val().length * 2; | |
textarea.setSelectionRange(len, len); | |
} else { | |
// ... otherwise replace the contents with itself | |
// (Doesn't work in Google Chrome) | |
$(textarea).val($(textarea).val()); | |
} | |
// Scroll to the bottom, in case we're in a tall textarea | |
// (Necessary for Firefox and Google Chrome) | |
textarea.scrollTop = 999999; | |
} | |
// Time and date functionality | |
function toDateObject(shortDate) { | |
var splitDate = shortDate.split('/'); | |
if (splitDate.length != 3) return; | |
var month = parseInt(splitDate[0], 10) - 1; | |
var day = parseInt(splitDate[1], 10); | |
var year = parseInt(splitDate[2], 10); | |
var date = new Date(year, month, day); | |
return date; | |
} | |
function toShortWeekday(date) { | |
var days = ['S', 'M', 'T', 'W', 'T', 'F', 'S']; | |
return days[date.getDay()]; | |
} | |
function toShortDate(date) { | |
if (typeof date == 'string') date = new Date(date); | |
if (!date) date = new Date(); | |
return date.format('mm/dd/yyyy'); // or 'shortDate' | |
} | |
function toLongDate(date) { | |
if (date && typeof date == 'string') date = new Date(date); | |
if (!date) date = new Date(); | |
return date.format('dddd, mmm. d, yyyy'); // or 'fullDate' | |
} | |
function toISODate(date) { | |
function pad(n) { | |
return n < 10 ? '0' + n : n; | |
} | |
return date.getUTCFullYear() + '-' + | |
pad(date.getUTCMonth()+1) + '-' + | |
pad(date.getUTCDate()) + 'T' + | |
pad(date.getUTCHours()) + ':' + | |
pad(date.getUTCMinutes()) + ':' + | |
pad(date.getUTCSeconds()) + 'Z'; | |
} | |
// Inclusive | |
function getDatesBetween(oldestDate, newestDate) { | |
var allDates = []; | |
var currentDate = new Date(oldestDate.getTime()); | |
var num = 0; | |
while (currentDate <= newestDate) { | |
allDates.push(toShortDate(currentDate)); | |
currentDate.setDate(currentDate.getDate()+1); | |
num++; | |
} | |
return allDates; | |
} | |
function getDatesSince(oldestDate) { | |
var allDates = []; | |
var today = new Date(); | |
var currentDate = new Date(oldestDate.getTime()); | |
var num = 0; | |
while (currentDate < today) { | |
allDates.push(toShortDate(currentDate)); | |
currentDate.setTime(currentDate.getTime()+(1*24*60*60*1000)); | |
num++; | |
} | |
return allDates; | |
} | |
function getCurrentTime() { | |
var nowTime = new Date(); | |
var timeHours = nowTime.getHours(); | |
var timeMinutes = ':00'; | |
if (nowTime.getMinutes() > 15) { | |
if (nowTime.getMinutes() < 45) { | |
timeMinutes = ':30'; | |
} else { | |
timeHours += 1; | |
} | |
} | |
var timeSuffix = 'am'; | |
if (timeHours >= 12) { | |
timeSuffix = 'pm'; | |
} | |
if (timeHours > 12) { | |
timeHours = timeHours - 12; | |
} | |
if (timeHours === 0) timeHours = 12; | |
return {time: (timeHours + timeMinutes), suffix: timeSuffix}; | |
} | |
function renderTemplate(templateId, data) { | |
var template; | |
var html; | |
templateId = templateId && templateId.replace('#', ''); | |
if (!document.getElementById(templateId)) { | |
log('Could not find template ' + templateId); | |
return ''; | |
} | |
if (window.JsViews) { | |
template = window.JsViews.template(templateId, document.getElementById(templateId).innerHTML); | |
html = window.JsViews.render(data || {}, templateId); | |
} else { | |
template = $.template(templateId, document.getElementById(templateId).innerHTML); | |
html = $.render(data || {}, templateId); | |
} | |
loadVisibleImages(); | |
return html; | |
} | |
function useTouchEvents() { | |
if (isTouchDevice()) { | |
if (isAndroid()) return true; | |
if (isIOS()) return false; | |
} | |
return false; | |
} | |
function triggerClick(dom) { | |
if (useTouchEvents()) { | |
dom.trigger('tap'); | |
} else { | |
dom.trigger('click'); | |
} | |
} | |
ISTOUCHING = false; | |
function addTouchOrClickHandler(dom, callback, logThis) { | |
function logAction(message, dom) { | |
log(message + ' on ' + $(dom).text().replace('\n', '')); | |
} | |
if (useTouchEvents()) { | |
dom.each(function() { | |
$(this).unbind('tap', callback); | |
$(this).bind('tap', callback); | |
$(this).bind('touchstart', function(e) { | |
e.preventDefault(); | |
//e.stopPropagation(); | |
var item = e.currentTarget; | |
if (ISTOUCHING) return; | |
item.moved = false; | |
ISTOUCHING = true; | |
item.startX = e.touches[0].pageX; | |
item.startY = e.touches[0].pageY; | |
$(item).addClass('active'); | |
}); | |
$(this).bind('touchmove', function(e) { | |
var item = e.currentTarget; | |
if (Math.abs(e.touches[0].pageX - item.startX) > 10 || | |
Math.abs(e.touches[0].pageY - item.startY) > 10) { | |
item.moved = true; | |
$(item).removeClass('active'); | |
} | |
}); | |
$(this).bind('touchend', function(e) { | |
var item = e.currentTarget; | |
ISTOUCHING = false; | |
if(!item.moved) { | |
//e.stopPropagation(); | |
//e.preventDefault(); | |
$(item).trigger('tap'); | |
} | |
setTimeout(function() { | |
$(item).removeClass('active'); | |
}, 1000); | |
delete item.moved; | |
}); | |
}); | |
} else { | |
dom.unbind('click', callback).bind('click', callback); | |
} | |
} | |
function addClickHandler(dom, callback) { | |
dom.unbind('click', callback).bind('click', callback); | |
} | |
function addWindowScrollHandler(callback) { | |
$(window).off('scroll', callback).on('scroll', $.throttle(500, callback)); | |
} | |
function enableElement(dom) { | |
$(dom).attr('disabled', false).removeAttr('disabled'); | |
} | |
function disableElement(dom) { | |
$(dom).attr('disabled', 'disabled'); | |
} | |
function showModal(dom) { | |
if ($(dom).modal) { | |
$(dom).modal('show'); | |
} | |
} | |
function hideModal(dom) { | |
if ($(dom).modal) { | |
$(dom).modal('hide'); | |
} | |
} | |
function addModalShowHandler(dom, callback) { | |
$(dom).unbind('shown', callback).bind('shown', callback); | |
} | |
function addModalHideHandler(dom, callback) { | |
$(dom).unbind('hidden', callback).bind('hidden', callback); | |
} | |
function loadVisibleImages() { | |
$('img').each(function() { | |
if (this.src === '' && $(this).attr('data-src') && ED.util.isVisible($(this))) { | |
if (ED.util.inView($(this), 500)) { | |
this.src = $(this).attr('data-src'); | |
} | |
} | |
}); | |
} | |
// Logging | |
var allLogs = []; | |
function log(something) { | |
// Store | |
var storedSomething = something; | |
if (window.JSON) { | |
storedSomething = JSON.stringify(something); | |
} | |
storedSomething = 'LOG @ ' + new Date().toString() + ': ' + truncateText(storedSomething, 200); | |
allLogs.push(storedSomething); | |
$('#mobile-feedback-logs').html(allLogs.reverse().join('<br>')); | |
// Output | |
if (window.console) { | |
if (something instanceof Date) { | |
something = something.toDateString(); | |
} | |
if (isIOS() || isAndroid()) { | |
if (typeof something == 'object') { | |
something = JSON.stringify(something); | |
} | |
something = truncateText(something, 2000); | |
something = '\nLOG: ' + something; | |
var stacktrace = ''; | |
if (window.printStackTrace) { | |
try { | |
stacktrace = '\n -' + printStackTrace().slice(4).join('\n -'); | |
something += '\nSTACKTRACE:' + stacktrace; | |
} catch(e) {} | |
} | |
if (isIOS()) { | |
//alert(something); | |
console.log(something); | |
} else { | |
console.log(something); | |
} | |
if ($('#logs-viewer').length) { | |
$('#logs-viewer').prepend(something.replace(/\n/g, '<br>') + '<br>'); | |
} | |
} else { | |
console.log(something); | |
} | |
} | |
} | |
function getLogs() { | |
return allLogs; | |
} | |
var timedEvents = []; | |
function timeEvent(name) { | |
timedEvents.push({'name': name || 'unnamed', time: Date.now()}); | |
} | |
function showTimedEvents() { | |
var timeText = ''; | |
var lastTime = null; | |
for (var i = 0; i < timedEvents.length; i++) { | |
var timedEvent = timedEvents[i]; | |
timeText += 'Event ' + timedEvent.name + ': ' + timedEvent.time; | |
if (lastTime) timeText += timedEvent.time - lastTime.time + ' after'; | |
timeText += '\\n'; | |
} | |
log(timeText); | |
} | |
return { | |
// Data structures | |
each: each, | |
keys: keys, | |
values: values, | |
inArray: inArray, | |
impl: impl, | |
// Strings | |
toCamelCase: toCamelCase, | |
toUnderscore: toUnderscore, | |
truncateText: truncateText, | |
linkifyText: linkifyText, | |
stripHtml: stripHtml, | |
trimText: trimText, | |
// Dates | |
toLongDate: toLongDate, | |
toShortDate: toShortDate, | |
toISODate: toISODate, | |
toShortWeekday: toShortWeekday, | |
toDateObject: toDateObject, | |
getDatesSince: getDatesSince, | |
getDatesBetween: getDatesBetween, | |
getCurrentTime: getCurrentTime, | |
// Window | |
isAndroid: isAndroid, | |
isSafari: isSafari, | |
isIOS: isIOS, | |
isSmallScreen: isSmallScreen, | |
isTouchDevice: isTouchDevice, | |
changePage: changePage, | |
getUrlParam: getUrlParam, | |
getHashParam: getHashParam, | |
changeHashParam: changeHashParam, | |
getBrowserInfo: getBrowserInfo, | |
// DOM | |
inView: inView, | |
isVisible: isVisible, | |
detectHash: detectHash, | |
resetScroll: resetScroll, | |
putCursorAtEnd: putCursorAtEnd, | |
renderTemplate: renderTemplate, | |
addClickHandler: addClickHandler, | |
addTouchOrClickHandler: addTouchOrClickHandler, | |
addWindowScrollHandler: addWindowScrollHandler, | |
enableElement: enableElement, | |
disableElement: disableElement, | |
triggerClick: triggerClick, | |
showModal: showModal, | |
hideModal: hideModal, | |
addModalShowHandler: addModalShowHandler, | |
addModalHideHandler: addModalHideHandler, | |
loadVisibleImages: loadVisibleImages, | |
// Logging | |
log: log, | |
getLogs: getLogs, | |
timeEvent: timeEvent, | |
showTimedEvents: showTimedEvents | |
}; | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment