Your browser doesn't support the features required by impress.js, so you are presented with a simplified version of this presentation.

For the best experience please use the latest Chrome, Safari, or Firefox browser.

Programming
the
Diffuse
HOP
WEB
Manuel Serrano
image/svg+xml
thumbtack.pngthumbtack.pngst-pierre-2005.png
thumbtack.pngthumbtack.pngst-pierre-2013.png
digital equipments
+network everywhere
+sensors everywhere
+big/open/many data
=Diffuse Computing
OUR INFRASTRUCTURE:
THE WEB!
HTML4.01 XHTML CSS2.1 JavaScript XSLT ActionScript MathML SVG PHP ASP JSP GWT XML OAuth WebSockets RSS Rails CoffeeScript Dart WebGL HTML5 CSS3
TOO
MANY
TECHNOLOGIES
XUL TypeScript EcmaScript Jitty DOM JSON SOAP XML HTTP Rest Ajax Silverlight RDF Nginx Lightttpd Apache Tomcat CGI DHTML Rest Comet UDDI WSDL Atom
Problem #1
SERVER SIDE
CLIENT SIDE
ASYMMETRIC
Problem #2
our proposal
Multitier Programming
THE WEB
TECHNOLOGIC
html'89 [cern]
http'90 [cern]
url'92 [cern]
cgi'93 [ncsa]
https'94 [netscape]
netscape'94 [netscape]
javascript s.'94 [netscape]
javascript c.'95 [netscape]
ie1'95 [microsoft]
html2'95 [rfc 1866]
http1.0'95 [rfc 1945]
css'96 [w3c]
http1.1'96 [rfc 2616]
php'95 [zend]
http auth'96 [rfc 2617]
opera'96 [opera]
apache'96 [apache]
flash'96 [macromedia]
gecko'97 [netscape]
dhtml'97 [netscape + microsoft]
css2'98 [w3c]
svg'98 [w3c]
mathml'98 [w3c]
soap'98 [microsoft]
khtml'98 [kde]
html 4.01'99 [w3c]
xhr (ajax)'99 [microsoft]
httpu'99 [ietf]
wsdl'00 [ibm]
dom'00 [w3c]
xhtml'00 [w3c]
webkit'02 [apple]
json'02 [State Software inc]
html 5'04 [whatwg]
canvas'04 [apple]
jquery'06
webgl'06 [mozilla]
chrome'08 [google]
mobile'09
websocket'10[google,apple]
htmlHyperText Markup Language
httpHypertext Transfer Protocol
urlUniform resource locator
cgiCommon Gateway Interface
phpPersonal Home Page/Hypertext Preprocessor
svgScalable Vector Graphics
mathmlMathematical markup languages
soap Simple Object Access Protocol
xhrXmlHTTPRequest (ajax)
LIGHTTPD0.08
JETTY0.6
VLC0.6
EMACS1.6
GCC6.2
CHROMIUM7
FIREFOX9
LINUX15.5
The early Web
image/svg+xml Layer 1
client
image/svg+xml
html
image/svg+xml
html
image/svg+xml Layer 1
server #1
image/svg+xml Layer 1
server #2
the server is the program
The Web 1.0
image/svg+xml Layer 1
client
image/svg+xml
html
image/svg+xml Layer 1
server #1
the program is in the server
The Web 1.0
the BACK button
Christian Queinnec. The influence of browsers on evaluators or, continuations to program web servers, ICFP'2000
netscape-bar.png
The Web 2.0
image/svg+xml Layer 1
client
JS
JS
image/svg+xml
JS
image/svg+xml
image/svg+xml Layer 1
server
image/svg+xml Layer 1
image/svg+xml Layer 1
image/svg+xml Layer 1
image/svg+xml Layer 1
image/svg+xml Layer 1
image/svg+xml Layer 1
the program is in the server and in the client
a single language
H::=JavaScript
|<tag> {name: H, ... ,H }[</tag>]
|sservice [id](id, ...){H; ...}
|~{H}
|~${H}
$~$~$~$~$~$~$~$~$~$~$~$~$~$~$~$~$~$~$ $~$~$~$~$~$~$~$~$~$~$~$~$~$~$~$~$~$~$ $~$~$~$~$~$~$~$~$~$~$~$~$~$~$~$~$~$~$ $~$~$~$~$~$~$~$~$~$~$~$~$~$~$~$~$~$~$
MULTITIER
$~$~$~$~$~$~$~$~$~$~$~$~$~$~$~$~$~$~$ $~$~$~$~$~$~$~$~$~$~$~$~$~$~$~$~$~$~$ $~$~$~$~$~$~$~$~$~$~$~$~$~$~$~$~$~$~$ $~$~$~$~$~$~$~$~$~$~$~$~$~$~$~$~$~$~$
client-side mark
Hop structured programming
more abstractions...
injecting server-side code
code generation
server-side dom
   <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" 
     "http://www.w3.org/TR/html4/strict.dtd">
   <HTML>
     <HEAD>
       <META http-equiv="Content-Type" content="text/html">
       <SCRIPT src="fib.js" type="application/javascript"/>
     </HEAD>
     <BODY>
       <TABLE>
         <TR>   <TD    onclick="alert('fib(1)='+fib(1))">1</TD>  </TR>
         <TR>   <TD    onclick="alert('fib(2)='+fib(2))">2</TD>  </TR>
         <TR>   <TD    onclick="alert('fib(3)='+fib(3))">3</TD>  </TR>
       </TABLE>
     </BODY>
   </HTML>


   <HTML> {
     <HEAD> { jscript: "fib.js" }



     <BODY> {
       <TABLE> {
         <TR> { <TD> { onclick: ~{alert('fib(1)='+fib(1))}, 1} },
         <TR> { <TD> { onclick: ~{alert('fib(2)='+fib(2))}, 2} },
         <TR> { <TD> { onclick: ~{alert('fib(3)='+fib(3))}, 3} }
     } </TABLE>
   } </BODY>
 } </HTML>
service fibonacci() {
 return
   <HTML> {
     <HEAD> { jscript: "fib.js" }



     <BODY> {
       <TABLE> {
         <TR> { <TD> { onclick: ~{alert('fib(1)='+fib(1))}, 1} },
         <TR> { <TD> { onclick: ~{alert('fib(2)='+fib(2))}, 2} },
         <TR> { <TD> { onclick: ~{alert('fib(3)='+fib(3))}, 3} }
     } </TABLE>
   } </BODY>
 } </HTML>
}
service fibonacci() {
 return
   <HTML> {
     <HEAD> { jscript: "fib.js" }



     <BODY> {
       <TABLE> {
         <TRFIB> { onclick: ~{alert('fib(1)='+fib(1))}, 1},
         <TRFIB> { onclick: ~{alert('fib(2)='+fib(2))}, 2},
         <TRFIB> { onclick: ~{alert('fib(3)='+fib(3))}, 3}
     } </TABLE>
   } </BODY>
 } </HTML>
}

function TRFIB( attrs, nodes ) {
   return <TR> {
      <TD> { onclick: attrs.onclick, nodes }
   };
}
service fibonacci() {
 return
   <HTML> {
     <HEAD> { jscript: "fib.js" }



     <BODY> {
       <TABLE> {
         <TRFIB> { 1 },
         <TRFIB> { 2 },
         <TRFIB> { 3 }
     } </TABLE>
   } </BODY>
 } </HTML>
}

