Use local files with Browser extensions (kango framework) - json

I'm working on a "browser extension" using "Kango Framework" (http://kangoextensions.com/)
When i want to link a css file i have to use external source (href='http://mysite.com/folder/mysite.css), how should i change the href to make is source from the plugin folder ? (ex: href='mylocalpluginfolder/localfile.css')
i've tried 'localfile.css' and putting the file in the same folder as the JS file.
$("head").append("");
How should i change the json file to make it work ? Should i declare the files as "extended_scripts" or "content_scripts" ?
I've a hard time finding support for this framework, even though the admins are awesome !
Thanks for your help. (please do not suggest to use other solutions, because i won't be able to code plugins for IE and Kango is my only option for this). I didn't find any samples matching my need as the only example available on their site is linking to outside content (christmas tree).

If you want to add CSS in page from content script you should:
Get CSS file contents
Inject CSS code in page
function addStyle(cssCode, id) {
if (id && document.getElementById(id))
return;
var styleElement = document.createElement("style");
styleElement.type = "text/css";
if (id)
styleElement.id = id;
if (styleElement.styleSheet){
styleElement.styleSheet.cssText = cssCode;
}else{
styleElement.appendChild(document.createTextNode(cssCode));
}
var father = null;
var heads = document.getElementsByTagName("head");
if (heads.length>0){
father = heads[0];
}else{
if (typeof document.documentElement!='undefined'){
father = document.documentElement
}else{
var bodies = document.getElementsByTagName("body");
if (bodies.length>0){
father = bodies[0];
}
}
}
if (father!=null)
father.appendChild(styleElement);
}
var details = {
url: 'styles.css',
method: 'GET',
async: true,
contentType: 'text'
};
kango.xhr.send(details, function(data) {
var content = data.response;
kango.console.log(content);
addStyle(content);
});

I do it another way.
I have a JSON containing the styling for specified web sites, when i should change the css.
Using jQuery's CSS gives an advantage on applying CSS, as you may know css() applying in-line css and inline css have a priority over classes and IDs defined in default web pages files and in case of inline CSS it will override them. I find it fine for my needs, you should try.
Using jQuery:
// i keep info in window so making it globally accessible
function SetCSS(){
$.each(window.skinInfo.css, function(tagName, cssProps){
$(tagName).css(cssProps);
});
return;
}
// json format
{
"css":{
"body":{"backgroundColor":"#f0f0f0"},
"#main_feed .post":{"borderBottom":"1px solid #000000"}
}
}

As per kango framework structure, resources must be placed in common/res directory.
Create 'res' folder under src/common folder
Add your css file into it and then access that file using
kango.io.getResourceUrl("res/style.css");
You must add this file into head element of the DOM.
This is done by following way.
// Common function to load local css into head element.
function addToHead (element) {
'use strict';
var head = document.getElementsByTagName('head')[0];
if (head === undefined) {
head = document.createElement('head');
document.getElementsByTagName('html')[0].appendChild(head);
}
head.appendChild(element);
}
// Common function to create css link element dynamically.
function addCss(url) {
var css_tag = document.createElement('link');
css_tag.setAttribute('type', 'text/css');
css_tag.setAttribute('rel', 'stylesheet');
css_tag.setAttribute('href', url);
addToHead(css_tag);
}
And then you can call common function to add your local css file with kango api
// Add css.
addCss(kango.io.getResourceUrl('res/style.css'));

Related

How to add nofollow attribute on CKEditor when modify post

I'm using CKEditor 4.5.5 version.
I add the next code for adding nofollow.
It working fine and stored my DB. but, when I modify the post, CKEditor auto-removed ref="nofollow" attributes.
How can I loading origin attributes on CKEditor?
-- Write page. add nofollow code --
CKEDITOR.on('instanceReady', function () {
CKEDITOR.on('dialogDefinition', function (ev) {
var editor = ev.editor;
editor.dataProcessor.htmlFilter.addRules({
elements: {
a: function (element) {
if (!element.attributes.rel)
element.attributes.rel = 'nofollow';
}
}
});
});
});
You need to modify file ckeditor_config.js.
Add following code:
config.extraAllowedContent = 'a[rel]';

Cesium: Theming the InfoBox

I have seen a few examples on Google Groups which demonstrate how to modify the css of the infobox. In this particular example, javascript is used to append a css link to the head of the document:
https://groups.google.com/forum/#!topic/cesium-dev/f0iODd42PeI
var cssLink = frameDocument.createElement("link");
cssLink.href = buildModuleUrl('Path/To/Your/CSS/File.css');
cssLink.rel = "stylesheet";
cssLink.type = "text/css";
viewer.infoBox.frame.contentDocument.head.appendChild(cssLink);
This, however, has not resulted in any changes to the style of my markup.
At best, I have been able to wrap the contents of the infobox by iterating through the entities in the .then function call subsequent to loading a geoJson dataset. When wrapping the contents, I can set style values which are readily apparent in the resulting markup.
var dataSource = Cesium.GeoJsonDataSource.load('../data/mGeoJson.json').then(function(data) {
viewer.dataSources.add(data);
var entities = data.entities.values;
for (var i = 0; i < entities.length; i++)
var entity = entities[i];
if (entity.properties.hasOwnProperty("description")) {
entity.description = '<div style="height: 360px;">' + entity.properties.description
+ '</div>';
}
}
}
This is useful, but does not completely satisfy the requirements of my app.
Could someone provide additional insight into overriding the theme of the infobox, without having to iterate over entities to modify the value of their description properties?
The original solution here wasn't working, because the infoBox is an iframe that has not yet asynchronously loaded when you were trying to modify it.
Instead, you can add an load listener to the iframe, like this:
var viewer = new Cesium.Viewer('cesiumContainer');
var frame = viewer.infoBox.frame;
frame.addEventListener('load', function () {
var cssLink = frame.contentDocument.createElement('link');
cssLink.href = Cesium.buildModuleUrl('Path/To/Your/CSS/File.css');
cssLink.rel = 'stylesheet';
cssLink.type = 'text/css';
frame.contentDocument.head.appendChild(cssLink);
}, false);
This waits for the iframe to become ready to receive the modification, and then applies it.
For what it's worth, I've found success in modifying the theme of the infobox by simply importing my css files in the head of the document. I'm not sure why I wasn't able to modify it directly with stylesheets, as it wasn't previously affecting the infobox's appearance, and this issue was mirrored in the posts that I found in the cesium-dev Google Group. Regardless, it seems to be working just fine now.

Am I paranoid? "Brutally" big Polymer website after Vulcanize. What can I do to fix it?

Maybe I'm paranoid. I always like to have my code as slim as possible. I always target my websites to be under 1.5 MB (All images compressed and resized as appropriate(. I started working with Polymer the month before thinking that I could shave off those 150 KBs from Bootstrap and 90 KB from jQuery, and have a relatively lightweight site.
I've just vulcanized my elements.html file and I am horrified. The beast is 947KB without images, just bare HTML and JS. I have around 40 custom elements + Couple of the Elements catalog (and I'm not even close to creating new elements). (GZip is 307.40 KB out of 947KB) (Using ASP.NET MVC5 and .NET 4.6).
With a regular 3G connection, it takes about 5.15 seconds to load in Chrome 52 (which is awful). The Polymer Shop demo loads beautifully fast (<3 seconds from cold cache in regular 3G)
First of all, is this acceptable? I'm trying to hit before the 3 second mark (or get to it as close as possible).
Also, there are many JavaScript files that are being loaded part of Vulcanize which I don't need.
I've seen this Gist: Vulcanize and Polymer auto Lazy Loading but I don't know what to do with it.
These are the imports of my elements.html file:
<link rel="import" href="../bower_components/app-route/app-route.html">
<link rel="import" href="../bower_components/app-route/app-location.html">
<link rel="import" href="../bower_components/app-layout/app-drawer-layout/app-drawer-layout.html">
<link rel="import" href="../bower_components/app-layout/app-drawer/app-drawer.html">
<link rel="import" href="./pgarena-drawer/pgarena-drawer.html">
<link rel="import" href="./pgarena-navbar/pgarena-navbar.html">
<link rel="import" href="./pgarena-auth/pgarena-oauth/pgarena-oauth.html">
<link rel="import" href="./routes/pgarena-app.html">
Then all my custom elements (pgarena) have more polymer components built into it.
I've tried several combinations (Only with my elements) and (Only with the Polymer elements shown) and I've had varied results (as expected)
I don't know what to do... Before resorting to a hacky stuff... Any recommendations?
Ok, people, bear with me. This is going to be a long answer. It can become a little bit hairy. First of all, this was a Polymer 1.x solution. I don't know what of this has changed for version 2.0
TL;DR:
We get the URLs of the .HTML and use JavaScript to create the link attribute (HTML import) to load the element. We check with Polymer using Polymer.isInstance(element) to see if the object has been set or not.
Here's the code:
For this to work, I was using iron-pages, and custom JavaScript.
We have our app, as the following:
Note*: The following code I had it in the same file, you can separate it as you wish.
<!-- Main Entry point for the application. This will work as the main "Controller"-->
<link rel="import" href="../../bower_components/polymer/polymer.html">
<link rel="import" href="../../bower_components/app-route/app-location.html">
<link rel="import" href="../../bower_components/app-route/app-route.html">
<link rel="import" href="../../bower_components/iron-pages/iron-pages.html">
<dom-module id="pgarena-app">
<template>
<pgarena-action-config></pgarena-action-config>
<app-route route="{{route}}"
pattern="/:page"
data="{{data}}"
tail="{{tail}}">
</app-route>
<iron-pages selected="[[data.page]]" attr-for-selected="title" fallback-selection="404">
<pgarena-home-app title="" route="[[tail]]"></pgarena-home-app>
<pgarena-tournament-app title="tournaments" route="[[tail]]"></pgarena-tournament-app>
<!--<pgarena-clash-app title="clash" route="[[tail]]"></pgarena-clash-app>-->
<pgarena-account-app title="account" route="[[tail]]"><content></content></pgarena-account-app>
<pgarena-teams-app title="teams" route="[[tail]]"></pgarena-teams-app>
<div title="404">
<h1>{{data.page}} could not be found!</h1>
</div>
</iron-pages>
</template>
<script>
(function () {
'use strict';
Polymer({
is: 'pgarena-app',
ready: function () {
/* console.log("Route is ");
console.log(this.data.page);
console.log(this.tail);*/
document.addEventListener('iron-select',
function (event) {
/*
console.log("---------------------");
console.log(event);
console.log("---------------------");*/
var element = getSelectedElement(event);
var tagName = element.tagName.toLowerCase();
LazyLoad(Polymer, element, tagName, event.target);
});
}
});
})();
</script>
Let's get some things first:
My app is called: "pgarena-app"
I don't know if this has been fixed, but the app-route element has a problem with two-way data binding. Meaning that for iron-pages, I had to use the double brackets [[]] to one-way data bind.
App route passes the information from the url to iron-pages so it can toggle the different elements.
This is not mandatory, and I don't know if this is the right way to do it. I divided my application into "views", which are elements by itself. They load all the elements required to that "view" by. Note: This has no effect, whatsoever in lazy loading.
Note, that the elements are not included in the URL, because we're going to lazy load them.
Let's go to the JavaScript portion of this element:
<script>
(function () {
'use strict';
Polymer({
is: 'pgarena-app',
ready: function () {
document.addEventListener('iron-select',
function (event) {
var element = getSelectedElement(event);
var tagName = element.tagName.toLowerCase();
LazyLoad(Polymer, element, tagName, event.target);
});
}
});
})();
</script>
The code is simple in here. We define our element, and we listen to the iron select event. This signals us that iron-page has been selected. We lazy load the element if it's not there. The magic behind this, is in the custom LazyLoad JavaScript, which is below.
<script>
/**
* Defines all the routes of the imports in here
*
* This is how it goes: The Key name is the tag name of the element.
* The value is the relative URL from the elements folder.
*
* You then get the element's tag name and look for it.
*
* DO NOT PUT TRAILING SLASH BEFORE THE URL! Thanks :)
**/
var PGArena = PGArena || {};
PGArena.LazyLoad =
{
"pgarena-home-app": "routes/home/pgarena-home-app.html",
"pgarena-tournament-app": "routes/tournament/pgarena-tournament-app.html",
"pgarena-account-app": "routes/account/pgarena-account-app.html",
"pgarena-clash-app": "routes/clash/pgarena-clash-app.html",
"pgarena-teams-app": "routes/teams/pgarena-teams-app.html",
"pgarena-tournament-index-view": "views/tournament/pgarena-tournament-index-view/pgarena-tournament-index-view.html",
"pgarena-tournament-list-view": "views/tournament/pgarena-tournament-list-view/pgarena-tournament-list-view.html",
"pgarena-account-index-view": "views/account/pgarena-account-index-view/pgarena-account-index-view.html",
"pgarena-account-login-view": "views/account/pgarena-account-login-view/pgarena-account-login-view.html",
"pgarena-account-register-view": "views/account/pgarena-account-register-view/pgarena-account-register-view.html",
"pgarena-account-confirm-email-view": "views/account/pgarena-account-confirm-email-view/pgarena-account-confirm-email-view.html",
"pgarena-account-oauth-view": "views/account/pgarena-account-oauth-view/pgarena-account-oauth-view.html",
"pgarena-clash-index-view": "views/clash/pgarena-clash-index-view/pgarena-clash-index-view.html",
"pgarena-clash-brawl-view": "views/clash/pgarena-clash-brawl-view/pgarena-clash-brawl-view.html",
"pgarena-teams-index-view": "views/team/pgarena-teams-index-view/pgarena-teams-index-view.html",
"pgarena-teams-create-view": "views/team/pgarena-teams-create-view/pgarena-teams-create-view.html"
};
/**
* This variable keeps track of all the vulcanized elements.
*
**/
PGArena.Vulcanized = {
}
/**
* Global Placeholder for checking which is the selected item of the iron-selectors that
are ready for lazy loading.
**/
PGArena.IronSelected = {
}
/**
* LazyLoad
*
* Lazy Loads the elements as needed. This function is triggered by iron-select
* event. If the element is already registered, then it is not loaded again.
*
* Polymer => Dependency Injection of the Polymer object. (Polymer itself)
* element => The element (DOM-wise: a.k.a tags with everything)
* elementName => The element's name.
* selectorTrigger => The element who triggered the select.
**/
function LazyLoad(Polymer, element, elementName, selectorTrigger) {
if (Polymer.isInstance(element)) {
// console.log(elementName + " is already registered ;)");
return;
} else {
//console.log(elementName+" isn't registered. On its way for Lazy Loading!");
}
//console.log("Lazy Load Started");
var hasProp = PGArena.LazyLoad.hasOwnProperty(elementName);
if (!hasProp) {
console.log("Property " + elementName + " not found for Lazy Loading");
return;
}
var href = PGArena.LazyLoad[elementName];
LoadImportAsync(href, elementName, selectorTrigger);
}
function Spinner(elementName, active) {
var paperId = 'js-' + elementName;
var queryName = active ? elementName : paperId;
var createElem = active ? 'paper-spinner-lite' : elementName;
var elem = document.querySelector(queryName);
var spinner = document.createElement(createElem);
spinner.setAttribute('active', '');
if (elem === null || elem === undefined)
return;
console.log("Element Name is");
console.log(queryName);
console.log("Element is");
console.log(elem);
console.log("Spinner is:");
console.log(spinner);
if (active) {
spinner.setAttribute('id', 'js-' + elementName);
console.log("replacing time");
elem.parentNode.replaceChild(document.createTextNode("Caca"), elem);
//elem.parentNode.replaceChild(spinner, elem);
}
else {
console.log("Replaced");
//elem.parentNode.replaceChild(elem, spinner);
}
}
function ForcedLoad() {
}
/**
* Loads the required import and appends it to the document. It really doesn't
* matter where it is appended.
*
**/
function LoadImportAsync(href, elementName) {
var link = document.createElement('link');
link.rel = 'import';
link.href = getBaseUrl() + "/NodeJS/Polymer/app/elements/" + href;
link.setAttribute('async', ''); // make it async!
link.onload = function () { Spinner(elementName, false); }
link.onerror = function (e) { console.log("There was an error loading " + elementName + ". Please Check the URL") };
document.head.appendChild(link);
}
function getBaseUrl() {
var pathArray = location.href.split('/');
var protocol = pathArray[0];
var host = pathArray[2];
return protocol + '//' + host;
}
/**
* On non-blink browsers (a.k.a Firefox , Edge, Internet Explorer)
* The event.srcElement is undefined. We need to search for it ourselves.
*
* The way we do that is that we get the current targetted element which is the iron form.
* Retrieve its selection mechanism and the supposed element's index.
*
* We proceed by query Selecting the element in the DOM all the way until we nab it.
* Then we are faced with the next challenge. We don't know if the element is using an
* index-based approach (0, 1, 2...) or an attribute approach(title="home", title="tournament",etc.)
*
* So we proceed to fetch its selection mechanism by grabbing the attrForSelected. If null, it means that
* it is using the index-based approach. We continue and get the children position at the element.
*
* Note that selectedAttr variable will return me either the index or the selected attribute's value.
* So it's going to be 0, 1, 2 if using the index based approach.
*
**/
function getSelectedElement(event) {
if (event.srcElement !== undefined)
return event.srcElement.selectedItem;
var element = event.target;
//Get the current selected attribute:
var selectedAttr = element.selected;
//Gets the attribute that is being used for selection:
var attrForSelected = element.attrForSelected;
//This means that it is not using index based
if (attrForSelected !== null) {
return element.querySelector('[' + attrForSelected + '="' + selectedAttr + '"]');
}
//Continues using index based:
var childelem = element.children[parseInt(selectedAttr)];
return childelem;
}
</script>
The first thing we do is to define the URLs relative to the document I have. I do this by defining a json with a key whose name is the title attribute of the iron-pages and the value with the relative URL to this document (the pgarena-app).
What I mean, is that in the case I want to load pgarena-tournament-appand my pgarena-app (my main entry point to the application) is in www/polymer/pgarena-app.htmland my pgarena-tournament-app is in www/polymer/routes/tournament/pgarena-tournament-app.html, since this is relative my JSON will be:
var PGArena = PGArena || {};
PGArena.LazyLoad =
{
"tournament" : "routes/tournament/pgarena-tournament-app.html",
};
Note PGArena.LazyLoad can be anything, this is a global variable I defined with the PGArena namespace.
Then, we see that the code LazyLoad is called:
function LazyLoad(Polymer, element, elementName, selectorTrigger) {
if (Polymer.isInstance(element)) {
// console.log(elementName + " is already registered ;)");
return;
} else {
//console.log(elementName+" isn't registered. On its way for Lazy Loading!");
}
//console.log("Lazy Load Started");
var hasProp = PGArena.LazyLoad.hasOwnProperty(elementName);
if (!hasProp) {
console.log("Property " + elementName + " not found for Lazy Loading");
return;
}
var href = PGArena.LazyLoad[elementName];
LoadImportAsync(href, elementName, selectorTrigger);
}
What I do in here is to check if the Element that I want to lazy load has been referenced in the JSON I defined (PGarena.LazyLoad). If it's not in there, then what I do is that I log that message. If it's there, and it has not loaded, then I Asynchronously load it by creating the HTML import and appending it to the head:
/**
* Loads the required import and appends it to the document. It really doesn't
* matter where it is appended.
*
**/
function LoadImportAsync(href, elementName) {
var link = document.createElement('link');
link.rel = 'import';
link.href = getBaseUrl() + "/NodeJS/Polymer/app/elements/" + href;
link.setAttribute('async', ''); // make it async!
link.onload = function () { Spinner(elementName, false); }
link.onerror = function (e) { console.log("There was an error loading " + elementName + ". Please Check the URL") };
document.head.appendChild(link);
}
Please notice (I don't know if they've fixed this). There's a polyfill for HTML imports for Firefox, and Edge, and Safari(I believe). The polyfill uses XHR (AJAX) for loading the imports!!! I mention this because at the beginning I tried to intercept the HTML Imports and in Google Chrome it didn't work.
Let me know if you need anything else. As you can see, I tried to use the spinner, but I didn't get it to work. Hope this helps!
I'd start with the fact that Vulcanize only merges files together by default. Have you added the switches to remove comments for example?
Also, you would want to minify you bundled HTML and JS files. Most example will show you a Gulp setup but you could just as well minify the vulcanized files in a second step in an npm script.
Maybe this post will help: https://jongeho.wordpress.com/2015/06/24/endeavors-with-polymer-1-0-vulcanize-crisper-html-minifier/
That said, it is true that a Web Component-rich app will naturally be quite large. It is something I've also been noticing

html5 template tag: fallback for content access

I am using a template tag in a webkit browser (JavaFX WebView 2.2) to store elements that I may clone and append on the main part of the document.
However, I can't access its content using templateElement.content (the HTML5 standard). Instead, I use jQuery to get the elements inside the template TAG with the selector "#templateElement div".
Seems the template tag is not yet fully supported (inner scripts also run), although its contents are not rendered.
My fear is that, when the template tag becomes supported, the way to get its contents will break and my page will stop working.
What is the recommended way of getting template contents regardless future implementation changes?
HTML:
<template id="templateElement">
<div>Clone Me!</div>
</template>
JavaScript:
function getContentsCurrent() {
var toBeCloned = $("#templateElement div")[0];
//append where needed...
}
function getContentsFuture() {
var toBeCloned = templateElement.content.getElementsByTagName("div")[0];
//append where needed...
}
EDIT
I think jQuery won't be able to handle this automatically, even in the future, because the template "innerHTML" is purposely routed to content so that it becomes inaccessible to the DOM (so no selector touches it accidentally).
You could test if the content feature exists before:
function getContents() {
var toBeCloned;
if ( templateElement.content )
toBeCloned = templateElement.content.getElementsByTagName("div")[0];
else
toBeCloned = templateElement.querySelector("div");
//append where needed...
}
Another way:
var content = templateElement.content || templateElement
var toBeCloned = content.querySelector( "div" )
//...

Lifehacker implemention of url change with Ajax

I see that Lifehacker is able to change the url while using AJAX to update part of the page. I guess that can be implemented using HTML5 or history.js plugin, but I guess lifehacker is using neither.
Does any one has a clue on how they do it?
I am new to AJAX and just managed to update part of the page using Ajax.
Thank you #Robin Anderson for a detailed step by step algo. I tried it and it is working fine. However, before I can test it on production, I would like to run by you the code that I have. Did I do everything right?
<script type="text/javascript">
var httpRequest;
var globalurl;
function makeRequest(url) {
globalurl = url;
/* my custom script that retrieves original page without formatting (just data, no templates) */
finalurl = '/content.php?fname=' + url ;
if(window.XMLHttpRequest){httpRequest=new XMLHttpRequest}else if(window.ActiveXObject){try{httpRequest=new ActiveXObject("Msxml2.XMLHTTP")}catch(e){try{httpRequest=new ActiveXObject("Microsoft.XMLHTTP")}catch(e){}}}
/* if no html5 support, just load the page without ajax*/
if (!(httpRequest && window.history && window.history.pushState)) {
document.href = url;
return false;
}
httpRequest.onreadystatechange = alertContents;
alert(finalurl); /* to make sure, content is being retrieved from ajax */
httpRequest.open('GET', finalurl);
httpRequest.send();
}
/* for support to back button and forward button in browser */
window.onpopstate = function(event) {
if (event.state !== null) {
document.getElementById("ajright").innerHTML = event.state.data;
} else {
document.location.href = globalurl;
return false;
};
};
/* display content in div */
function alertContents() {
if (httpRequest.readyState === 4) {
if (httpRequest.status === 200) {
var stateObj = { data: httpRequest.responseText};
history.pushState(stateObj, "", globalurl);
document.getElementById("ajright").innerHTML = httpRequest.responseText;
} else {
alert('There was a problem with the request.');
}
}
}
</script>
PS: I do not know how to paste code in comment, so I added it here.
It is not an requirement to have the markup as HTML5 in order to use the history API in the browser even if it is an HTML5 feature.
One really quick and simple implementation of making all page transistions load with AJAX is:
Hook up all links except where rel="external" exist to the function "ChangePage"
When ChangePage is triggered, check if history API is supported in the browser.
If history API isn't supported, do either push a hashtag or make a normal full page load as fallback.
If history API is supported:
Prevent the normal link behaviour.
Push the new URL to the browser history.
Make a AJAX request to the new URL and fetch its content.
Look for your content div (or similar element) in the response, take the HTML from that and replace the HTML of the corresponding element on the current page with the new one.
This will be easy to implement, easy to manage caches and work well with Google's robots, the downside is that is isn't that "optimized" and it will be some overhead on the responses (compared to a more complex solution) when you change pages.
Will also have backward compatibility, so old browsers or "non javascript visitors" will just get normal page loads.
Interesting links on the subject
History API Compatibility in different browsers
Mozillas documentation of the History API
Edit:
Another thing worth mentioning is that you shouldn't use this together with ASP .Net Web Forms applications, will probably screw up the postback handling.
Code addition:
I have put together a small demo of this functionality which you can find here.
It simply uses HTML, Javascript (jQuery) and a tiny bit of CSS, I would probably recommend you to test it before using it. But I have checked it some in Chrome and it seems to work decent.
Some testing I would recommend is:
Test in the good browsers, Chrome and Firefox.
Test it in a legacy browser such as IE7
Test it without Javascript enabled (just install Noscript or similar to Chrome/Firefox)
Here is the javascript I used to achieve this, you can find the full source in the demo above.
/*
The arguments are:
url: The url to pull new content from
doPushState: If a new state should be pushed to the browser, true on links and false on normal state changes such as forward and back.
*/
function changePage(url, doPushState, defaultEvent)
{
if (!history.pushState) { //Compatability check
return true; //pushState isn't supported, fallback to normal page load
}
if (defaultEvent != null) {
defaultEvent.preventDefault(); //Someone passed in a default event, stop it from executing
}
if (doPushState) { //If we are supposed to push the state or not
var stateObj = { type: "custom" };
history.pushState(stateObj, "Title", url); //Push the new state to the browser
}
//Make a GET request to the url which was passed in
$.get(url, function(response) {
var newContent = $(response).find(".content"); //Find the content section of the response
var contentWrapper = $("#content-wrapper"); //Find the content-wrapper where we are supposed to change the content.
var oldContent = contentWrapper.find(".content"); //Find the old content which we should replace.
oldContent.fadeOut(300, function() { //Make a pretty fade out of the old content
oldContent.remove(); //Remove it once it is done
contentWrapper.append(newContent.hide()); //Add our new content, hidden
newContent.fadeIn(300); //Fade it in!
});
});
}
//We hook up our events in here
$(function() {
$(".generated").html(new Date().getTime()); //This is just to present that it's actually working.
//Bind all links to use our changePage function except rel="external"
$("a[rel!='external']").live("click", function (e) {
changePage($(this).attr("href"), true, e);
});
//Bind "popstate", it is the browsers back and forward
window.onpopstate = function (e) {
if (e.state != null) {
changePage(document.location, false, null);
}
}
});
The DOCTYPE has no effect on which features the page can use.
They probably use the HTML5 History API directly.