JSONP with jQuery and ASP.NET

JSONP allows you to execute something similar to AJAX, across domains without worrying about whether the request is across domains, or any special CORS configuration.

It’s not generally a huge deal to set up CORS, but JSONP is guaranteed to work in all browsers and servers without any special handling.

Javascript

function go() {
    jQuery.ajax({
        // url: "/Home/DoSomething", // Regular MVC
        // url: "/Handler1.ashx", // WebForms
        data: {xyz:"World"},
        dataType: "jsonp",
        type: "get"
    }).done(function (result) {
        console.log(result);
    });
}

ASP.NET

// Regular MVC - no special filters, return types, etc. 
public ActionResult DoSomething(string xyz) 
{
    Response.ContentType = "application/json";
    var foo = new { FirstName = "John", LastName = "Doe", Message = "Hello " + xyz };
    Response.Write(string.Format("{0}({1});", Request["callback"], JsonConvert.SerializeObject(foo)));
    return null;
}

// WebForms
public class Handler1 : IHttpHandler 
{
    public void ProcessRequest(HttpContext context)
    {
        context.Response.ContentType = "application/json";
        var foo = new { FirstName = "John", LastName = "Doe", Message = "Hello " + context.Request["xyz"] };
        context.Response.Write(string.Format("{0}({1});", context.Request["callback"], JsonConvert.SerializeObject(foo)));
    }

    public bool IsReusable { get { return false; } }
}

jQuery noConflict

If you need to inject a specific version of jQuery into a webpage that may or may not already have a different version of jQuery, or if you specifically need two different versions loaded, then you can use jQuery.noConflict.

You might use this if you’re building a custom control that gets injected into a page, where you want to make sure your stuff works as intended, but you don’t want to accidentally upgrade or downgrade the main page’s jQuery.

Sample Code

This sample will show how to load jQuery 1.8.3 into a page which may or may not already have a different version loaded.

var _jq183;

function load183() {
    if (typeof window.jQuery === "function" && window.jQuery.fn && window.jQuery.fn.jquery === "1.8.3") {
        _jq183 = window.jQuery;
        return;
    }

    if (typeof window.jQuery === 'undefined' || !window.jQuery.fn || (window.jQuery.fn.jquery !== "1.8.3")) {

        var done = false;

        var body = document.getElementsByTagName("body")[0];
        var scr = document.createElement("script");
        scr.setAttribute("src", "https://code.jquery.com/jquery-1.8.3.min.js");
        scr.onload = scr.onreadystatechange = function () {
            if (!done && (!this.readyState || this.readyState === "loaded" || this.readyState === "complete")) {
                done = true;
                _jq183 = jQuery.noConflict(true);
                scr.onload = scr.onreadystatechange = null;
                body.removeChild(scr);
            };
        };
        body.appendChild(scr);
    };
}

function showVersion() {
    if (typeof $ === "undefined") {
        console.log("$ is undefined");
    } else {
        console.log("$: " + $.fn.jquery);
    }
    if (typeof jQuery === "undefined") {
        console.log("jQuery is undefined");
    } else {
        console.log("jQuery: " + jQuery.fn.jquery);
    }
    console.log("_jq183: " + (_jq183 ? _jq183.fn.jquery : "N/A"));
}

You may get an error message, like:

Refused to load the script 'https://code.jquery.com/jquery-1.8.3.min.js' because it violates the following Content Security Policy directive: "script-src somethingsomething.com".</code>

From what I’ve read, if you get this, you’re out of luck because the site won’t allow you to inject a script from another domain.

jQuery Filter by Data

jQuery won’t let you search using a regular selector for data attributes if those data attributes were added using the data function:

jQuery("#something").data("somekey", "someval");
jQuery("#something[data-somekey=someval]"); // does not find it

Instead, define a new function:

jQuery.fn.filterByData = function(prop, val) {
    return this.filter(function() { 
        return jQuery(this).data(prop) == val;
    });
};

jQuery("#something").filterByData("somekey", "someval"); // finds it

StackOverflow question: http://stackoverflow.com/questions/4191386/jquery-how-to-find-an-element-based-on-a-data-attribute-value

Stackoverflow answer: http://stackoverflow.com/a/15651670/111266

jQuery AJAX

I’m sure there’s a perfectly good explanation for why the “always” function has two different signatures – one for requests that were successful and a different one for unsuccessful ones. But whatever their reason, they’re wrong. This is just plain stupid that you don’t know what kind of object the first and third parameters are. But they never asked me before they implemented it, so there’s nothing I can do about it…