function TRFIB( _, i ) {
   return <TR> {
      <TD> {
	 onclick: ~{ alert( ${"fib("+i+")="} + fib(${i}) ), i }
      } 
   }
}
service fibonacci() {
 return
   <HTML> {
     <HEAD> { jscript: "fib.js" }



     <BODY> {
       <TABLE> {
          [1, 2, 3].map( function( i ) { return <TRFIB> { i } } );
       } </TABLE>
     } </BODY>
   } </HTML>
}



function TRFIB( _, i ) {
   return <TR> {
      <TD> {
	 onclick: ~{ alert( ${"fib(" + i + ")="} + fib(${i}) ), i }
      } 
   }
}
service fibonacci() {
 return
   <HTML> {
     <HEAD> { jscript: "fib.js" }



     <BODY> {
        <TABLE> {
          cache 
        } </TABLE>
     } </BODY>
   } </HTML>
}

var cache = [1, 2, 3].map( function( i ) { return <TRFIB> { i } } ) );

function TRFIB( _, i ) {
   return <TR> {
      <TD> {
	 onclick: ~{ alert( ${"fib(" + i + ")="} + fib(${i}) ), i }
      } 
   }
}
client-side program
=
server-side value
REUSE
COMPATIBILITY
CONFORMITY
JAVASCRIPT
client side libraries
HTML + CSS
extended uis
WEB SERVICES
libraries and data structures
<HEAD> { jscript: "http://..flipcounter.js" }
type flipcounter = "div[data-hss-tag=flipcounter]"
function FLIPCOUNTER( attrs ) {
   return <DIV> {
      "data-hss-tag": "flipcounter",
      id: attrs.id,
      ~{ window.addEventListener( "load", 
	  function() {
            return new flipCounter( ${attrs.id} )
          } ) } } }
google.png
var wmap = <SVG:IMG> { src: "http://wikipedia.org/..."};
var balloon = "HTML4.01";
   <DIV> { wmap, balloon,
      ~{ ${wmap}.addEventListener( "load",
         function (e) {
            return canvas.Arrow(
                     ${balloon}.boundingBox(),
                     ${wmap}.france.boundingBox())})}}
wikipedia.png
image/svg+xml Layer 1
image/svg+xml Layer 1
image/svg+xml Layer 1
SERVICES
Services bind URLS
URL
+function
=service
urlbar.png
service fibonacci( { n: 10 } ) {
   return <HTML> {
      <TABLE> {
	 [1, 2, ..., 40]
	    .slice( 0, n )
	    .map( function( i ) {
	       return <TR> {
		  <TD> { "fib(" i ")=" },
		  <TD> { fib( i ) } } </TR> } ) } </TABLE> } }
