My JavaScript book is out! Don't miss the opportunity to upgrade your beginner or average dev skills.
Showing posts with label Ajax. Show all posts
Showing posts with label Ajax. Show all posts

Monday, September 20, 2010

Fragment and Vertex Shaders: My Way To Load

I have finally received the fifth and amazing version of the OpenGL SuperBible book and I have already started digging into it, really well done for what I can tell.

The book is mainly focused on "real OpenGL development", something surely more suitable for tough C/C++ developers rather than Web Monkeys like me but since the book includes an OpenGL ES 2.0 related part, and since latter spec is basically what we can find in WebGL, it's always better start learning what's next knowing history and background of the used technology ... and here I am :-)

Something Already Wrong

I am not completely sure about "who started this techniques", John Resig with his micro template inside script nodes with a type text/html may be the indirect responsible for a sort of growing "monster" we can see in almost every WebGL related example ...

HTML Embedded Shaders, the Web 3.0 NO-GO

Let me pass the therm, but 3D websites are not that far away from reality. Minefield, Chrome, WebKit, and probably others, are working hard with Khronos and WebGL and I do believe official support will come later this year with next browsers releases ( ... hoping IE9 final won't "block the world" avoiding WebGL support ... )
Back in the topic, the common examples technique is a step backward in 1999 where runtime loading did not exists (aka: Ajax or scripts injection) and an HTML page was a trash bin for any kind of rubbish.

A Better Technique

We are in 2010 and we want use latest technologies, so why should we use old and deprecated one? Let me show this simple getShader function alternative:

// our shaders base path
loadShaders.base = "shader/";

// our shaders loader
function loadShaders(gl, shaders, callback) {
// (C) WebReflection - Mit Style License
function onreadystatechange() {
var
xhr = this,
i = xhr.i
;
if (xhr.readyState == 4) {
shaders[i] = gl.createShader(
shaders[i].slice(0, 2) == "fs" ?
gl.FRAGMENT_SHADER :
gl.VERTEX_SHADER
);
gl.shaderSource(shaders[i], xhr.responseText);
gl.compileShader(shaders[i]);
if (!gl.getShaderParameter(shaders[i], gl.COMPILE_STATUS))
throw gl.getShaderInfoLog(shaders[i])
;
!--length && typeof callback == "function" && callback(shaders);
}
}
for (var
shaders = [].concat(shaders),
asynchronous = !!callback,
i = shaders.length,
length = i,
xhr;
i--;
) {
(xhr = new XMLHttpRequest).i = i;
xhr.open("get", loadShaders.base + shaders[i] + ".c", asynchronous);
if (asynchronous) {
xhr.onreadystatechange = onreadystatechange;
}
xhr.send(null);
onreadystatechange.call(xhr);
}
return shaders;
}

With above minifier friendly function we can avoid shaders on the page. The natural advantage is that we can properly organize shaders in a structure like this one:

root

shader

vs // vertex shaders

ShaderName.c

fs // fragment shaders

FragmentName.c

Having a proper extension means that we can edit these files with highlighted syntax and we can edit these files a part, without looking for id or scripts inside an HTML page. Fragment and Vertex Shaders are our "application templates", so why on earth should we include them in the layout? Via loadShaders we can use cached shaders, share shaders, etc etc, and here some usage example.


vra gl = canvas.getContext("experimental-webgl");

// synchronous order, one shader
var myFragmentShader = loadShaders(gl, "fs/myFragment");

// synch, more shaders
var shaders = loadShaders(gl, [
"vs/what",
"vs/ever"
]);
// [whatShader, everShader]


// asynchronous loading
loadShaders(gl, ["fs/what", "vs/ever"], function (shaders) {
var
fragmentWhatShader = shaders[0],
vertexEverShader = shaders[1];
;
});


// asynch in "don't care mode, just cache them"
loadShaders(gl, ["fs/what", "vs/ever"], true);



Features


  • Synchronous or Asynchronous loading

  • Multiple Loads (unordered compilation for each result)

  • Ordered Results, (order respected accordingly with paths)

If I have forgotten something please point it out but even more please, whatever you like or not this solution, do not include shaders in your HTML page (OK, OK, gotcha, for some example it may make life easier but ... you know how people get examples ... right?)

Cheers :)

