-
Notifications
You must be signed in to change notification settings - Fork 0
Home
Depends on jQuery 1.3+
<script src="kobayashi.js"></script>
<script>Kobayashi.DEFAULT_TARGET = 'content'</script>
<a href="#/foo/">Load /foo/ into #content</a>
<div id="content"></div>
Kobayashi enables history and bookmarks with AJAX and automates loading of HTML chunks into a webpage. It simplifies building AJAX web applications in a RESTful fashion.
The typical scenario for employing Kobayashi is when you have a few pages with the same layout, menu, header, footer and just want the menu links to switch content in the “content” div. Without AJAX, you’d have to reload the whole pages with all they have in common. With AJAX, you wouldn’t be able to use the back and forward keys and to bookmark pages.
The problem of many HTML pages sharing a lot of content is often solved with PHP. You can see a lot of URLs like this:
http://example.com/index.php?page=intro
http://example.com/index.php?page=about
http://example.com/index.php?page=links
Here, index.php most likely has the common stuff in it and pages intro.html
, about.html
and links.html
may contain the differing contents. Notice that the URLs are pretty descriptive — you know you’re on the root, accessing a page named intro, about or links. The obvious drawback is that the common stuff is always sent from server to client whenever another page is requested.
Kobayashi does this for you on the client side. If intro.html
, about.html
and links.html
(without the layout) are accessible at http://example.com/intro.html
, http://example.com/about.html
and http://example.com/links.html
, you can use addresses like these to achieve the same thing with Kobayashi:
http://example.com/index.html#/intro.html
http://example.com/index.html#/about.html
http://example.com/index.html#/links.html
Here, index.html could look something like this:
<html> <head> <script src="kobayashi.js"></script> <script>Kobayashi.DEFAULT_TARGET = 'content'</script> <title>Kobayashi Demo</title> </head> <body> <ul id="menu" class="js-hashadr-container"> <li><a href="intro.html">Intro</a></li> <li><a href="about.html">About</a></li> <li><a href="links.html">Links</a></li> </ul> <div id="content">Welcome to Kobayashi demo!</div> </body> </html>
The tricky parts are
- setting
Kobayashi.DEFAULT_TARGET
tocontent
, which is the ID of the div where the pages are going to be loaded and - setting
class="js-hashadr-container"
to the menu ul, which changes the behavior of all links within it to merely alter the hash part of the URL.
Kobayashi watches for the URL hash part to change and loads the appropriate page to the “content” div. Thus, links, bookmarks and history work as desired, the page is not unnecessarily reloaded and no extra programming is required.
This is the core functionality of Kobayashi. However, to be of use for building a realistic web application,it provides many more features. Read on to learn about them.
Links (<a>) with class="js-hashadr"
or inside an element with class="js-hashadr-container"
will call function adr
passing it their href attribute as parameter and stop following the link.
Class js-nohashadr
cancels the effect of js-hashadr-container
. This enables a link to be excluded from the hash-address functionality.
This will change location.hash and load stuff into an appropriate element (Kobayashi.DEFAULT_TARGET
by default), reflecting this at the URL in the address bar.
Links with class="js-simpleload"
or inside an element with class="js-simpleload-container"
will call function js-simpleload
passing it their href attribute as parameter and stop following the link.
This will load stuff into an appropriate element, not affecting the URL in the address bar.
If the target element is hidden (display: none;
), it is shown by default upon injection of content.
Presence of the js-noautoshow
class overrides this.
Whenever content is loaded as a result of hashchange
event or simple_load
function, the custom content_added
event is triggered.
When loading content is attempted but the request fails, the load_content_failed
event is triggered instead.
Classes:
- js-hashadr
- js-hashadr-container
- js-simpleload
- js-simpleload-container
- js-noautoshow
Say your web application lives on http://example.com/export/applications/myeshop/
. The things you want to load with Kobayashi are at http://example.com/export/applications/myeshop/procucts
and http://example.com/export/applications/myeshop/prices
. Then the links would have to look like this:
http://example.com/export/applications/myeshop/#/export/applications/myeshop/products/
http://example.com/export/applications/myeshop/#/export/applications/myeshop/prices/
The repetition of the path is not very nice. To please your eye, Kobayashi detects a global variable BASE_URL
, which in this case should be /export/applications/myeshop/
. If it was set to this value, then you should use the links in this form instead:
http://example.com/export/applications/myeshop/#/products/
http://example.com/export/applications/myeshop/#/prices/
To enable you to note the user that content is being loaded, three events are available:
- show_loading
- dec_loading
- hide_loading
The show_loading
event is triggered whenever loading an URL is commenced. It can be triggered repeatedly, when several URLs are being requested from one hash change.
The dec_loading
event is triggered whenever loading an URL has terminated — successfully or not.
The hide_loading
event is triggered when all loading has been terminated. It is not normally triggered at all, only in exceptional cases.
You may want to condition showing a “loading” message on a counter’s positive value. It should be initialized to zero, increased on show_loading
, decreased on dec_loading
and set to zero on hide_loading
.
location.hash is interpreted as a sequence of specifiers delimited by “#
” characters.
Each of those specifiers is passed to the adr
function, see below.
The requests are done in parallel but inserted into DOM in the order they appear in the hash string, which allows you to load things into an element which is yet to be loaded. An example:
Say you have <div id="talk">
under URL /comment-container/
and you have <p>first comment</p><p>second comment</p>
under URL /comments/
.
Of course you want the result to be <div id="talk"><p>first comment</p><p>second comment</p></div>
. You can achieve this simply by requesting address:
#/comment-container/#talk::/comments/
This will work even though the div #talk isn’t in the DOM yet and even if the request for /comments/ finishes before /comment-container/.
It is assumed here that Kobayashi.DEFAULT_TARGET
is set to "content"
.
Function adr
is the cornerstone of handling addresses in the specifiers in location.hash
.
It receives two arguments:
- A specifier
- Options object
Specifiers can be in three distinct formats:
-
URL
Example:/articles/article/
- target_id::URL
Example:passwd-container::/password_change/
- target_id::relative-address-base::URL
Example1:history::content::history/
Example2:history::::history/
Format 1 is a shorthand for content::URL
, i.e. omitting the target ID substitutes value of “Kobayashi.DEFAULT_TARGET
” as a default.
Specifier in format 2 says that an URL should be loaded from the given URL and the response injected into the element with the given ID.
Format 3 makes it possible to use a relative address to address from another specifier.
Relative address operate on the address up to the first “?” or “#” character in location.href.
Suppose you are at this location: http://example.com/#content::/articles/article/118/
and you want to load a deletion confirmation to the “delete-dialog
” element. Say the delete confirmation can be loaded from
/articles/article/118/delete
. Using a relative address directly, like at
<a class="hashadr" href="delete-dialog::delete/">
would result in this address being used:
http://example.com/#content::/articles/article/118/#delete-dialog::delete/
and that would load
http://example.com/delete/
into #delete-dialog, which is not what you need.
Enter format 3: saying <a class="hashadr" href="delete-dialog::content::delete/">
means:
Take the address loaded into #content
, take it as a base for the relative address “delete/
” and load it into “#delete-dialog
”.
Leaving the middle part (relative-base) empty is the same as putting content
inside, so
delete-dialog::content::delete/
could be equally written as delete-dialog::::delete/
.
Applying relative addresses is hand-implemented in JavaScript, so it can behave differently than you might expect. Test is before you apply something wild.
Double dot (..) cuts off the last directory, so applying ../foo/
on /a/b/
won’t result to /a/b/../foo/
but to /a/foo/
.
Get parameters can be added conveniently: start the address with the &
sign.
If you’re at
/foo/?color=green&shape=square
and you apply
&color=blue&taste=bitter
, you get
/foo/?color=blue&shape=square&taste=bitter
Second parameter for the adr
function — options — is optional. If present, it must be an object. These keys are recognized:
- hash
- just_get
- just_set
Normally, function adr
looks at location.hash
, modifies it as the specifier dictates and assigns the result back to location.hash
.
options.hash
and options.just_get
override this.
Set options.hash
to whatever string you want function adr
to look at instead of location.hash
.
Example:
You’re at: http://example.com/myapp/#this#is#irrelevant
You call: adr('../oaxaca/', {hash: '/america/mexico/tasco/'})
You’re sent to: http://example.com/myapp/#/america/mexico/oaxaca/
Set options.just_get
to ‘hash’ if you want function adr
to return the resulting hash (with all specifiers) instead of applying it to location.hash
.
Example:
You’re at: http://example.com/myapp/#/index/#gallery::/trips/2009/
You call: adr('gallery::../2008/', {just_get: 'hash'})
You get: #/index/#gallery::/trips/2008/
location will not change.
Set options.just_get
to ‘address’ if you want function adr
to return just the resulting address of the one affected specifier instead of applying it to location.hash
.
Example:
You’re at: http://example.com/myapp/#/index/#gallery::/trips/2009/
You call: adr('gallery::../2008/', {just_get: 'address'})
You get: /trips/2008/
location will not change.
Set options.just_set
to true to prevent the hashchange
event from being triggered. This will prevent loading any content based on the hash change. Upon next hashchange event, the new state will be recognized though.
Example:
You’re at: http://example.com/myapp/#/america/mexico/tasco/
You call: adr('../oaxaca/', {just_set: true})
location is changed to http://example.com/myapp/#/america/mexico/oaxaca/
but no request is made and the content will stay intact as if you stayed in Tasco.
Later you call: adr('#right-column::/comments/')
You are sent to: http://example.com/myapp/#/america/mexico/oaxaca/#right-column::/comments/
and comments will be loaded to the right column and oaxaca will be loaded into content.
Set options.nohistory
to true if you want this change in the location string to be ignored by the browser’s history management. This is useful for redirects.
Example:
You’re at: http://example.com/myapp/#/america/mexico/tasco/
You call: adr('../')
You get to: http://example.com/myapp/#/america/mexico/
Then you call: adr('mexico-city', {nohistory: true})
You get to: http://example.com/myapp/#/america/mexico/mexico-city
You press the back button.
You get to: http://example.com/myapp/#/america/mexico/tasco/
A shorthand for adr(_specifier_, {just_get:true})
is available: get_hashadr
.
Function get_adr
does the same and prepends BASE_PATH
in front of the result.
Both of these accept the same arguments as adr
, so you can still set options.hash
.
Use function get_adr
to get addresses directly usable in conventional hyperlinks.
Normal 302 responses from the server affect AJAX requests in the same way as normal requests, so redirecting in this way works normally, however Kobayashi will not be aware of the redirection and will not update the hash. This means that if you come to /~john
which redirects to /user/john/
, then /user/john/
will load OK but the hash will still show /~john
as originally requested.
To remedy this, Kobayashi recognizes special HTTP response header Redirect-To
(case sensitive). If your response is a success (e.g. status 200) and in the headers, Redirect-To: /user/john/
is found, then redirection is performed by Kobayashi, reflecting the change to the URL. Optional callbacks as well as chained requests should work normally, i.e. as if the request was atomic.
The value of the Redirect-To
header should contain an optional BASE_URL, which will be stripped off for the URL.
Since everything is loaded into a non-reloading page, all needed CSS and JavaScript files must be loaded at the starting page. This is impractical and slows down the initial load. On the other hand, if the JavaScripts were inside the loaded pieces, then they would be loaded and run repeatedly, which is undesired.
To remedy this, the request_media
function is provided.
Its only argument is an URL of a medium to be loaded. The type of the medium is recognized by the suffix of the URL. Currently, only .js and .css files are recognized. Re-requesting already loaded media is ignored. When the requested media are loaded, the media_loaded
event is fired. Calling the function several times in a row results in sequentially loading the media, waiting for each other to address library dependencies and CSS priorities. The media_loaded event is fired after the whole bunch of media are loaded.
If a library needs to re-run some code when new content is loaded, it should not rely on request_media
being called. Rather, it should bind a desired function to the content_added
event.
- Kobayashi
The object encapsulating the library’s interface - Kobayashi.DEFAULT_TARGET
ID of the element where AJAX’ed stuff is injected if no target is specified. - Kobayashi.ADDRESS_POSTPROCESS
An object mapping addresses (that are to appear in hash specifiers) to their postprocessed variants.
location.hash will be automatically updated when an address to be postprocessed is requested. - Kobayashi.LOADED_URLS
An object mapping id’s of elements into addresses loaded into them. - Kobayashi.URL_LOADED_BY_HASH
An object mapping IDs of elements where stuff has been loaded to boolean values indicating whether this address is reflected in the URL hash part. False indicates address loaded bysimple_load
. - function Kobayashi.load_content(args)
Commences an XMLHttpRequest and schedules it to be loaded into DOM when it finishes and when all previous chunks have been inserted.
The only argument is an object, where some keys are recognized and which is passed intact to the XMLHttpRequest asoriginal_options
.
Recognized fields:
-
target_id
: ID of the element where the result should be loaded. This is the only required field. -
address
: The URL (good without http:// and all that). If it is omitted, it is interpreted as a request to return the target element to its original state. This is achieved by loading BASES[ target_id ] or if it is not defined, then reloading the whole page. -
success_callback
: Function to be executed after the loaded content is successfull injected into DOM. -
error_callback
: Function to be executed if loading the content fails. This and the above-mentioned callback receives thearg
object passed toload_content
asthis
. It contains axhr
field containing the XMLHttpRequest.
-
- function Kobayashi.reload_content(container_id)
If an URL is loaded into the container given by its id, then it is reloaded,
otherwise its BASE_URL is loaded. If no BASE_URL is defined either, the whole page is reloaded. - function Kobayashi.unload_content(container_id, options)
Deletes the record of any address being loaded into the corresponding container.
One effect of this is that subsequent requests for loading are actually executed.
Another effect is that the container is emptied. This can be prevented by setting options.keep_content to true. - function Kobayashi.simple_load(specifier)
The function called on click on an
<a class="js-simpleload">
. - function closest_loaded(element)
Given an HTML element, returns the closest ancestor that was loaded via AJAX. Returns null if element has not been dynamically loaded.
- function adr(address, options)
- function get_hashadr(address, options)
- function get_adr(address, options)
- function request_media(url, success_callback, error_callback)
See Media Loading
- function arr2map(array)
A general-purpose function for converting
[a,b,c]
arrays into{a:1,b:2,c:1}
objects. - function alert_dump(object, name)
Debugging function. alerts the keys and values of an object.
- function carp(anything, …)
Passes its arguments to console.log and appends them to the
#debug
div. - BASE_URL
The path to the index page, with trailing slash
- BASE_PATH
Like BASE_URL only without trailing slash
- function prepend_base_path_to(url)