Service invocations
iframe.src = ${fibonacci}( {n: 10} );
img.src = ${getMandelbrotImage}( 1024 );
${fib}( 10 )
   .post( function( el ) { body.appendChild( el ) }
Service marshalling
${fibarray}( 10 )
   .post( function( arr ) {
      document.body.appendChild(
	 <TABLE> {
	    arr.map( function( i ) {
	       return <TR> {
		  <TD> { i.key },
		  <TD> { i.data }
	       } } ) } ) };

service fibarray( n ) {
   return [ 1, 2, ..., 40 ]
      .slice( 0, n )
      .map( function( i ) {
	 return {
	    key: i,
	    data: fib( i )
	 } } ) }
prev.pngnext.png
<TABLE> {
   <TR> {
      <TD> {
	 onclick: ~{ ${slidePrev}.post() },
	 <IMG> { :src "prev.png" } },
      <TD> {
	 onclick: ~{ ${slideNext}.post() },
	 <IMG> { :src "next.png" } }
   }
}

service slideNext() { ... }
service slidePrev() { ... }
image/svg+xml Layer 1
image/svg+xml Layer 1
image/svg+xml Layer 1
MOUSEEVENTS
KEYBOARDEVENTS
WINDOWEVENTS
MULTIMEDIAEVENTS
SERVEREVENTS
var hop = require( "hop" );

service slidePrev() {
   hop.broadcast( "goto-slide", -1 );
}
service slideNext() {
   hop.broadcast( "goto-slide", +1 );
}

<HTML> {
   ~{ 
      server.addEventListener(
	 "goto-slide",
         function( e ) {
	    gotoSlide( currentSlide().num + e.value );
	 } );
   }
   ...
}
image/svg+xml Layer 1
JAVASCRIPT?!
    // The code is heavily inspired by Modernizr http://www.modernizr.com/
    var pfx = (function () {
        
        var style = document.createElement('dummy').style,
            prefixes = 'Webkit Moz O ms Khtml'.split(' '),
            memory = {};
        
        return function ( prop ) {
            if ( typeof memory[ prop ] === "undefined" ) {
                
                var ucProp  = prop.charAt(0).toUpperCase() + prop.substr(1),
                    props   = (prop + ' ' + prefixes.join(ucProp + ' ') + ucProp).split(' ');
                
                memory[ prop ] = null;
                for ( var i in props ) {
                    if ( style[ props[i] ] !== undefined ) {
                        memory[ prop ] = props[i];
                        break;
                    }
                }
            
            }
            
            return memory[ prop ];
        };
    
    })();
    
    // `arraify` takes an array-like object and turns it into real Array
    // to make all the Array.prototype goodness available.
    var arrayify = function ( a ) {
        return [].slice.call( a );
    };
    
    // `css` function applies the styles given in `props` object to the element
    // given as `el`. It runs all property names through `pfx` function to make
    // sure proper prefixed version of the property is used.
    var css = function ( el, props ) {
        var key, pkey;
        for ( key in props ) {
            if ( props.hasOwnProperty(key) ) {
                pkey = pfx(key);
                if ( pkey !== null ) {
                    el.style[pkey] = props[key];
                }
            }
        }
        return el;
    };
    
    // `toNumber` takes a value given as `numeric` parameter and tries to turn
    // it into a number. If it is not possible it returns 0 (or other value
    // given as `fallback`).
    var toNumber = function (numeric, fallback) {
        return isNaN(numeric) ? (fallback || 0) : Number(numeric);
    };
    
    // `byId` returns element with given `id` - you probably have guessed that ;)
    var byId = function ( id ) {
        return document.getElementById(id);
    };
    
    // `$` returns first element for given CSS `selector` in the `context` of
    // the given element or whole document.
    var $ = function ( selector, context ) {
        context = context || document;
        return context.querySelector(selector);
    };
    
    // `$$` return an array of elements for given CSS `selector` in the `context` of
    // the given element or whole document.
    var $$ = function ( selector, context ) {
        context = context || document;
        return arrayify( context.querySelectorAll(selector) );
    };
    
    // `triggerEvent` builds a custom DOM event with given `eventName` and `detail` data
    // and triggers it on element given as `el`.
    var triggerEvent = function (el, eventName, detail) {
        var event = document.createEvent("CustomEvent");
        event.initCustomEvent(eventName, true, true, detail);
        el.dispatchEvent(event);
    };
    
    // `translate` builds a translate transform string for given data.
    var translate = function ( t ) {
        return " translate3d(" + t.x + "px," + t.y + "px," + t.z + "px) ";
    };
    
    // `rotate` builds a rotate transform string for given data.
    // By default the rotations are in X Y Z order that can be reverted by passing `true`
    // as second parameter.
    var rotate = function ( r, revert ) {
        var rX = " rotateX(" + r.x + "deg) ",
            rY = " rotateY(" + r.y + "deg) ",
            rZ = " rotateZ(" + r.z + "deg) ";
        
        return revert ? rZ+rY+rX : rX+rY+rZ;
    };
    
    // `scale` builds a scale transform string for given data.
    var scale = function ( s ) {
        return " scale(" + s + ") ";
    };
    
    // `perspective` builds a perspective transform string for given data.
    var perspective = function ( p ) {
        return " perspective(" + p + "px) ";
    };
    
    // `getElementFromHash` returns an element located by id from hash part of
    // window location.
    var getElementFromHash = function () {
        // get id from url ` or `slide-id` and "enhanced" `\/?/,"") );
    };
    
    // `computeWindowScale` counts the scale factor between window size and size
    // defined for the presentation in the config.
    var computeWindowScale = function ( config ) {
        var hScale = window.innerHeight / config.height,
            wScale = window.innerWidth / config.width,
            scale = hScale > wScale ? wScale : hScale;
        
        if (config.maxScale && scale > config.maxScale) {
            scale = config.maxScale;
        }
        
        if (config.minScale && scale < config.minScale) {
            scale = config.minScale;
        }
        
        return scale;
    };
    
    // CHECK SUPPORT
    var body = document.body;
    
    var ua = navigator.userAgent.toLowerCase();
    var impressSupported = 
                          // browser should support CSS 3D transtorms 
                           ( pfx("perspective") !== null ) &&
                           
                          // and `classList` and `dataset` APIs
                           ( body.classList ) &&
                           ( body.dataset ) &&
                           
                          // but some mobile devices need to be blacklisted,
                          // because their CSS 3D support or hardware is not
                          // good enough to run impress.js properly, sorry...
                           ( ua.search(/(iphone)|(ipod)|(android)/) === -1 );
    
    if (!impressSupported) {
        // we can't be sure that `classList` is supported
        body.className += " impress-not-supported ";
    } else {
        body.classList.remove("impress-not-supported");
        body.classList.add("impress-supported");
    }
    
    // GLOBALS AND DEFAULTS
    
    // This is were the root elements of all impress.js instances will be kept.
    // Yes, this means you can have more than one instance on a page, but I'm not
    // sure if it makes any sense in practice ;)
    var roots = {};
    
    // some default config values.
    var defaults = {
        width: 1024,
        height: 768,
        maxScale: 1,
        minScale: 0,
        
        perspective: 1000,
        
        transitionDuration: 1000
    };
    
    // it's just an empty function ... and a useless comment.
    var empty = function () { return false; };
    
    // IMPRESS.JS API
    
    // And that's where interesting things will start to happen.
    // It's the core `impress` function that returns the impress.js API
    // for a presentation based on the element with given id ('impress'
    // by default).
    var impress = window.impress = function ( rootId ) {
        
        // If impress.js is not supported by the browser return a dummy API
        // it may not be a perfect solution but we return early and avoid
        // running code that may use features not implemented in the browser.
        if (!impressSupported) {
            return {
                init: empty,
                goto: empty,
                prev: empty,
                next: empty,
                current: empty
            };
        }
        
        rootId = rootId || "impress";
        
        // if given root is already initialized just return the API
        if (roots["impress-root-" + rootId]) {
            return roots["impress-root-" + rootId];
        }
        
        // data of all presentation steps
        var stepsData = {};
        
        // element of currently active step
        var activeStep = null;
        
        // current state (position, rotation and scale) of the presentation
        var currentState = null;
        
        // array of step elements
        var steps = null;
        
        // configuration options
        var config = null;
        
        // scale factor of the browser window
        var windowScale = null;        
        
        // root presentation elements
        var root = byId( rootId );
        var canvas = document.createElement("div");
        
        var initialized = false;
        
        // STEP EVENTS
        //
        // There are currently two step events triggered by impress.js
        // `slideenter` is triggered when the step is shown on the 
        // screen (the transition from the previous one is finished) and
        // `slideleave` is triggered when the step is left (the
        // transition to next step just starts).
        
        // reference to last entered step
        var lastEntered = null;
        
        // `onStepEnter` is called whenever the step element is entered
        // but the event is triggered only if the step is different than
        // last entered step.
        var onStepEnter = function (step) {
            if (lastEntered !== step) {
                triggerEvent(step, "slideenter");
                lastEntered = step;
            }
        };
        
        // `onStepLeave` is called whenever the step element is left
        // but the event is triggered only if the step is the same as
        // last entered step.
        var onStepLeave = function (step) {
            if (lastEntered === step) {
                triggerEvent(step, "slideleave");
                lastEntered = null;
            }
        };
        
        // `initStep` initializes given step element by reading data from its
        // data attributes and setting correct styles.
        var initStep = function ( el, idx ) {
            var data = el.dataset,
                step = {
                    translate: {
                        x: toNumber(data.x),
                        y: toNumber(data.y),
                        z: toNumber(data.z)
                    },
                    rotate: {
                        x: toNumber(data.rotateX),
                        y: toNumber(data.rotateY),
                        z: toNumber(data.rotateZ || data.rotate)
                    },
                    scale: toNumber(data.scale, 1),
                    el: el
                };
            
            if ( !el.id ) {
                el.id = "step-" + (idx + 1);
            }
            el.index = idx;
	   
            stepsData["impress-" + el.id] = step;
            
            css(el, {
                position: "absolute",
                transform: "translate(-50%,-50%)" +
                           translate(step.translate) +
                           rotate(step.rotate) +
                           scale(step.scale),
                transformStyle: "preserve-3d"
            });
        };
        
        // `init` API function that initializes (and runs) the presentation.
        var init = function () {
            if (initialized) { return; }
            
            // First we set up the viewport for mobile devices.
            // For some reason iPad goes nuts when it is not done properly.
            var meta = $("meta[name='viewport']") || document.createElement("meta");
            meta.content = "width=device-width, minimum-scale=1, maximum-scale=1, user-scalable=no";
            if (meta.parentNode !== document.head) {
                meta.name = 'viewport';
                document.head.appendChild(meta);
            }
            
            // initialize configuration object
            var rootData = root.dataset;
            config = {
                width: toNumber( rootData.width, defaults.width ),
                height: toNumber( rootData.height, defaults.height ),
                maxScale: toNumber( rootData.maxScale, defaults.maxScale ),
                minScale: toNumber( rootData.minScale, defaults.minScale ),                
                perspective: toNumber( rootData.perspective, defaults.perspective ),
                transitionDuration: toNumber( rootData.transitionDuration, defaults.transitionDuration )
            };
            
            windowScale = computeWindowScale( config );
            
            // wrap steps with "canvas" element
            arrayify( root.childNodes ).forEach(function ( el ) {
                canvas.appendChild( el );
            });
            root.appendChild(canvas);
            
            // set initial styles
            document.documentElement.style.height = "100%";
            
            css(body, {
                height: "100%",
                overflow: "hidden"
            });
            
            var rootStyles = {
                position: "absolute",
                transformOrigin: "top left",
                transition: "all 0s ease-in-out",
                transformStyle: "preserve-3d"
            };
            
            css(root, rootStyles);
            css(root, {
                top: "50%",
                left: "50%",
                transform: perspective( config.perspective/windowScale ) + scale( windowScale )
            });
            css(canvas, rootStyles);
            
            body.classList.remove("impress-disabled");
            body.classList.add("impress-enabled");
            
            // get and init steps
            steps = $$(".step", root);
            steps.forEach( initStep );
            
            // set a default initial state of the canvas
            currentState = {
                translate: { x: 0, y: 0, z: 0 },
                rotate:    { x: 0, y: 0, z: 0 },
                scale:     1
            };
            
            initialized = true;
            
            triggerEvent(root, "impress:init", { api: roots[ "impress-root-" + rootId ] });
        };
        
        // `getStep` is a helper function that returns a step element defined by parameter.
        // If a number is given, step with index given by the number is returned, if a string
        // is given step element with such id is returned, if DOM element is given it is returned
        // if it is a correct step element.
        var getStep = function ( step ) {
            if (typeof step === "number") {
                step = step < 0 ? steps[ steps.length + step] : steps[ step ];
            } else if (typeof step === "string") {
                step = byId(step);
            }
            return (step && step.id && stepsData["impress-" + step.id]) ? step : null;
        };
        
        // used to reset timeout for `slideenter` event
        var stepEnterTimeout = null;
        
        // `goto` API function that moves to step given with `el` parameter (by index, id or element),
        // with a transition `duration` optionally given as second parameter.
        var goto = function ( el, duration ) {
            
            if ( !initialized || !(el = getStep(el)) ) {
                // presentation not initialized or given element is not a step
                return false;
            }
            
            // Sometimes it's possible to trigger focus on first link with some keyboard action.
            // Browser in such a case tries to scroll the page to make this element visible
            // (even that body overflow is set to hidden) and it breaks our careful positioning.
            //
            // So, as a lousy (and lazy) workaround we will make the page scroll back to the top
            // whenever slide is selected
            //
            // If you are reading this and know any better way to handle it, I'll be glad to hear about it!
            window.scrollTo(0, 0);
            
            var step = stepsData["impress-" + el.id];
            
            if ( activeStep ) {
                activeStep.classList.remove("active");
                body.classList.remove("impress-on-" + activeStep.id);
            }
            el.classList.add("active");
            
            body.classList.add("impress-on-" + el.id);
            
            // compute target state of the canvas based on given step
            var target = {
                rotate: {
                    x: -step.rotate.x,
                    y: -step.rotate.y,
                    z: -step.rotate.z
                },
                translate: {
                    x: -step.translate.x,
                    y: -step.translate.y,
                    z: -step.translate.z
                },
                scale: 1 / step.scale
            };
            
            // Check if the transition is zooming in or not.
            //
            // This information is used to alter the transition style:
            // when we are zooming in - we start with move and rotate transition
            // and the scaling is delayed, but when we are zooming out we start
            // with scaling down and move and rotation are delayed.
            var zoomin = target.scale >= currentState.scale;

	    if( isNaN( duration ) && el.hasAttribute( "data-duration" ) ) {
	       duration = toNumber( el.getAttribute( "data-duration" ),
				    config.transitionDuration );
	    } else {
               duration = toNumber(duration, config.transitionDuration);
	    }
	   
            var delay = (duration / 2);
            
            // if the same step is re-selected, force computing window scaling,
            // because it is likely to be caused by window resize
            if (el === activeStep) {
                windowScale = computeWindowScale(config);
            }
            
            var targetScale = target.scale * windowScale;
            
            // trigger leave of currently active element (if it's not the same step again)
            if (activeStep && activeStep !== el) {
                onStepLeave(activeStep);
            }
            
            // Now we alter transforms of `root` and `canvas` to trigger transitions.
            //
            // And here is why there are two elements: `root` and `canvas` - they are
            // being animated separately:
            // `root` is used for scaling and `canvas` for translate and rotations.
            // Transitions on them are triggered with different delays (to make
            // visually nice and 'natural' looking transitions), so we need to know
            // that both of them are finished.
            css(root, {
                // to keep the perspective look similar for different scales
                // we need to 'scale' the perspective, too
                transform: perspective( config.perspective / targetScale ) + scale( targetScale ),
                transitionDuration: duration + "ms",
                transitionDelay: (zoomin ? delay : 0) + "ms"
            });
            
            css(canvas, {
                transform: rotate(target.rotate, true) + translate(target.translate),
                transitionDuration: duration + "ms",
                transitionDelay: (zoomin ? 0 : delay) + "ms"
            });
            
            // Here is a tricky part...
            //
            // If there is no change in scale or no change in rotation and translation, it means there was actually
            // no delay - because there was no transition on `root` or `canvas` elements.
            // We want to trigger `slideenter` event in the correct moment, so here we compare the current
            // and target values to check if delay should be taken into account.
            //
            // I know that this `if` statement looks scary, but it's pretty simple when you know what is going on
            // - it's simply comparing all the values.
            if ( currentState.scale === target.scale ||
                (currentState.rotate.x === target.rotate.x && currentState.rotate.y === target.rotate.y &&
                 currentState.rotate.z === target.rotate.z && currentState.translate.x === target.translate.x &&
                 currentState.translate.y === target.translate.y && currentState.translate.z === target.translate.z) ) {
                delay = 0;
            }
	   
	    var dir = 1;
	    if( activeStep && (activeStep.index > el.index) ) dir = -1;
            
	    // MS: invoke the optional slideleave user hook
	    if (activeStep) {
	       if (("onkeyup" in activeStep) && (activeStep.onkeyup instanceof Function)) {
		  var ev = new HopEvent("slideleave");
		  ev.target = el;
		  ev.direction = dir;
		  
		  activeStep.onkeyup(ev);
	       }
	    }

            // store current state
            currentState = target;
            activeStep = el;

	    // MS: invoke the optional slideenter user hook
 	    el.setAttribute("data-step", "0");
	    if ("onkeydown" in el) {
	       if (el.onkeydown instanceof Function) {
		  var ev = new HopEvent("slideenter");
		  ev.target = el;
		  ev.direction = dir;
		  el.onkeydown(ev);
	       }
	    }
            
            // And here is where we trigger `slideenter` event.
            // We simply set up a timeout to fire it taking transition duration (and possible delay) into account.
            //
            // I really wanted to make it in more elegant way. The `transitionend` event seemed to be the best way
            // to do it, but the fact that I'm using transitions on two separate elements and that the `transitionend`
            // event is only triggered when there was a transition (change in the values) caused some bugs and 
            // made the code really complicated, cause I had to handle all the conditions separately. And it still
            // needed a `setTimeout` fallback for the situations when there is no transition at all.
            // So I decided that I'd rather make the code simpler than use shiny new `transitionend`.
            //
            // If you want learn something interesting and see how it was done with `transitionend` go back to
            // version 0.5.2 of impress.js: http://github.com/bartaz/impress.js/blob/0.5.2/js/impress.js
            window.clearTimeout(stepEnterTimeout);
            stepEnterTimeout = window.setTimeout(function() {
                onStepEnter(activeStep);
            }, duration + delay);

            return el;
        };
        
        // `prev` API function goes to previous step (in document order)
        var prev = function () {
            var prev = steps.indexOf( activeStep ) - 1;
            prev = prev >= 0 ? steps[ prev ] : steps[ steps.length-1 ];
            
            return goto(prev);
        };
        
        // `next` API function goes to next step (in document order)
        var next = function () {
            var next = steps.indexOf( activeStep ) + 1;
            next = next < steps.length ? steps[ next ] : steps[ 0 ];
            
            return goto(next);
        };

        // `home` MS: API function goes to the first step (in document order)
        var home = function () {
	    return goto(0);
        };
        
        // `end` MS: API function goes to the last step (in document order)
        var end = function () {
	    return goto(steps.length-1);
        };
        
        // `current` MS: API function that returns the current slide
        var current = function () {
            return activeStep;
        };

        // Adding some useful classes to step elements.
        //
        // All the steps that have not been shown yet are given `future` class.
        // When the step is entered the `future` class is removed and the `present`
        // class is given. When the step is left `present` class is replaced with
        // `past` class.
        //
        // So every step element is always in one of three possible states:
        // `future`, `present` and `past`.
        //
        // There classes can be used in CSS to style different types of steps.
        // For example the `present` class can be used to trigger some custom
        // animations when step is shown.
        root.addEventListener("impress:init", function(){
            // STEP CLASSES
            steps.forEach(function (step) {
                step.classList.add("future");
            });
            
            root.addEventListener("slideenter", function (event) {
                event.target.classList.remove("past");
                event.target.classList.remove("future");
                event.target.classList.add("present");
            }, false);
            
            root.addEventListener("slideleave", function (event) {
                event.target.classList.remove("present");
                event.target.classList.add("past");
            }, false);
            
        }, false);
        
        // Adding hash change support.
        root.addEventListener("impress:init", function(){
            
            // last hash detected
            var lastHash = "";
            
            // `step-id` to prevent default browser
            // scrolling to element in hash.
            //
            // And it has to be set after animation finishes, because in Chrome it
            // makes transtion laggy.
            // BUG: http://code.google.com/p/chromium/issues/detail?id=62820
            root.addEventListener("slideenter", function (event) {
                window.location.hash = lastHash = "' ) {
                    target = document.getElementById( href.slice(1) );
                }
            }

           if ( api.goto(target) ) {
                event.stopImmediatePropagation();
                event.preventDefault();
            }
        }, false);
        
        // delegated handler for clicking on step elements
        document.addEventListener("click", function ( event ) {
            var target = event.target;
            // find closest step element that is not active
            while ( !(target.classList.contains("step") && !target.classList.contains("active")) &&
                    (target !== document.documentElement) ) {
                target = target.parentNode;
            }
            
            if ( api.goto(target) ) {
                event.preventDefault();
            }
        }, false);
        
        // touch handler to detect taps on the left and right side of the screen
        // based on awesome work of @hakimel: https://github.com/hakimel/reveal.js
        document.addEventListener("touchstart", function ( event ) {
            if (event.touches.length === 1) {
                var x = event.touches[0].clientX,
                    width = window.innerWidth * 0.3,
                    result = null;
                    
                if ( x < width ) {
                    result = api.prev();
                } else if ( x > window.innerWidth - width ) {
                    result = api.next();
                }
                
                if (result) {
                    event.preventDefault();
                }
            }
        }, false);
        
        // rescale presentation when window is resized
        window.addEventListener("resize", throttle(function () {
            // force going to active step again, to trigger rescaling
            api.goto( document.querySelector(".active"), 500 );
        }, 250), false);
        
    }, false);
        
})(document, window);