var jqXHR = jQuery.ajax(ajaxParams);
jqXHR.done(function(data, textStatus, jqXHR) {});
jqXHR.fail(function(jqXHR, textStatus, errorThrown) {});
jqXHR.always(function(data|jqXHR, textStatus, jqXHR|errorThrown ) { });
The ajaxParams object contains lots of different options - the ones I generally use are as follows:
{
    // absolute or relative
    url: "/path/to/some/url",

    // will you be receiving "json", "html", "text", or "xml" back
    // 99% of the time I use json or html. If json, then the result will already
    // be parsed into a javascript object.
    dataType: "json",

    // what kind of data you are posting
    // 'application/x-www-form-urlencoded; charset=UTF-8' is the default
    contentType: "application/json; charset=utf-8",

    // This one is fun. If you're posting form-urlencoded, then this can 
    // either be a string representing the form post, like name=John&age=34
    // or it can be a javascript object and jQuery will automatically build
    // the string: { name: "John", age: 34 }
    // However, if you are posting JSON, then it needs to be a JSON string (not
    // an object, but the JSON representation of that object):
    // JSON.stringify({ name: "John", age: 34 })
    data: myData
}

There’s a lot more you can do with it – see the documentation.

Add EXIF date to photo

If you have a photo that you need to add a timestamp to the EXIF information, you can use the following .NET code. Apparently there are a ridiculous number of formats and standards which are incompatible with each other and conflicting, but from what I’ve found, this should cover regular use cases.

I’m sure there’s a better solution, but this requires that you have some unrelated image file that has the appropriate tags in it, in case your photo is missing the properties entirely. Not exactly a great solution, but it does seem to work.

static void Go(string oldFile, string newFile) {
    DateTime theDate = new DateTime(2009, 1, 15);

    Func<int, PropertyItem> getPropItem = id => {
        using (var image = Image.FromFile(@"c:\temp\IMG_6139.jpg")) { // Some unrelated image file that already has EXIF data
            return image.GetPropertyItem(id);
        }
    };

    using (var image = Image.FromFile(oldFile)) {
        PropertyItem propItem;
        int ID = 0x9004;
        try {
            propItem = image.GetPropertyItem(ID);
        } catch {
            propItem = getPropItem();
        }
        propItem.Value = Encoding.UTF8.GetBytes(theDate.ToString("yyyy\\:MM\\:dd HH\\:mm\\:ss") + "\0");
        image.SetPropertyItem(propItem);

        ID = 0x0132;
        try {
            propItem = image.GetPropertyItem(ID);
        } catch {
            propItem = getPropItem();
        }
        propItem.Value = Encoding.UTF8.GetBytes(theDate.ToString("yyyy\\:MM\\:dd HH\\:mm\\:ss") + "\0");
        image.SetPropertyItem(propItem);

        image.Save(newFile, ImageFormat.Jpeg);
    }
}

GZip with Pako

GZip is a super-fast compression technique, usable anywhere. In javascript, you can use the Pako library to compress your data.

Browser

<!DOCTYPE html>
<html>
<head>
    <title>Test Zip</title>
    </head>
    <body>
        <textarea id="inputText" style="width: 90%; height: 300px;"></textarea>
        <textarea id="outputText" style="width: 90%; height: 300px;"></textarea>
        <br>
        <input type="button" value="Go" onclick="go();">
        <script src="https://cdn.jsdelivr.net/pako/1.0.5/pako.min.js"></script>
        <script>
            function go() {
                console.log(document.getElementById("inputText").value.length);
                var compressedBytes = pako.gzip(document.getElementById("inputText").value);

                console.log("compressedBytes: " + compressedBytes.length);

                try {
                   var decompressedText = pako.ungzip(compressedBytes, { to: "string" });
                   document.getElementById("outputText").value = decompressedText;
                } catch (ex) {
                    console.log(ex);
                }
            }
        </script>
    </body>
</html>

NodeJS

const pako = require("pako");

const inputText = `旧団撃健載婚。西住姿要臣市未検成録止記悪転団。稿代江型集対著側込円号乳前爆下集広。稿界版陽更工振火国張産指天支日自果圧見。判社安団自外面用売神農更春日策現管野。線咲破面経速町日止満行直。集禁未化捕併投購加様塗抗録規開各在造能訴。断否界駅小安計注必式面柴。会継内欺受方添条武風復不断撃楽産支。

Hello world, this is a test!

知系締描文民法芸機各共作請始紹論明禁。者注省警権宅急今変埼問何文力。陣済当名屋康慣生用前更測説官類無。早高医教護告野滑綱混着特購加遅代逸通口戦。増再賀期木多合作就勢城析車人出済。訪任人改重江賞需農神書関。市理闘演礼場事付現運違有指的。心秋雄式作意切加県米外限竹真法鮮。足顔漁性倍何買真購話注私紙中際。`;