Saturday, September 05, 2009

Formaldehyde - Ajax PHP Error Debugger

I am proud to announce my last Web 2.Next creation, Formaldehyde, the most simple, lightweight, scalable, and complete (for its simplicity) Ajax and PHP Error Debugger.
I described everything in its dedicated Google Code Project Page, but just to summarize without many other words, this is the common deployment situation:


while this is what's up with a single formaldehyde.php file inclusion:


I hope you will like it, can't wait for some comment :geek:

Sunday, May 24, 2009

Basic Ajax Twitter Application

Yesterday I posted a work in progress of a Full PHP Ajax Proxy, today I would like to show you what you could do with that file.

Twitter API Directly via Ajax


Thanks to basic realm authentication support, it is extremely simple to create your own twitter app in your home page. Here the example:

<style type="text/css">
div.twit {
font-family: "Lucida Grande", "Lucida Sans Unicode", "Lucida Sans", Verdana, Tahoma, sans-serif;
width: 320px;
}
div.twit h3,
div.twit h4 {
margin: 0;
padding: 0;
}
div.twit h3 {
font-size: 10pt;
color: blue;
}
div.twit h4 {
font-size: 7pt;
font-weight: normal;
text-align: right;
color: #999;
}
div.twit span {
font-size: 8pt;
}
</style>
<script type="text/javascript" src="http://vice-versa.googlecode.com/svn/trunk/build/vice-versa.min.js"></script>
<script type="text/javascript">
function addIntoList(info, container){
var div = document.createElement('<div class="twit"></div>'),
h3 = div.appendChild(document.createElement("h3")),
span= div.appendChild(document.createElement("span")),
h4 = div.appendChild(document.createElement("h4"))
;
h3.innerText = info.user.screen_name;
h4.innerText = info.created_at.substring(0, 19);
span.innerHTML = info.text.replace(/([a-zA-Z]+:\/\/[^\s]+)/g, '<a href="$1">$1</a>');
container.insertBefore(div, container.firstChild);
};

onload = function(){
var xhr = new XMLHttpRequest;
xhr.open(
"GET", "proxy.php?url=" +
"http://twitter.com/statuses/friends_timeline.json" + "?_=" + Math.random(),
false,
"YOURNAME",
"YOURPASS"
);
xhr.send(null);
if(199 < xhr.status && xhr.status < 400){
document.body.innerHTML = "";
for(var
list = Function("return " + xhr.responseText)().reverse(),
length = list.length,
i = 0;
i < length; ++i
)
addIntoList(list[i], document.body);
};
setTimeout(onload, 10000); // again after 10 seconds
};
</script>


Be Careful!!!


First of all it is not a good idea to put user and password directly in your page source code, so try locally but do not put online. Secondly, I did not say anything yet about side effects and security problems related to this PHP proxy I am working on ....so, as I wrote in the other post source, "do not try at home"

PHP Full Proxy ... A Work In Progress

Something "truly dangerous" to play with: a proxy file able to understand everything via XMLHttpRequest, enabling any sort of cross site requests (no COMET yet, it could arrive soon).
Dunno even why I am posting this "little monster", but I am sure a lot of people would like to have "access to the entire web" simply using Ajax, and nothing else, something like this:

function website_exists(url){
var xhr = new XMLHttpRequest;
xhr.open("HEAD", "proxy.php?url=" + (url), false);
xhr.send(null);
return 199 < xhr.status && xhr.status < 400;
};

if(website_exists("http://gogle.com"))
alert("Of Course, It's Big G!");


WebReflection PHP Proxy



<?php