// THAT'S ALL FOLKS!
//
// Thanks for reading it all.
// Or thanks for scrolling down and reading the last part.
//
// I've learnt a lot when building impress.js and I hope this code and comments
// will help somebody learn at least some part of it.
tiobe.pngtiobe2.png
JS 1
  • Functional language
    • polymorphism
    • gc
    • higher order
  • Initially client-side only
    • dom
    • ajax
  • Now server-side also
  • Dynamic
#include <stdio.h>

/* a forward definition */
static void hello_world();

/* the entry point of the application */
int main( int argc, char *argv[], char *env[] ) {
   hello_world();
   return 0;
}

void hello_world() {
   puts( "hello, world!" );
}
function hello_world() {
   console.log( "hello, world!" );
}

// a top-level expression
hello_world();
#include <stdio.h>
#include <stdlib.h>

int fib( int n ) {
   if( n < 2 ) {
      return 1;
   } else {
      return fib( n - 1 ) + fib( n - 2 );
   }
}

int main( int argc, char *argv[], char *env[] ) {
   printf( "fib: %d\n", fib( atoi( argv[ 1 ] ) ) );
}
function fib( n ) {
   if( n < 2 ) {
      return 1;
   } else {
      return fib( n - 1 ) + fib( n - 2 );
   }
}

