1
0
mirror of https://git.tt-rss.org/git/tt-rss.git synced 2025-12-14 08:05:56 +00:00

build custom layer of Dojo to speed up loading of tt-rss (refs #293)

This commit is contained in:
Andrew Dolgov
2011-03-04 19:02:28 +03:00
parent cfad9259a6
commit a089699c89
144 changed files with 55627 additions and 13766 deletions

View File

@@ -5,254 +5,406 @@
*/
if(!dojo._hasResource["dojo.back"]){
dojo._hasResource["dojo.back"]=true;
if(!dojo._hasResource["dojo.back"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dojo.back"] = true;
dojo.provide("dojo.back");
(function(){
var _1=dojo.back;
function _2(){
var h=window.location.hash;
if(h.charAt(0)=="#"){
h=h.substring(1);
/*=====
dojo.back = {
// summary: Browser history management resources
}
return dojo.isMozilla?h:decodeURIComponent(h);
};
function _3(h){
if(!h){
h="";
}
window.location.hash=encodeURIComponent(h);
_4=history.length;
};
if(dojo.exists("tests.back-hash")){
_1.getHash=_2;
_1.setHash=_3;
}
var _5=(typeof (window)!=="undefined")?window.location.href:"";
var _6=(typeof (window)!=="undefined")?_2():"";
var _7=null;
var _8=null;
var _9=null;
var _a=null;
var _b=[];
var _c=[];
var _d=false;
var _e=false;
var _4;
function _f(){
var _10=_c.pop();
if(!_10){
return;
}
var _11=_c[_c.length-1];
if(!_11&&_c.length==0){
_11=_7;
}
if(_11){
if(_11.kwArgs["back"]){
_11.kwArgs["back"]();
}else{
if(_11.kwArgs["backButton"]){
_11.kwArgs["backButton"]();
}else{
if(_11.kwArgs["handle"]){
_11.kwArgs.handle("back");
}
}
}
}
_b.push(_10);
};
_1.goBack=_f;
function _12(){
var _13=_b.pop();
if(!_13){
return;
}
if(_13.kwArgs["forward"]){
_13.kwArgs.forward();
}else{
if(_13.kwArgs["forwardButton"]){
_13.kwArgs.forwardButton();
}else{
if(_13.kwArgs["handle"]){
_13.kwArgs.handle("forward");
}
}
}
_c.push(_13);
};
_1.goForward=_12;
function _14(url,_15,_16){
return {"url":url,"kwArgs":_15,"urlHash":_16};
};
function _17(url){
var _18=url.split("?");
if(_18.length<2){
return null;
}else{
return _18[1];
}
};
function _19(){
var url=(dojo.config["dojoIframeHistoryUrl"]||dojo.moduleUrl("dojo","resources/iframe_history.html"))+"?"+(new Date()).getTime();
_d=true;
if(_a){
dojo.isWebKit?_a.location=url:window.frames[_a.name].location=url;
}else{
}
return url;
};
function _1a(){
if(!_e){
var hsl=_c.length;
var _1b=_2();
if((_1b===_6||window.location.href==_5)&&(hsl==1)){
_f();
return;
}
if(_b.length>0){
if(_b[_b.length-1].urlHash===_1b){
_12();
return;
}
}
if((hsl>=2)&&(_c[hsl-2])){
if(_c[hsl-2].urlHash===_1b){
_f();
return;
}
}
if(dojo.isSafari&&dojo.isSafari<3){
var _1c=history.length;
if(_1c>_4){
_12();
}else{
if(_1c<_4){
_f();
}
}
_4=_1c;
}
}
};
_1.init=function(){
if(dojo.byId("dj_history")){
return;
}
var src=dojo.config["dojoIframeHistoryUrl"]||dojo.moduleUrl("dojo","resources/iframe_history.html");
if(dojo._postLoad){
console.error("dojo.back.init() must be called before the DOM has loaded. "+"If using xdomain loading or djConfig.debugAtAllCosts, include dojo.back "+"in a build layer.");
}else{
document.write("<iframe style=\"border:0;width:1px;height:1px;position:absolute;visibility:hidden;bottom:0;right:0;\" name=\"dj_history\" id=\"dj_history\" src=\""+src+"\"></iframe>");
}
};
_1.setInitialState=function(_1d){
_7=_14(_5,_1d,_6);
};
_1.addToHistory=function(_1e){
_b=[];
var _1f=null;
var url=null;
if(!_a){
if(dojo.config["useXDomain"]&&!dojo.config["dojoIframeHistoryUrl"]){
console.warn("dojo.back: When using cross-domain Dojo builds,"+" please save iframe_history.html to your domain and set djConfig.dojoIframeHistoryUrl"+" to the path on your domain to iframe_history.html");
}
_a=window.frames["dj_history"];
}
if(!_9){
_9=dojo.create("a",{style:{display:"none"}},dojo.body());
}
if(_1e["changeUrl"]){
_1f=""+((_1e["changeUrl"]!==true)?_1e["changeUrl"]:(new Date()).getTime());
if(_c.length==0&&_7.urlHash==_1f){
_7=_14(url,_1e,_1f);
return;
}else{
if(_c.length>0&&_c[_c.length-1].urlHash==_1f){
_c[_c.length-1]=_14(url,_1e,_1f);
return;
}
}
_e=true;
setTimeout(function(){
_3(_1f);
_e=false;
},1);
_9.href=_1f;
if(dojo.isIE){
url=_19();
var _20=_1e["back"]||_1e["backButton"]||_1e["handle"];
var tcb=function(_21){
if(_2()!=""){
setTimeout(function(){
_3(_1f);
},1);
}
_20.apply(this,[_21]);
};
if(_1e["back"]){
_1e.back=tcb;
}else{
if(_1e["backButton"]){
_1e.backButton=tcb;
}else{
if(_1e["handle"]){
_1e.handle=tcb;
}
}
}
var _22=_1e["forward"]||_1e["forwardButton"]||_1e["handle"];
var tfw=function(_23){
if(_2()!=""){
_3(_1f);
}
if(_22){
_22.apply(this,[_23]);
}
};
if(_1e["forward"]){
_1e.forward=tfw;
}else{
if(_1e["forwardButton"]){
_1e.forwardButton=tfw;
}else{
if(_1e["handle"]){
_1e.handle=tfw;
}
}
}
}else{
if(!dojo.isIE){
if(!_8){
_8=setInterval(_1a,200);
}
}
}
}else{
url=_19();
}
_c.push(_14(url,_1e,_1f));
};
_1._iframeLoaded=function(evt,_24){
var _25=_17(_24.href);
if(_25==null){
if(_c.length==1){
_f();
}
return;
}
if(_d){
_d=false;
return;
}
if(_c.length>=2&&_25==_17(_c[_c.length-2].url)){
_f();
}else{
if(_b.length>0&&_25==_17(_b[_b.length-1].url)){
_12();
}
}
};
})();
=====*/
(function(){
var back = dojo.back;
// everyone deals with encoding the hash slightly differently
function getHash(){
var h = window.location.hash;
if(h.charAt(0) == "#"){ h = h.substring(1); }
return dojo.isMozilla ? h : decodeURIComponent(h);
}
function setHash(h){
if(!h){ h = ""; }
window.location.hash = encodeURIComponent(h);
historyCounter = history.length;
}
// if we're in the test for these methods, expose them on dojo.back. ok'd with alex.
if(dojo.exists("tests.back-hash")){
back.getHash = getHash;
back.setHash = setHash;
}
var initialHref = (typeof(window) !== "undefined") ? window.location.href : "";
var initialHash = (typeof(window) !== "undefined") ? getHash() : "";
var initialState = null;
var locationTimer = null;
var bookmarkAnchor = null;
var historyIframe = null;
var forwardStack = [];
var historyStack = [];
var moveForward = false;
var changingUrl = false;
var historyCounter;
function handleBackButton(){
//summary: private method. Do not call this directly.
//The "current" page is always at the top of the history stack.
var current = historyStack.pop();
if(!current){ return; }
var last = historyStack[historyStack.length-1];
if(!last && historyStack.length == 0){
last = initialState;
}
if(last){
if(last.kwArgs["back"]){
last.kwArgs["back"]();
}else if(last.kwArgs["backButton"]){
last.kwArgs["backButton"]();
}else if(last.kwArgs["handle"]){
last.kwArgs.handle("back");
}
}
forwardStack.push(current);
}
back.goBack = handleBackButton;
function handleForwardButton(){
//summary: private method. Do not call this directly.
var last = forwardStack.pop();
if(!last){ return; }
if(last.kwArgs["forward"]){
last.kwArgs.forward();
}else if(last.kwArgs["forwardButton"]){
last.kwArgs.forwardButton();
}else if(last.kwArgs["handle"]){
last.kwArgs.handle("forward");
}
historyStack.push(last);
}
back.goForward = handleForwardButton;
function createState(url, args, hash){
//summary: private method. Do not call this directly.
return {"url": url, "kwArgs": args, "urlHash": hash}; //Object
}
function getUrlQuery(url){
//summary: private method. Do not call this directly.
var segments = url.split("?");
if(segments.length < 2){
return null; //null
}
else{
return segments[1]; //String
}
}
function loadIframeHistory(){
//summary: private method. Do not call this directly.
var url = (dojo.config["dojoIframeHistoryUrl"] || dojo.moduleUrl("dojo", "resources/iframe_history.html")) + "?" + (new Date()).getTime();
moveForward = true;
if(historyIframe){
dojo.isWebKit ? historyIframe.location = url : window.frames[historyIframe.name].location = url;
}else{
//console.warn("dojo.back: Not initialised. You need to call dojo.back.init() from a <script> block that lives inside the <body> tag.");
}
return url; //String
}
function checkLocation(){
if(!changingUrl){
var hsl = historyStack.length;
var hash = getHash();
if((hash === initialHash||window.location.href == initialHref)&&(hsl == 1)){
// FIXME: could this ever be a forward button?
// we can't clear it because we still need to check for forwards. Ugg.
// clearInterval(this.locationTimer);
handleBackButton();
return;
}
// first check to see if we could have gone forward. We always halt on
// a no-hash item.
if(forwardStack.length > 0){
if(forwardStack[forwardStack.length-1].urlHash === hash){
handleForwardButton();
return;
}
}
// ok, that didn't work, try someplace back in the history stack
if((hsl >= 2)&&(historyStack[hsl-2])){
if(historyStack[hsl-2].urlHash === hash){
handleBackButton();
return;
}
}
if(dojo.isSafari && dojo.isSafari < 3){
var hisLen = history.length;
if(hisLen > historyCounter) handleForwardButton();
else if(hisLen < historyCounter) handleBackButton();
historyCounter = hisLen;
}
}
};
back.init = function(){
//summary: Initializes the undo stack. This must be called from a <script>
// block that lives inside the <body> tag to prevent bugs on IE.
// description:
// Only call this method before the page's DOM is finished loading. Otherwise
// it will not work. Be careful with xdomain loading or djConfig.debugAtAllCosts scenarios,
// in order for this method to work, dojo.back will need to be part of a build layer.
if(dojo.byId("dj_history")){ return; } // prevent reinit
var src = dojo.config["dojoIframeHistoryUrl"] || dojo.moduleUrl("dojo", "resources/iframe_history.html");
if (dojo._postLoad) {
console.error("dojo.back.init() must be called before the DOM has loaded. "
+ "If using xdomain loading or djConfig.debugAtAllCosts, include dojo.back "
+ "in a build layer.");
} else {
document.write('<iframe style="border:0;width:1px;height:1px;position:absolute;visibility:hidden;bottom:0;right:0;" name="dj_history" id="dj_history" src="' + src + '"></iframe>');
}
};
back.setInitialState = function(/*Object*/args){
//summary:
// Sets the state object and back callback for the very first page
// that is loaded.
//description:
// It is recommended that you call this method as part of an event
// listener that is registered via dojo.addOnLoad().
//args: Object
// See the addToHistory() function for the list of valid args properties.
initialState = createState(initialHref, args, initialHash);
};
//FIXME: Make these doc comments not be awful. At least they're not wrong.
//FIXME: Would like to support arbitrary back/forward jumps. Have to rework iframeLoaded among other things.
//FIXME: is there a slight race condition in moz using change URL with the timer check and when
// the hash gets set? I think I have seen a back/forward call in quick succession, but not consistent.
/*=====
dojo.__backArgs = function(kwArgs){
// back: Function?
// A function to be called when this state is reached via the user
// clicking the back button.
// forward: Function?
// Upon return to this state from the "back, forward" combination
// of navigation steps, this function will be called. Somewhat
// analgous to the semantic of an "onRedo" event handler.
// changeUrl: Boolean?|String?
// Boolean indicating whether or not to create a unique hash for
// this state. If a string is passed instead, it is used as the
// hash.
}
=====*/
back.addToHistory = function(/*dojo.__backArgs*/ args){
// summary:
// adds a state object (args) to the history list.
// description:
// To support getting back button notifications, the object
// argument should implement a function called either "back",
// "backButton", or "handle". The string "back" will be passed as
// the first and only argument to this callback.
//
// To support getting forward button notifications, the object
// argument should implement a function called either "forward",
// "forwardButton", or "handle". The string "forward" will be
// passed as the first and only argument to this callback.
//
// If you want the browser location string to change, define "changeUrl" on the object. If the
// value of "changeUrl" is true, then a unique number will be appended to the URL as a fragment
// identifier (http://some.domain.com/path#uniquenumber). If it is any other value that does
// not evaluate to false, that value will be used as the fragment identifier. For example,
// if changeUrl: 'page1', then the URL will look like: http://some.domain.com/path#page1
//
// There are problems with using dojo.back with semantically-named fragment identifiers
// ("hash values" on an URL). In most browsers it will be hard for dojo.back to know
// distinguish a back from a forward event in those cases. For back/forward support to
// work best, the fragment ID should always be a unique value (something using new Date().getTime()
// for example). If you want to detect hash changes using semantic fragment IDs, then
// consider using dojo.hash instead (in Dojo 1.4+).
//
// example:
// | dojo.back.addToHistory({
// | back: function(){ console.log('back pressed'); },
// | forward: function(){ console.log('forward pressed'); },
// | changeUrl: true
// | });
// BROWSER NOTES:
// Safari 1.2:
// back button "works" fine, however it's not possible to actually
// DETECT that you've moved backwards by inspecting window.location.
// Unless there is some other means of locating.
// FIXME: perhaps we can poll on history.length?
// Safari 2.0.3+ (and probably 1.3.2+):
// works fine, except when changeUrl is used. When changeUrl is used,
// Safari jumps all the way back to whatever page was shown before
// the page that uses dojo.undo.browser support.
// IE 5.5 SP2:
// back button behavior is macro. It does not move back to the
// previous hash value, but to the last full page load. This suggests
// that the iframe is the correct way to capture the back button in
// these cases.
// Don't test this page using local disk for MSIE. MSIE will not create
// a history list for iframe_history.html if served from a file: URL.
// The XML served back from the XHR tests will also not be properly
// created if served from local disk. Serve the test pages from a web
// server to test in that browser.
// IE 6.0:
// same behavior as IE 5.5 SP2
// Firefox 1.0+:
// the back button will return us to the previous hash on the same
// page, thereby not requiring an iframe hack, although we do then
// need to run a timer to detect inter-page movement.
//If addToHistory is called, then that means we prune the
//forward stack -- the user went back, then wanted to
//start a new forward path.
forwardStack = [];
var hash = null;
var url = null;
if(!historyIframe){
if(dojo.config["useXDomain"] && !dojo.config["dojoIframeHistoryUrl"]){
console.warn("dojo.back: When using cross-domain Dojo builds,"
+ " please save iframe_history.html to your domain and set djConfig.dojoIframeHistoryUrl"
+ " to the path on your domain to iframe_history.html");
}
historyIframe = window.frames["dj_history"];
}
if(!bookmarkAnchor){
bookmarkAnchor = dojo.create("a", {style: {display: "none"}}, dojo.body());
}
if(args["changeUrl"]){
hash = ""+ ((args["changeUrl"]!==true) ? args["changeUrl"] : (new Date()).getTime());
//If the current hash matches the new one, just replace the history object with
//this new one. It doesn't make sense to track different state objects for the same
//logical URL. This matches the browser behavior of only putting in one history
//item no matter how many times you click on the same #hash link, at least in Firefox
//and Safari, and there is no reliable way in those browsers to know if a #hash link
//has been clicked on multiple times. So making this the standard behavior in all browsers
//so that dojo.back's behavior is the same in all browsers.
if(historyStack.length == 0 && initialState.urlHash == hash){
initialState = createState(url, args, hash);
return;
}else if(historyStack.length > 0 && historyStack[historyStack.length - 1].urlHash == hash){
historyStack[historyStack.length - 1] = createState(url, args, hash);
return;
}
changingUrl = true;
setTimeout(function() {
setHash(hash);
changingUrl = false;
}, 1);
bookmarkAnchor.href = hash;
if(dojo.isIE){
url = loadIframeHistory();
var oldCB = args["back"]||args["backButton"]||args["handle"];
//The function takes handleName as a parameter, in case the
//callback we are overriding was "handle". In that case,
//we will need to pass the handle name to handle.
var tcb = function(handleName){
if(getHash() != ""){
setTimeout(function() { setHash(hash); }, 1);
}
//Use apply to set "this" to args, and to try to avoid memory leaks.
oldCB.apply(this, [handleName]);
};
//Set interceptor function in the right place.
if(args["back"]){
args.back = tcb;
}else if(args["backButton"]){
args.backButton = tcb;
}else if(args["handle"]){
args.handle = tcb;
}
var oldFW = args["forward"]||args["forwardButton"]||args["handle"];
//The function takes handleName as a parameter, in case the
//callback we are overriding was "handle". In that case,
//we will need to pass the handle name to handle.
var tfw = function(handleName){
if(getHash() != ""){
setHash(hash);
}
if(oldFW){ // we might not actually have one
//Use apply to set "this" to args, and to try to avoid memory leaks.
oldFW.apply(this, [handleName]);
}
};
//Set interceptor function in the right place.
if(args["forward"]){
args.forward = tfw;
}else if(args["forwardButton"]){
args.forwardButton = tfw;
}else if(args["handle"]){
args.handle = tfw;
}
}else if(!dojo.isIE){
// start the timer
if(!locationTimer){
locationTimer = setInterval(checkLocation, 200);
}
}
}else{
url = loadIframeHistory();
}
historyStack.push(createState(url, args, hash));
};
back._iframeLoaded = function(evt, ifrLoc){
//summary:
// private method. Do not call this directly.
var query = getUrlQuery(ifrLoc.href);
if(query == null){
// alert("iframeLoaded");
// we hit the end of the history, so we should go back
if(historyStack.length == 1){
handleBackButton();
}
return;
}
if(moveForward){
// we were expecting it, so it's not either a forward or backward movement
moveForward = false;
return;
}
//Check the back stack first, since it is more likely.
//Note that only one step back or forward is supported.
if(historyStack.length >= 2 && query == getUrlQuery(historyStack[historyStack.length-2].url)){
handleBackButton();
}else if(forwardStack.length > 0 && query == getUrlQuery(forwardStack[forwardStack.length-1].url)){
handleForwardButton();
}
};
})();
}