helma/modules/jala/code/History.js

253 lines
6.7 KiB
JavaScript

//
// Jala Project [http://opensvn.csie.org/traccgi/jala]
//
// Copyright 2004 ORF Online und Teletext GmbH
//
// Licensed under the Apache License, Version 2.0 (the ``License'');
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an ``AS IS'' BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// $Revision$
// $LastChangedBy$
// $LastChangedDate$
// $HeadURL$
//
/**
* @fileoverview Fields and methods of the jala.History class.
*/
// Define the global namespace for Jala modules
if (!global.jala) {
global.jala = {};
}
/**
* Constructs a new History object.
* @class This class is an implementation of a Browser-like history
* stack suitable to use in any Helma application. The difference
* to a Browser's history is that this implementation ignores
* POST requests and checks if Urls in the stack are still valid to
* prevent eg. redirections to a HopObject's url that has been deleted.
* Plus it is capable to create new "intermediate" history-stacks
* and this way maintain a "history of histories" which is needed for
* eg. editing sessions in a popup window that should use their own
* request history without interfering with the history of the
* main window.
* @constructor
*/
jala.History = function() {
var MAX = 40;
/**
* Stack constructor
* @private
*/
var Stack = function(id) {
this.items = [];
this.id = id;
return this;
};
/**
* Returns the current url including query string
* @private
*/
var getUrl = function() {
var query;
var url = path.href(req.action);
try {
if (query = req.getServletRequest().getQueryString())
url += "?" + query;
} catch (e) {
// ignore
}
return url;
}
/**
* Checks if a request is valid for being added
* to the history stack. This method returns false
* for all POST-requests or requests whose action name
* contains a dot (to prevent requests for external stylesheets
* or the like from being recorded).
* @private
* @type Boolean
*/
var isValid = function() {
// FIXME: we should check for mimetype of response instead
// of assuming that requests containing a dot aren't worth
// being put into history stack ...
if (req.isPost() || (req.action && req.action.contains(".")))
return false;
return true;
};
/**
* returns a single Stack instance
* @private
*/
var getStack = function() {
if (history.length < 1)
history.push(new Stack(getUrl()));
return history[history.length -1];
};
/**
* Variable containing the history-stacks
* @private
*/
var history = [];
/**
* Initializes a new history stack, adds
* it to the array of stacks (which makes it
* the default one to use for further requests)
* and records the current request Url.
*/
this.add = function() {
if (!isValid())
return;
var url = getUrl();
if (getStack().id != url) {
history.push(new Stack(url));
this.push();
}
return;
};
/**
* Removes the current history stack
*/
this.remove = function() {
history.pop();
return;
};
/**
* Records a request Url in the currently active
* history stack.
*/
this.push = function() {
if (isValid()) {
var obj = path[path.length-1];
var url = getUrl();
var stack = getStack();
if (stack.items.length < 1 || stack.items[stack.items.length -1].url != url) {
if (stack.items.length >= MAX)
stack.items.shift();
stack.items.push({
url: url,
path: path.href().substring(root.href().length).replace(/\+/g, " ")
});
}
}
return;
};
/**
* Clears the currently active history stack
*/
this.clear = function() {
getStack().items.length = 0;
return;
};
/**
* Redirects the client back to the first valid
* request in history. Please mind that searching for
* a valid Url starts at <em>history.length - 2</em>.
* @param {Number} offset The index position in the stack to start
* searching at
*/
this.redirect = function(offset) {
res.redirect(this.pop(offset));
return;
};
/**
* Retrieves the first valid request Url in history
* stack starting with a given offset. The default offset is 1.
* Any valid Url found is removed from the stack, therefor
* this method <em>alters the contents of the history stack</em>.
* @param {Number} offset The index position in history stack to start
* searching at
* @return The Url of the request
* @type String
*/
this.pop = function(offset) {
/**
* checks if a referrer is stil valid
* @private
*/
var isValidPath = function(p) {
var arr = p.split("/");
var obj = root;
for (var i=0;i<arr.length -1;i++) {
if (!(obj = obj.get(unescape(arr[i]))) || obj.__node__.getState() == 3)
return false;
}
return true;
};
var obj;
var cut = offset != null ? offset : 1;
var stack = getStack();
if (stack.items.length > 0) {
while (cut-- > 0) {
obj = stack.items.pop();
}
}
while (stack.items.length > 0) {
obj = stack.items.pop();
// check if url is valid
if (isValidPath(obj.path)) {
return obj.url;
}
}
return path.href();
};
/**
* Retrieves the request Url at the given position
* in the current history stack. If no offset is given
* the last Url in the stack is returned. This method
* <em>does not alter the stack contents</em>!
* @param {Number} offset The index position in history stack to start
* searching at
* @return The Url of the request
* @type String
*/
this.peek = function(offset) {
var stack = getStack();
return stack.items[stack.items.length - (offset != null ? offset : 1)];
};
/**
* Returns the contents of all history stacks
* as string
* @return The history stacks as string
* @type String
*/
this.dump = function() {
return history.toSource();
};
/** @ignore */
this.toString = function() {
return "[History " + getStack().toSource() + "]";
};
return this;
}