var argv = process.argv;
console.log( "fib: " + fib( parseInt( argv[ 1 ] ) ) );
// return a new string composed of the scheme of a URL
// url_scheme( "http://hop.inria.fr" ) -> "http"
char *url_scheme( char *url ) {
   char *s = url, *res;
   int i;

   while( *s++ != ':' );

   i = s - url;
   res = malloc( i );
   res[ i - 1 ] = 0;
   
   memcpy( res, url, i - 1 );
   
   return res;
}
function url_scheme( url ) {
   for( var i = 0; url.charAt( i ) != ":"; i++ );

   return url.substring( 0, i );
}
// more efficient version with regular expressions
function url_scheme( url ) {
   var m = url.match( new Regexp( "(^[^:]+)://" ) );

   return m[ 1 ];
}
function url_scheme( url ) {
   return url.match( /(^[^:]+):[/][/]/ )[ 1 ];
}
#include <stdarg.h>

static obj_t generic_entry( obj_t proc, ... ) {
   va_list argl;
   obj_t optional = BNIL, runner;

   va_start( argl, proc );
   
   if( (runner = va_arg( argl, obj_t )) != BEOA ) {
      obj_t tail;
      
      optional = tail = MAKE_PAIR( runner, BNIL );
      
      while( (runner = va_arg( argl, obj_t )) != BEOA ) {
         SET_CDR( tail, MAKE_PAIR( runner, BNIL ) );
         tail = CDR( tail );
      } 
   }

   va_end( argl );
   
   return apply( PROCEDURE_REF( proc, 3 ), optional );
}
function foo( a, b ) {
   return arguments[ 2 ];
}

console.log( foo( 1, 2, 3 ) )
3
JS ≉ C
  • Untyped
  • No integers (floats only)
  • No pointers (safe, gc)
  • No vectors (arrays, aka hashtables)
  • Immutable unicode strings
  • Higher order
double add( double x, double y ) { return x + y; }
function add( x, y ) { return x + y; }
var add = function( x, y ) { return x + y; }
function add( x, y ) {
   function plus( i, j ) { return i + j; }
   return plus( x - 0, y - 0 );
}
function makeAdd( x ) {
   function plus( y ) { return x + y; }
   return plus;
}

var inc = makeAdd( 1 )
inc( 24 )
25
function o( f, g ) {
   return function( x ) {
      return f( g( x ) );
   }
}
JS ≠ JAVA
var p1 = { x: 1, y: 2 };

function Point( x, y ) {
   this.x = x;
   this.y = y;
}

var p2 = new Point( 1, 2 );
var p3 = new Point();
{ x: undefined, y: undefined }

p1 instanceof Point
false
p2 instanceof Point
true
Point.prototype = {
   norm: function() {
      return Math.sqrt( this.x*this.x + this.y*this.y );
   },
   z: 123
}
var p4 = new Point( 0, 2 );
var p5 = new Point( 4, 5 );
p4.norm()
2
p4.z
123
p2.z
undefined

