Tuesday, November 09, 2010
So, sometime in 2000 I bought a Humanscale Freedom Headrest task chair online for around $1300.
I have always and still do put in long sessions at the keyboard and this chair is amazing. But this is not a review of the chair itself.
In 2004 I went on a 2 year sabbatical in Asia and while liquidating I sold (pawned) my chair to a good friend.
When I got back my oversized buddy had put some extreme wear on the chair resulting in some breakage on the arms but in that the chair has a lifetime warrantee on hard parts I reclaimed the chair without concern.
I wrote down the serial number and headed down to the local Humanscale dearler, Copenhagen, talked to the office manager for around 5 minutes, left the serial number and in 2 weeks had brand new arm.
Cut to Oct 2010.
The chair, after 10 years of solid use, began to creak a bit and lube as I liked I could not quiet the chair. Very puzzling. The mystery was solved one night as I was reclining in the chair and BANG! I was lying almost horizontal.
The cast base had developed stress fractures, which were responsible for the creaks resulting from being slightly out of alignment. These fractures, on both sides of the base, had finally given way.
I emailed Humanscale with an RMA request and while the rep I ultimately connected with did not understand 'RMA' she eventually asked me to email some pictures of the chair.
2 days later I got an email requesting the serial number and a shipping address.
3 weeks later (today!) FedEx knocks on the door and drops off my new chair.
I cannot say enough about Humanscale's warrantee and customer service.
This chair is one of the best buying decisions I have ever made.
Sunday, October 17, 2010
ok, so I said it.
After spending 8 hours trying to figure out why some fairly complex but carefully crafted and instrumented javascript code was working on in all browsers (including chrome) except Safari, I stumbled across another native JSON bug.
Safari JSON coult not serialize a simple object that json2.js (and most other browser's native stringify) has no problem with, throws and kills the script.
I will likely break the case out and submit a bug but...
Sky Sanders says
"Forget the sunscreen, if you remember anything I have ever said, remember that considering the inconsistency and presence of confirmed bugs in multiple browser native JSON implementations, when less than trivial JSON functionality is required, include json2.js and remove the polite check at the top, forcing an overwrite of .stringify and .parse."
Monday, October 11, 2010
The window.name transport is a new technique for secure cross-domain browser based data transfer, and can be utilized for creating secure mashups with untrusted sources. window.name is implemented in Dojo in the new dojox.io.windowName module, and it is very easy to make web services available through the window.name protocol. window.name works by loading a cross-domain HTML file in an iframe. The HTML file then sets its window.name to the string content that should be delivered to the requester. The requester can then retrieve the window.name value as the response. The requested resource never has access to the requester’s environment (JavaScript variables, cookies, and DOM) Kris Zyp http://www.sitepen.com/blog/2008/07/22/windowname-transport/
You can read the above or grok this: window.name is the only property on an iframe that is not locked down
for xdomain requests. similar to jsonp, if a server returns a script that sets window.name to a value,
that value can be read by the calling page.
Starting with the promise of xdr communication, I started with DojoToolkit's dojox.io.windowName. It works as advertised, but in IE the clicking and spinning is just too much to bear.
Then I google-stumbled into
Between the three of them and a little elbow grease, I arrived at a very simple xdr with no practical data length restrictions (browser JS engines start to throw OOM exceptions at around 10mb of json) that works on all of the 8 browsers installed upon my primary windows development machine.
PROS:
- Quick and clean
- More secure than XHR
- Transparent caching and cookies
- .....
CONS:
- No error feedback except empty data. The server must wrap itself tight and return errors in JSON with a 200 OK
- No headers
- ...
Conclusion:
While the this functionality, whether the simple example I present here or a more refined version such as Dojox.io, ultimately does not meet the requirements for which I spike it, I anticipate that it will come in handy so here is an implementation that you can try out.
window.name.client.htm
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
</head>
<body>
<script type="text/javascript">
var NameWindowTransport = function(callback) {
/// <summary>Provides, with a little cooperation from the foreign server, cross domain communication</summary>
/// <param name="callback" type="function(data)"></param>
var _callback = callback;
var doc, iframe;
if ("ActiveXObject" in window) {
// IE: we could just append an iframe element but the click and spinner
// would make it unbearable. spin up a disconnected browser and use it
doc = new ActiveXObject("htmlfile");
doc.open();
doc.write("<html><body><iframe id='iframe' scr='about:blank' style='visibility:hidden;' width='0' height='0'></iframe></body></html>");
doc.close();
iframe = doc.getElementById('iframe');
} else {
// for nice borwsers, this is easy
doc = document;
iframe = doc.createElement('iframe');
iframe.style.visibility = "hidden";
iframe.style.width = "0";
iframe.style.height = "0";
iframe.setAttribute("src", "about:blank");
doc.body.appendChild(iframe);
}
iframe.onload = function() {
// data is transfered in window.name
// apparently no size limitations
var data = iframe.contentWindow.name;
// the only error detection is empty data. 8-\
// so wrap your server calls tight and return
// error json in a window.name envelope
if (data && _callback) {
_callback(data);
}
};
// would be on prototype in a production script, but this is simply a POC for
// proposing integrating the less annoying ActiveX hack into Dojox.io
this.beginRequest = function(url, callback) {
_callback = callback || _callback;
iframe.setAttribute("src", url);
};
}
// unlike an xhr, we need a document do work on, so we can put the script in the
// body to ensure we have one to work with
var nwt = new NameWindowTransport(function(data) {
alert(data);
});
nwt.beginRequest("Window.Name.Server.aspx");
</script>
</body>
</html>
window.client.server.htm
<html>
<script type='text/javascript'>
window.name = document.getElementsByTagName('script')[0].innerHTML.match(/temp\s*=([\w\W]*)/)[1];
temp = { foo: 'bar' } //<-- this is the data
</script>
</html>
window.client.server.aspx
<%@ Page Language="C#" %>
<%@ Import Namespace="System.Web.Script.Serialization" %>
<script runat="server">
protected void Page_Load(object sender, EventArgs e)
{
Response.Write("<html><scr" + @"ipt type='text/javascript'>window.name=document.getElementsByTagName('script')[0].innerHTML.match(/temp\s*=([\w\W]*)/)[1];temp=");
var data = new { foo = "bar" };
Response.Write(new JavaScriptSerializer().Serialize(data));
Response.Write("</scr" + "ipt></html>");
}
</script>
Sunday, September 19, 2010
I recently had need to copy a grid to clipboard as CSV and after a brief googling concluded that I needed to roll my own.
Here it is.
//
function jsonToCSV(sourceRows, omitHeader, fieldsToIgnore)
{
/// <summary>
/// Converts an array of JSON objects to CSV.
///</summary>
/// <param name="sourceRows" type="Object[]">
/// An array of objects containing a single level of scalar fields.
/// e.g. [{ foo: "bar", fu: true }, { foo: "baarbar", fu: false }]
///
/// The first element in the array will define the header row.
/// </param>
/// <param name="omitHeader" type="Boolean" optional="true">if true, header row is not emitted</param>
/// <param name="fieldsToIgnore" type="Map" optional="true">a map of fields to ignore e.g. { field1:1,field4:1 }</param>
/// <returns type="String"></returns>
/// <author name="sky sanders" contact="http://skysanders.net/subtext" date="2010-09-19"/>
function quote(value)
{
return '"' + value.replace(/"/g, '""').replace(/\r/g, "\\r").replace(/\n/g, "\\b") + '"';
};
function pad(n)
{
return n < 10 ? '0' + n : n;
};
var header = "";
var rows = "";
var headerComplete = false;
for (var i = 0; i < sourceRows.length; i++)
{
var firstElement = true;
var row = "";
for (var key in sourceRows[i])
{
if (sourceRows[i].hasOwnProperty(key))
{
if (fieldsToIgnore && (key in fieldsToIgnore))
{
continue;
}
if (!headerComplete)
{
if (!firstElement)
{
header = header.concat(", ");
};
header = header.concat(key);
};
if (!firstElement)
{
row = row.concat(", ");
};
var value = sourceRows[i][key];
if (typeof value != 'undefined' && value !== null)
{
if (value instanceof Date)
{
var dateResult = value.getUTCFullYear() + '-'
+ pad(value.getUTCMonth() + 1) + '-'
+ pad(value.getUTCDate()) + 'T'
+ pad(value.getUTCHours()) + ':'
+ pad(value.getUTCMinutes()) + ':'
+ pad(value.getUTCSeconds()) + 'Z';
row = row.concat(dateResult);
}
else if ((value instanceof Boolean) || !isNaN(value))
{
row = row.concat(value.valueOf());
}
else
{
row = row.concat(quote(value.valueOf()));
}
}
firstElement = false;
}
}
rows = rows.concat(row).concat("\r\n");
headerComplete = true;
}
return omitHeader ? rows : header.concat("\r\n").concat(rows);
};
//
Saturday, August 21, 2010
Almost a year ago, during my WCF Date<-->JSON Date adventure, I discovered a bug in the Firefox native JSON implementation where in the stringify replacer does not behave properly.
I and several other people bugged it on bugzilla, but it still hasn't been fixed.
So, you can not trust Firefox native JSON stringify.
What is the solution?
If you are using JSON, you are (or should be) loading json2.js regardless and letting it defer to native implementations when they are present. In the case of Firefox, this leaves you flat, so here is a feature detecting prefix for json2.js.
(function() {
// ff STILL has buggy native stringify
// https://bugzilla.mozilla.org/show_bug.cgi?id=548462
// we will use feature detection to determine if we need
// to replace the native stringify
if (typeof (JSON) == 'undefined') {
JSON = {};
};
if (typeof JSON.stringify == 'function') {
var s = JSON.stringify({ date: new Date(2009, 1, 1, 10, 10) }, function(key, value) {
if (key == "date") {
return String("SERIALIZED");
};
return value;
});
var sobj = JSON.parse(s);
if (sobj.date !== "SERIALIZED") {
JSON.stringify = {};
};
};
})();
// insert json2.js here
(function() {
// ensure bug patched
if (typeof JSON.stringify == 'function') {
var s = JSON.stringify({ date: new Date(2009, 1, 1, 10, 10) }, function(key, value) {
if (key == "date") {
return String("SERIALIZED");
};
return value;
});
var sobj = JSON.parse(s);
if (sobj.date !== "SERIALIZED") {
throw new Error("JSON STILL BROKEN");
};
};
})();