/** XMLHttpRequest PHP Proxy
* @author Andrea Giammarchi
* @blog http://webreflection.blogspot.com/
* @license Mit Style License
* @requires curl and Apache webserver
* @description basic authentication, GET, POST, HEAD, PUT, DELETE, others requests types.
* Nothing to do on the client side, except put "proxy.php?url=" as request prefix.
* The rest should be like normal in-server interaction
* @note DON'T TRY AT HOME
*/

// if no url has been provided, exit
if(!isset($_GET['url'])){
header('HTTP/1.1 400 Bad Request');
header('X-Proxy-Error: no url');
exit;
}

// work in progress
/* without Apache ... requires alternatives for Authorization and other stuff not in $_SERVER
if(!function_exists('getallheaders')){
function getallheaders(){
$headers= array();
foreach($_SERVER as $key => $value){
if(0 === strpos($key, 'HTTP_'))
$headers[str_replace(' ', '-', ucwords(str_replace('_', ' ', strtolower(substr($key, 5)))))] = $value;
}
return $headers;
}
}
// */

// GET, POST, PUT, HEAD, DELETE, ect ...
$method = $_SERVER['REQUEST_METHOD'];

// curl headers array
$headers= array();
foreach(getallheaders() as $key => $value)
$headers[] = $key.': '.$value;

// curl options
$opts = array(
CURLOPT_HEADER => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_BINARYTRANSFER => true,
CURLOPT_CUSTOMREQUEST => $method,
CURLOPT_HTTPHEADER => $headers
);

// if request is post ...
if($method === 'POST'){
// populate the array of keys/values to send
$headers = array();
foreach($_POST as $key => $value)
$headers[] = rawurlencode($key).'='.rawurlencode($value);
$opts[CURLOPT_POST] = true;
$opts[CURLOPT_POSTFIELDS] = implode('&', $headers);
}

// if it is a basic authorization request ...
if(isset($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW'])){
// create user and pass parameters to send
$opts[CURLOPT_HTTPAUTH] = CURLAUTH_BASIC;
$opts[CURLOPT_PROXYUSERPWD] = '['.
rawurlencode($_SERVER['PHP_AUTH_USER'])
.']:['.
rawurlencode($_SERVER['PHP_AUTH_PW'])
.']'
;
}

// init curl session
$call = $session = curl_init(substr($_SERVER['QUERY_STRING'], 4));

// set all options
curl_setopt_array($call, $opts);

// clear unnecessary variables
unset($opts);
unset($headers);

// retrieve the output
$result = explode(PHP_EOL, curl_exec($call));

// nothing else to do so far (this version is not compatible with COMET)
curl_close($call);

// for each returned information ...
for($i = 0, $length = count($result), $sent = array(); $i < $length; ++$i){
$value = $result[$i];

// if all headers has been sent ...
if($value === '')
// send the output
exit(implode(PHP_EOL, array_splice($result, ++$i)));
else {
// ... or send the header (do not overwrite if already sent)
$tmp = explode(':', $value);
header($value, !isset($sent[strtolower($tmp[0])]));
}
}

?>


Have fun exploring the net ;)

Friday, May 15, 2009

Ajax better than Flash AMF? Optimized homogeneous collections

I found this dojo related post extremely interesting and I instantly though in our application we have similar JSON structure which is effectively redundant and, for thousands of database rows, it slows down visualization responsiveness (not that much in intranet, enough via internet).

JSON.hpack


I am developing on spare time an "all web languages" homogeneous collection packer in order to speed up client/server interaction specially for database results where the column name could be considered as a key and each field in the same column as a row.

So far I created a JavaScript version which seems to work perfectly and results are quite impressive. For example, the total size of the 5000 items used in that dojo article switched from 37.23Kb to 26.27Kb, gzipped size, while number of characters to send / retrieve are 776133 against 99575.