p4.__proto__ === p5.__proto__ === Point.prototype;
true

p4.z = 200; p4.z
200

p5.z
123

Point.prototype.z = 300; p5.z + p4.z
500
getX() {
   return this.x;
}

norm( getX( p4 ) )
undefined

p4.getX = getX; p4.getX()
0
var p2 = new Point( 1, 2 );

p6.z = 5;
p6[ "z" ] = p6.z + 1;
if( "x" in p6 ) {
   delete p6.x;
}
p6.norm()
NaN

delete Point.prototype.norm;
p6.norm()
TypeError: Object #<Object> has no method 'norm'
var p3 = {
   get a() { return z - 1 },
   set a( v ) { z = v + 1 }
};
var z = 0;

p3.a
-1

p3.a = 5;
p3.a
5

z
6
var p3 = new Point( 0, 0 );

Object.defineProperty( p3, "a", {
   get: function() { return z - 1; },
   set: function( v ) { z = v + 1; },
   configurable: false
} );

p3.a
-1

p3.a = 5;
p3.a
5

z
6
JS ≅ LISP
(define (mapN f . l)
   (let loop ((l l))						     
      (if (null? (car l))
	  '()
	  (cons (apply f (map car l))
	     (loop (map cdr l))))))









(define (addN l)
   (reduce l (lambda (p c) (+ p c))))