console.log(inputText);

const compressedBytes = pako.gzip(inputText);

console.log(`compressedBytes: ${compressedBytes.length}`);

try {
   const decompressedText = pako.ungzip(compressedBytes, { to: "string" });
   console.log(decompressedText);
} catch (ex) {
    console.log(ex);
}

Javascript GUID

If you need a GUID in Javascript, here are samples for building your own, either in a browser setting, or using NodeJS where you can easily add more features.

In Browser

function createGuid() {
    return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function (c) {
        var r = Math.random() * 16 | 0, v = c == "x" ? r : (r & 0x3 | 0x8);
        return v.toString(16);
    });
}

NodeJS

Technically this isn’t a proper GUID, but it is 16 cryptographically-pseudorandom bytes and works just fine if you need something that fits into a Guid/Uniqueidentifier or whatever else you’re calling it.

const crypto = require("crypto");

class Guid {

    constructor(hex) {
        if (typeof hex != "string") {
            throw "Must be a string";
        }
        
        var val = hex.replace(/[^0-9a-fA-F]/g, "").toLowerCase();
        if (val.length != 32) {
            throw "Must be 32 hex characters";
        }

        this._hex = val;
    }

    static newGuid() {

        var bytes = crypto.randomBytes(32);

        for (let i = 0; i < 32; ++i) {
            bytes[i] = (bytes[i] / 16) | 0;
        }

        let i = 0;
        let hex = "xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx".replace(/[xy]/g, function (c) {
            var r = bytes[i++], v = c == "x" ? r : (r & 0x3 | 0x8);
            return v.toString(16);
        });
        return new Guid(hex);
    }

    toString(format) {
        format = (format || "D");
        let sub = (start, end) => this._hex.substring(start, end);
        switch (format) {
            case "N": return this._hex.toUpperCase();
            case "n": return this._hex;
            case "D": return (`${sub(0, 8)}-${sub(8, 12)}-${sub(12, 16)}-${sub(16, 20)}-${sub(20, 32)}`).toUpperCase();
            case "d": return (`${sub(0, 8)}-${sub(8, 12)}-${sub(12, 16)}-${sub(16, 20)}-${sub(20, 32)}`);
        }
    }
}

module.exports = Guid;

Destructuring in javascript

Used to extract values from arrays and objects and assign to variables, making it easier for callers to see what they need to pass in to the function

"use strict";

// old way:
function PersonOld(options) {
    this.name = options.name;
    this.age = options.age;
    this.height = options.height;
}

// new way:
function PersonNew({ name, age, height }) {
    this.name = name;
    this.age = age;
    this.height = height;
}

// if you want it optional, then set to a default value:
function PersonOptional({ name, age, height } = { name: "Bill", age: 31, height: 23 }) {
    this.name = name;
    this.age = age;
    this.height = height;
}

// you can use defaults within the object:
function PersonDefaults({ name, age = 99, height }) {
    this.name = name;
    this.age = age;
    this.height = height;
}

var options = {name: "John", age: 34, height: 72};
var personOld = new PersonOld(options);
var personNew = new PersonNew(options);

// same output
console.log(personOld);
console.log(personNew);

console.log(new PersonOptional());
console.log(new PersonDefaults({name: "Barry", height: 53}));

Browser Support

No IE support, requires a real browser

SHA hash in NodeJS

const crypto = require("crypto");
const filename = process.argv[2];
const fs = require("fs");

const hash = crypto.createHash("sha256");

const input = fs.createReadStream(filename);
input.on("readable", () => {
    const data = input.read();
    if (data) {
        hash.update(data);
    } else {
        console.log(`${hash.digest("hex")} ${filename}`);
    }
});

Reference

https://nodejs.org/api/crypto.html

Javascript does element have class

If you need to check if an element has a given class, there are a few ways. jQuery has a hasClass function. If you’re limited to users using IE10 or a real browser, then you can use the Element.classList property.

But if you have to cater to old IE users, and aren’t using jQuery, then you can use the following to check to see if an element has a class. Just put this in one of your header script files, before anything that would use it:

if (!Object.prototype.hasClass) {
    Object.prototype.hasClass = function (x) {
        if (!this.className || !this.className.indexOf) {
            return false;
        }
        return (" " + this.className + " ").indexOf(" " + x + " ") >= 0;
    };
}

var elem = document.getElementById("div1");
var hasRed = elem.hasClass("red");