As example, the host where I put the initial test does not allow more than 50Kb as uploaded data, this means that without JSON.hpack is not even possible to consider to move 5000 of rows.

The reason it could be faster is that JSON.stringify, the good old Douglas Crockford version, is not that fast as we think ( not comparable with native IE8 JSON.stringify ) and if the given object is smaller, obviously data to parse is less as well.

Here you can test the first JSON.hpack test page.

The project is not hosted yet so to know more about the simple API please read the incomplete (code speaking stable though) source code.

I wonder how many people would be interested in their favorite server side language implementation, I can create the PHP, Python, and C#, but I am looking for Java, Ruby, and if possible other languages as well. The logic truly simple so it is just a matter of time ... anyone fancy to contribute?

Monday, June 30, 2008

Ajax or JavaScript sub domain request

Some time we could have a situation like this one:

  • a site in a sub domain, called http://a.test.com

  • another site in another sub domain, called http://b.test.com

  • of course, a generic main site, called http://test.com


A common problem between one or more sub domains, is the possibility to use, or call, scripts in the main domain, because of security restrictions.

The simplest solution is to force, in the client side, the document.domain, specifying the common one, i.e.

dcument.domain = "test.com";

In this way you can add, for example, an iframe, and read its content, or use parent window object from the iframe that points, for example, to http://test.com

There are different reasons to do it, and one of them, is the ability to share a global, or common, space, between every sub domain, performing Ajax requests or whatever else we need.

This object, saved in http://test.com, and loaded by every other sub domain, like "a" or "b", could solve in a really simple way this problem.

CrossDomain = function(){
// webreflection.blogspot.com - Mit Style License
var uid = 0, iframe, unset;
return {
get:function(location){
for(var
search = location.search.substring(1).split("&"),
i = 0,
length = search.length,
value;
i < length;
i++
){
value = search[i].split("=");
if(value[0] === "uid"){
search = this[value[1]];
delete this[value[1]];
return search;
}
}
},
unset:function(){
document.domain = unset;
return this;
},
send:function(domain, value){
var id = Math.random() + "." + uid++;
this[id] = value;
if(!iframe){
(document.body || document.documentElement).appendChild(
iframe = document.createElement("iframe")
).style.position = "absolute";
iframe.style.width = iframe.style.height = "1px";
iframe.style.top = iframe.style.left = "-10000px";
};
iframe.src = domain + (~domain.indexOf("?") ? "&" : "?" ) + "uid=" + id;
return this;
},
set:function(){
if(!unset)
unset = document.domain;
document.domain = unset.split(".").slice(1).join(".");
return this;
}
}
}();

Its usage is really simple, and here there is an example from, as example, a.test.com:

// callback to call (if it is the same for other sub domains
// put them in an external file
function showMessage(message){
document.body.appendChild(document.createTextNode(message));
};

// set domain name, i.e. test.com
CrossDomain.set();

// call the page sending some value, every kind of value we need
CrossDomain.send("http://test.com/", ["a", "b", "c"]);


On the other file, the index inside test.com, we could simply use this piece of code:

// set domain name, manually or including CrossDomain
// and using its set method
document.domain = "test.com";

// call, simply, the parent function
// using the get method to avoid conflicts
// with multiple requestes
parent.showMessage(
"You sent me " + parent.CrossDomain.get(location)
); // will alert a,b,c

The good thing of this simple created bridge, is that once you are in test.com, from a.test.com or b.test.com, you can call via Ajax every page inside test.com, sharing a single folder inside the global domain, instead of copy the same stuff everywhere in other domains.

// set domain name
document.domain = "test.com";

// simple Ajax request
var xhr = new XMLHttpRequest;
xhr.open("get", "?demo", true);
xhr.onreadystatechange = function(){
if(xhr.readyState === 4)
parent.showMessage([
"You sent me " + parent.CrossDomain.get(location),
"And AJAX said " + xhr.responseText
].join("\n"));
};
xhr.send(null);


I hope this will be useful :)