(mapN '((1 2 3 4) (4 3 2 1)) addN)
(5 5 5 5)
function mapN( f, vec0 ) {
  var len = arguments[ 1 ].length;
  var res = [];
  var vecs = Array.prototype.slice.call(
     arguments, 1, arguments.length );

  for( var i = 0; i < len; i++ ) {
    res[ i ] = f.apply(
       false,
       vecs.map( function(v) { return v[ i ] } ) );
  }

  return res;
}

function addN( _ ) {
   return Array.prototype.reduce.call(
      arguments,
      function( p, c ) { return p + c; } );
}

mapN( addN, [1,2,3,4], [4,3,2,1] )
[ 5, 5, 5, 5 ]
JS is unique
> var v = 10;
v
10

this.v
10

this[ "v" ]
10 // no global variables, global properties! 

var name = "v"
this[ name ]
10

(function () { return this })().v
10
w = 20; w
20

this[ "w" ]
20

x
ReferenceError: x is not defined

function foo() { x = 4; }
foo()
undefined

x
4
function foo( a, b ) {
   var c = a + b;
   
   try {
      var c = a - b;
   } catch( e ) {
      return -1;
   }

   return c;
}

foo( 1, 2 )
-1
function foo( a, b ) {
   var e = c + b;
   
   try {
      var c = a + b;
   } catch( e ) {
      return -1;
   }

   return e;
}

foo( 1, "2" )
undefined2
function foo( a ) {
   for( var i = 0; i <= a; i++ ) {
      var x = i;
   }

   return x;
}

foo( 10 )
10
function foo( a ) {
   var t = new Array();
   
   for( var i = 0; i <= a; i++ ) {
      var x;

      x = i;
      t[ i ] = function() { return x; }
   }

   return t[ a ]() - t[ 0 ]();
}

foo( 10 )
0
function foo( a ) {
   var t = new Array();
   
   for( var i = 0; i <= a; i++ ) {
      t[ i ] = (function( x ) {
	 return function() { return x; }
      })( i );
   }

   return t[ a ]() - t[ 0 ]();
}

foo( 10 )
10
var a = new Array();

a[ "1" ] = 3;
a[ "1" ] === a[ 1 ];

a[ 1 ]
3

a[ 1.00000000000001 ]
undefined

a[ 1.000000000000000001 ]
3
"123" + 4
'1234'

4 + "123"
'4123'

var x = "123"; x++; x
124

var o = { toString: function() {return "4"}}; o++; o
5

{valueOf: function() {return 1}} % 2
1

null % undefined
NaN
js-11.5.pngecma.png
js-11.5.3.png
  • The program starts on the server
  • The client boots with an HTML page
  • HTML is the container
<HTML>
   <BODY>
      <TABLE>
         <TR><TD onclick="alert( '1' )">1</TD></TR>
         <TR><TD onclick="alert( '2' )">2</TD></TR>
         <TR><TD onclick="alert( '3' )">3</TD></TR>
      </TABLE>
   </BODY>
</HTML>
service() {
   return <HTML> {
      <BODY> {
	 <TABLE> {
            <TR> { <TD> { onclick: ~{ alert( "1" ) }, 1 } },
            <TR> { <TD> { onclick: ~{ alert( "2" ) }, 3 } },
            <TR> { <TD> { onclick: ~{ alert( "2" ) }, 3 } } 
	 } </TABLE>
      } </BODY>
   } </HTML>
}
<HTML>
   <BODY>
      <TABLE>
<?php
for( $i=1; $i<4; $i++ )
   echo "<TR><TD onclick='alert( $i )'>$i</TD></TR>"
?>
      </TABLE>
   </BODY>
</HTML>
service() {
   return <HTML> {
      <BODY> {
	 <TABLE> {
	    [1, 2, 3.map( function( i ) {
               <TD> { onclick: ~{alert( ${i} )}, i } } );
	 } </TABLE>
      } </BODY>
   } </HTML>
}
service dom() {
   var el = <UL> {
      <LI> { "foo" },
      <LI> { "bar" },
      <LI> { "gee" }
   };

   return <HTML> {
      <HEAD> { css: dom.resource( "iframe.hss" ) },
      el,
      <BUTTON> {
	 onclick: ~{
	    var c0 = ${el}.childNodes[ 0 ];
	    var c1 = ${el}.childNodes[ 1 ];

	    ${el}.replaceChild( c1, c0 );
	    ${el}.appendChild( c0 );
	 },
	 "rotate"
      } </BUTTON>
   } </HTML>
}
image/svg+xml Openclipart Meshed Gears 2013-03-13T14:10:54 gears/sprockets meshed together http://openclipart.org/detail/176293/meshed-gears-by-dav1dp-176293 dav1dp clip art clipart gear machine mesh sprocket
service tower() {
   return <HTML> {
      <HEAD> { css: tower.resource( "iframe.hss" ) },
      ~{
         function clicked (msg) {
            var but = <BUTTON> {
               onclick: ~{
                  clicked( ${msg + "+"} );
               },
               msg
            };
            
            document.body.appendChild( but );
         }
      },
      <BUTTON> {
         onclick: ~{ clicked( "click me also" ); },
         "click me"
      }
   }
}
image/svg+xml Openclipart Meshed Gears 2013-03-13T14:10:54 gears/sprockets meshed together http://openclipart.org/detail/176293/meshed-gears-by-dav1dp-176293 dav1dp clip art clipart gear machine mesh sprocket
  • ⟦function (x, y, ...) {...}⟧ =
    val x val x ... → val
    (function (a) {return 1+a})(4)
    5
  • ⟦service (x, y, ...) {...}⟧ =
    val x val x ... → frame
    (service (a) {return 1+a})(4)
    http://localhost:8080/hop/svc-23ab4c7?a=4
  • ⟦f.post( k )⟧ =
    frame ⇢ async
    (${service (a) {return 1+a}})(4).post(function (v) {return v})
    5
fs = require( 'fs' );

service cmd( path ) {
   return fs.readdirSync( path ).map(
      function( p ) {
	 return {
	    name: p.replace( /.*\//g, "" ), size: fs.statSync( p ).size
	 } } );
}

service dir( { path: "/tmp" } ) {
   return <HTML> {
      <BUTTON> {
	 onclick: ~{
	    ${cmd}( ${path} )
	       .post( function ( v ) {
		  document.body.appendChild( <TABLE> {
		     v.map( function( p ) {
			return <TR> { <TH> { p.name }, <TD> { p.size } };
		     } )
		  } </TABLE> ) } ) },
	 "du -sk"
      } </BUTTON>,
   } </HTML>
}
image/svg+xml Openclipart Meshed Gears 2013-03-13T14:10:54 gears/sprockets meshed together http://openclipart.org/detail/176293/meshed-gears-by-dav1dp-176293 dav1dp clip art clipart gear machine mesh sprocket
service proxy() {
   return <HTML> {
      ~{function k(e ) {
         document.body.appendChild( <DIV> { el } )
      }},
      <BUTTON> {
         onclick: ~{${svc}("localhost").post(k)},
         "direct call"
      },
      <BUTTON> {
         onclick: ~{${proxied}("www.foo.org").post(k)},
         "proxied call"
      }
   }
}

service proxied(host) {
   return svc(host)
      .post( function(v) { return v },
             { host: host, sync: true } );
}

service svc(name) {
   return name.toUpperCase();
}
service withurl() {
   var output = <DIV> {};
   var input = <INPUT> {value: "toto n'est pas content"};
   var select = <SELECT> {
      <OPTION> { value: "fr|en", "fr-&gt;en" },
      <OPTION> { value: "en|fr", "en-&gt;fr" }
   };
      
   var translate = service(text, langpair) {
      return translateText(text, langpair);
   };
      
   return <HTML> {
      <HEAD> { css: withurl.resource("iframe.hss") },
      select,
      input,
      <BUTTON> {
         onclick: ~{
            ${translate}(${input}.value, ${select}.value)
               .post(function(v) {
		  ${output}.innerHTML = v;
	       })
         },
         "translate"
      } </BUTTON>,
      output
   };
var hop = require("hop");

var url_base = "http://mymemory.translated.net/api/get";

function translateText(text, langpair) {
   var url = url_base
      + "?q=" + escape(text)
      + "&langpair=" + (langpair ? langpair : "en|fr");

   return hop.withURL(
      url,
      function(o) {
         if(o.responseStatus === 200) {
            var t = o.responseData.translatedText;
            
            return hop.charsetConvert(
	       unescape(t), "UTF-8");
         } } )
}
image/svg+xml Openclipart Meshed Gears 2013-03-13T14:10:54 gears/sprockets meshed together http://openclipart.org/detail/176293/meshed-gears-by-dav1dp-176293 dav1dp clip art clipart gear machine mesh sprocket
  • The server REPL (Read Eval Print Loop)
    1. wait for a socket connection
    2. parse the HTTP request
    3. send the response
    4. goto 1
  • The syntax of the request
    GET|HEAD|POST <path> HTTP/1.0\r\n
  • The response
    ..., 2xx success, ..., 4xx error, ...
  • A Request
    GET /html/rfc1945 HTTP/1.0
    Host: tools.ietf.org
    User-Agent: Mozilla/5.0
    Accept-Encoding: gzip, deflate
    Accept-Charset: utf-8...
  • A Response
    HTTP/1.0 200 OK
    Server: Apache/2.2.12 (Debian)
    Content-Length: 174663
    
    <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
    <HTML>
    <HEAD>
    <TITLE>RFC 1945 (rfc1945) - Hypertext Transfer Protocol...
require('hop').currentRequest()
(abstract-class %http-message
   (seconds::integer read-only (default (current-seconds)))
   (header::pair-nil (default '()))
   (content-length::integer read-only (default -1))
   (charset (default #f)))

(class http-request::%http-message
   (user::user read-only (default (class-nil user)))
   (localclientp::boolean (default #f))
   (http::symbol (default 'HTTP/1.1))
   (host::string (default "localhost"))
   (scheme::symbol (default 'http))
   (port::integer (default 80))
   (method::symbol read-only (default 'GET))
   (abspath::string (default ""))
   (connection::symbol (default 'keep-alive)))

(final-class http-server-request::http-request
   (authorization (default #f))
   (service::obj (default #unspecified)))

(class http-proxy-request::http-request
   (proxy-authorization (default #f)))
service text1() {
   return "<html><em>Foo</em></html>";
}
image/svg+xml Openclipart Meshed Gears 2013-03-13T14:10:54 gears/sprockets meshed together http://openclipart.org/detail/176293/meshed-gears-by-dav1dp-176293 dav1dp clip art clipart gear machine mesh sprocket
var hop = require('hop');

service text2() {
   return hop.HTTPResponseString(
      "<html><em>Foo</em></html>" );
}
image/svg+xml Openclipart Meshed Gears 2013-03-13T14:10:54 gears/sprockets meshed together http://openclipart.org/detail/176293/meshed-gears-by-dav1dp-176293 dav1dp clip art clipart gear machine mesh sprocket
var hop = require('hop');

service text3() {
   return hop.HTTPResponseString(
      "<html><em>Foo</em></html>",
      { contentType: "text/html" } );
}
image/svg+xml Openclipart Meshed Gears 2013-03-13T14:10:54 gears/sprockets meshed together http://openclipart.org/detail/176293/meshed-gears-by-dav1dp-176293 dav1dp clip art clipart gear machine mesh sprocket
service hop1() {
   return <HTML> { <EM> { "foo" } };
}
image/svg+xml Openclipart Meshed Gears 2013-03-13T14:10:54 gears/sprockets meshed together http://openclipart.org/detail/176293/meshed-gears-by-dav1dp-176293 dav1dp clip art clipart gear machine mesh sprocket
var hop = require('hop');

service hop2() {
   return hop.HTTPResponseHop( <HTML> {
      <EM> { "foo" }
   } );
}
image/svg+xml Openclipart Meshed Gears 2013-03-13T14:10:54 gears/sprockets meshed together http://openclipart.org/detail/176293/meshed-gears-by-dav1dp-176293 dav1dp clip art clipart gear machine mesh sprocket
var hop = require("hop");
var fs = require("fs");

service file1() {
   var fd = fs.openSync( module.filename, "r" );
   var sz = fs.statSync( module.filename ).size
   
   var buf = new Buffer( sz );
   fs.readSync( fd, buf, 0, sz, 0 );

   return hop.HTTPResponseString(
      buf.toString( "ascii", 0, sz )  );
}
image/svg+xml Openclipart Meshed Gears 2013-03-13T14:10:54 gears/sprockets meshed together http://openclipart.org/detail/176293/meshed-gears-by-dav1dp-176293 dav1dp clip art clipart gear machine mesh sprocket
var hop = require("hop");

service file2() {
   return hop.HTTPResponseFile( module.filename );
}
					  
image/svg+xml Openclipart Meshed Gears 2013-03-13T14:10:54 gears/sprockets meshed together http://openclipart.org/detail/176293/meshed-gears-by-dav1dp-176293 dav1dp clip art clipart gear machine mesh sprocket
var hop = require( "hop" );
var count = 2;

service authenticationAccept() {
   switch( count-- ) {
      case 2:
        return hop.HTTPResponseAuthentication( "I don't know you" );
      case 1:
        return hop.HTTPResponseAuthentication( "Do you really insist?" );
      case 0:
        count = 2;
        return "Ok for this time";
   }
}

service auth() {
   return <HTML> {
      <DIV> { "Click 3 times to get access." },
      <BUTTON> {
	 onclick: ~{ ${authenticationAccept}()
		     .post( function( v ) { document.write( v ) },
			    { fail: function( v ) { ; } } ) },
	 "click me"
      }
   }
}
image/svg+xml Openclipart Meshed Gears 2013-03-13T14:10:54 gears/sprockets meshed together http://openclipart.org/detail/176293/meshed-gears-by-dav1dp-176293 dav1dp clip art clipart gear machine mesh sprocket
var hop = require( "hop" );

var canvas = <CANVAS> {
   width: 550, height: 400,
   style: "border: 1px solid black"
};

service event() {
   return <HTML> {
      canvas,
      ~{
	 var ctx = ${canvas}.getContext( "2d" );
	 ctx.lineWidth = 10;

	 ${canvas}.addEventListener( "mousedown",
	    function( evt ) {
	       ctx.lineTo( evt.pageX, evt.pageY ); ctx.stroke();
	    } );
      }
   }
}



image/svg+xml Openclipart Meshed Gears 2013-03-13T14:10:54 gears/sprockets meshed together http://openclipart.org/detail/176293/meshed-gears-by-dav1dp-176293 dav1dp clip art clipart gear machine mesh sprocket
var hop = require( "hop" );

var canvas = <CANVAS> {
   width: 550, height: 400,
   style: "border: 1px solid black"
};

service event2() {
   return <HTML> {
      canvas,
      ~{
	 var ctx = ${canvas}.getContext( "2d" );
	 ctx.lineWidth = 10;

	 ${canvas}.addEventListener( "mousedown",
	    function( evt ) {
	       ctx.lineTo( evt.pageX, evt.pageY ); ctx.stroke();
	       ${service( evt ) {
		  hop.broadcast( "draw", evt );
	       } }( { pageX: evt.pageX, pageY: evt.pageY } ).post();
	    } );
      }
   }
}
image/svg+xml Openclipart Meshed Gears 2013-03-13T14:10:54 gears/sprockets meshed together http://openclipart.org/detail/176293/meshed-gears-by-dav1dp-176293 dav1dp clip art clipart gear machine mesh sprocket
var canvas = <CANVAS> {
   width: 550, height: 400,
   style: "border: 1px solid black"
};

service slave( {red: 200, green: 0, blue: 0} ) {
   return <HTML> {
      canvas,
      ~{
	 window.onload = function() {
	    var canvas = ${canvas};
	    var ctx = canvas.getContext( "2d" );
	    ctx.lineWidth = 15;
	    ctx.strokeStyle =
	       "rgba(" + ${red} + "," + ${green} + "," + ${blue} + ",1)";

	    server.addEventListener(
	       "draw",
	       function( evt ) {
		  ctx.lineTo( evt.value.pageX, evt.value.pageY );
		  ctx.stroke();
	       } );
	 }
      }
   } </HTML>
}
image/svg+xml Openclipart Meshed Gears 2013-03-13T14:10:54 gears/sprockets meshed together http://openclipart.org/detail/176293/meshed-gears-by-dav1dp-176293 dav1dp clip art clipart gear machine mesh sprocket
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>jQuery UI Datepicker - Default functionality</title>
    <link rel="stylesheet" 
      href="//code.jquery.com/ui/1.10.4/themes/smoothness/jquery-ui.css">
    <script src="//code.jquery.com/jquery-1.10.2.js"></script>
    <script src="//code.jquery.com/ui/1.10.4/jquery-ui.js"></script>
    <link rel="stylesheet" href="/resources/demos/style.css">
    <script>
    $(function() {
     $( "#datepicker" ).datepicker();
    });
  </script>
  </head>
  <body>
    <p>Date: <input type="text" id="datepicker"></p>
  </body>
</html>
service jquery() {
  return <HTML> {
      <HEAD> {
	 <TITLE> {"jQuery UI Datepicker - Default functionality"},
	 <LINK> {rel: "stylesheet", href: "http://code.jquery.com/ui/1.10.4/themes/smoothness/jquery-ui.css"
	 },
	 <SCRIPT> {src: "http://code.jquery.com/jquery-1.10.2.js"},
	 <SCRIPT> {src: "http://code.jquery.com/ui/1.10.4/jquery-ui.js"},
	 <LINK> {rel: "stylesheet", href: "http://jqueryui.com/resources/demos/style.css"
	 }
      },
    <P> { "Date: ", <DATEPICKER> {} }
  }
}

function DATEPICKER( attrs ) {
  var id = attrs.id || "datepicker";
  return [
    ~{ $(function() { $("#" + ${id}).datepicker() }) },
    <INPUT> { type: "text", id: id }
  ]
}
image/svg+xml Openclipart Meshed Gears 2013-03-13T14:10:54 gears/sprockets meshed together http://openclipart.org/detail/176293/meshed-gears-by-dav1dp-176293 dav1dp clip art clipart gear machine mesh sprocket

Use a spacebar or arrow keys to navigate

big-pointer.png
Table of contents
  1. Title
  2. ==== Web diffus ====
  3. Diffuse computing
  4. Infrastructure
  5. Too many technologies
  6. Architecture
  7. Multitier
  8. ==== Web technologies =====
  9. history of web techs
  10. Web sloc
  11. Primitive web
  12. Web 1.0
  13. Back button
  14. Web 2.0
  15. ==== hop ====
  16. Hop's syntax
  17. === multitier ====
  18. Hello world
  19. Multitier secret
  20. Compliance
  21. Hop's goals
  22. Multitier reuse JS
  23. Multitier reuse SVG
  24. Multitier services
  25. Service calls
  26. Service post
  27. Service calls and data
  28. Comet
  29. Server push
  30. Extended events
  31. Broadcast
  32. Diffuse
  33. === javascript ====
  34. Popularity
  35. Primer
  36. URLs
  37. Characteristics
  38. Hello
  39. Fib
  40. C Loop
  41. JavaScript Loop
  42. C varargs
  43. JavaScript varargs
  44. === JS vs C ===
  45. JS vs C (2)
  46. JavaScript functions
  47. JavaScript functions (2)
  48. === JS vs Java ===
  49. Prototype
  50. Prototype (2)
  51. Prototype (2)
  52. Delete
  53. Dynamic
  54. Dynamic (2)
  55. === JS vs Lisp ===
  56. Apply Lisp
  57. Apply
  58. === JS is unique ===
  59. Global object
  60. Global object (2)
  61. Local variables
  62. Local variables (2)
  63. Local variables (3)
  64. Local variables (4)
  65. Local variables (5)
  66. Cast
  67. Cast (2)
  68. Cast (3)
  69. Cast (4)
  70. ==== Web =====
  71. Hop Characteristics
  72. Hop HTML tables
  73. Hop HTML tables (2)
  74. Hop multitier dom
  75. Hop multitier tower
  76. Hop services
  77. Hop multitier dir
  78. Proxy
  79. withurl
  80. withurl (2)
  81. ==== Response =====
  82. An example an HTTP transaction
  83. HTTP requests
  84. HTTP responses (string)
  85. HTTP responses (hop)
  86. HTTP responses (file)
  87. HTTP responses (authentication)
  88. == Event ===
  89. Broadcast
  90. slave
  91. JQuery
  92. JQuery Hop