The Parable of the Lost State

    - Hello ... - Aah
    !
    - Well, why are you?
    - I am afraid of you!
    - What is it?
    - You always humiliate me ...
    - Heh, okay, I won’t.
    - True?
    - Doo, do you want some candy with an ajax?
    - Of course!
    - Come on Tada, fly to us in St. Petersburg. Here is a list of flights for which there are tickets sorted by price: rasp.yandex.ru/search?cityFrom=St . Petersburg&cityTo=Moscow
    - Um ... something you lied to uncle - there are generally all flights from Moscow to St. Petersburg, sorted by time of departure.
    “Um ... really, it didn't work out well ... but do you know why?”
    - Ne?
    - Because the state of the application does not affect the uri. Therefore, each time you go to this page from bookmarks, you will have to reinstall filtering and sorting.
    - Bloo, what to do?
    - Resign yourself, for as a visitor you can’t do anything already.
    - And if I were a developer?
    - Then you could go along the REST path and make the application state completely dependent on uri.
    - But then there will be a reload of the page.
    - And you only change the hash-part of the page.
    “But how do I know if uri has changed?”
    - There are libraries for this.
    - What if the state of my application is a multidimensional structure?
    - Use Hiqus .
    - Oh, how complicated it is ...
    - Why is it so complicated? See:
    HashState.listen( function(){ // вешаем обработчик изменения хэша
        console.log( 'state changed to: ' + HashState ) // дампим всё состояние
        console.log( 'filters: ' + HashState.sub( 'filters' ) ) // дампим лишь состояние фильтров
        console.log // выводим состояние сортировки
        (    'order by: '
        +    HashState.get( 'order', 'by' )
        +    ' '
        +    ( HashState.get( 'order', 'reverse' ) ? 'desc' : 'asc' )
        )
    })

    - And how to change the state?
    - Yes, elementary:
    HashState.put( 'order:by:cost' )
    HashState.put( 'order', 'reverse', true )
    HashState.put( 'filters', { type: 'airplane', free: true } )

    - A wash?
    - Yes, also not a problem:
    HashState( '' ) // стираем вообще всё
    HashState.put( 'filters', '' ) // стираем лишь состояние фильтров

    - It seems nothing complicated.
    - Namely. Catch Libu and blow at all times in St. Petersburg, otherwise they will eat all the tasty:
    var HashState= new function(){

    Version: 1
    Description: 'stores data in hash part of uri'
    License: 'public domain'

    Implementation:

    var handlers= []
    var latency= 25
    var timer= 0
    var hrefLast= ''
    var dataLast= null
    var data= null

    var has= function( list, item ){
        for( var i= list.length - 1; i >= 0; --i ) if( list[i] === item ) return true
        return false
    }
    var drop= function( list, item ){
        for( var i= list.length - 1; i >= 0; --i ) if( list[i] === item ) list.splice( i, 1 )
    }

    var listen= function( handler ){
        if( has( handlers, handler ) ) return
        handlers.push( handler )
        schedule()
    }
    var forget= function( handler ){
        drop( handlers, handler )
        if( !handlers.length ) freeze()
    }

    var freeze= function(){
        timer= clearTimeout( timer )
    }
    var schedule= function(){
        freeze()
        timer= setTimeout( update, latency )
    }

    var update= function(){
        var href= document.location.href
        if( href !== hrefLast ){
            data= Hiqus( href.replace( /^[^#]*#?/, '' ) )
            if( dataLast+'' != data )
                for( var i= 0, len= handlers.length; i < len; ++i )
                    handlers[i].call()
            hrefLast= href
            dataLast= data
        }
        if( timer ) schedule()
    }
    var change= function( data ){
        document.location= '#' + data
    }

    var HashState= function(){
        if( !arguments.length ) return Hiqus( data )
        change( data= Hiqus.apply( null, arguments ) )
        return HashState
    }

    HashState.prototype= function(){
        this.listen= function( handler ){
            listen( handler )
            return this
        }
        this.forget= function( handler ){
            forget( handler )
            return this
        }
        this.revive= function(){
            schedule()
            return this
        }
        this.freeze= function(){
            freeze()
            return this
        }
        this.put= function(){
            HashState( data.put.apply( data, arguments ) )
            return this
        }
        this.get= function(){
            return data.get.apply( data, arguments )
        }
        this.sub= function(){
            return data.sub.apply( data, arguments )
        }
        this.toString= function(){
            return ''+HashState()
        }
        return this
    }.apply( HashState )

    update()

    Export: return HashState

    Usage:

    HashState.listen( function(){ // observe changing
        console.log( 'state changed to: ' + HashState ) // all state
        console.log( 'filters: ' + HashState.sub( 'filters' ) ) // sub state
        console.log
        (    'order by: '
        +    HashState.get( 'order', 'by' ) // one value
        +    ' '
        +    ( HashState.get( 'order', 'reverse' ) ? 'desc' : 'asc' )
        )
    })
    HashState( '' ) // clear all

    // add parameters
    HashState.put( 'order:by:cost' )
    HashState.put( 'order', 'reverse', true )
    HashState.put( 'filters', { type: 'airplane', free: true } )
    HashState.put( 'filters', '' )

    }

    Also popular now: