Skip to content

Instantly share code, notes, and snippets.

@AutoSponge
Forked from pamelafox/util.js
Created April 13, 2012 19:13
Show Gist options
  • Select an option

  • Save AutoSponge/2379301 to your computer and use it in GitHub Desktop.

Select an option

Save AutoSponge/2379301 to your computer and use it in GitHub Desktop.

Revisions

  1. @pamelafox pamelafox revised this gist Apr 13, 2012. No changes.
  2. @pamelafox pamelafox created this gist Apr 13, 2012.
    639 changes: 639 additions & 0 deletions util.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,639 @@
    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
    };
    })();