Skip to content

Instantly share code, notes, and snippets.

@subtleGradient
Created December 16, 2009 15:18
Show Gist options
  • Save subtleGradient/257903 to your computer and use it in GitHub Desktop.
Save subtleGradient/257903 to your computer and use it in GitHub Desktop.
Harald Kirschner's (http://digitarald.de/) MooTools History (replaces HistoryManager)
#!/usr/bin/env sh
curl -s http://digitarald.de/playground/history/History.js |ruby -e 'print STDIN.read.gsub(/\r/,"")' > History.js
curl -s http://digitarald.de/playground/history/History.Routing.js |ruby -e 'print STDIN.read.gsub(/\r/,"")' > History.Routing.js
/**
* History
*
* @version 1.0
*
* @license MIT License
* @author Harald Kirschner <mail [at] digitarald.de>
* @copyright 2008 Author
*/
var History = $extend(history, {
implement: function(obj) {
return $extend(this, obj);
}
});
History.implement(new Events($empty));
History.implement({
state: null,
start: function() {
if (this.started) return this;
this.state = this.getHash();
if (Browser.Engine.trident) {
var iframe = new Element('iframe', {
'src': "javascript:'<html></html>'",
'styles': {
'position': 'absolute',
'top': '-1000px'
}
}).inject(document.body).contentWindow;
var writeState = function(state) {
iframe.document.write('<html><body onload="top.History.$listener(\'', encodeURIComponent(state) ,'\');">Moo!</body></html>');
iframe.document.close();
};
$extend(this, {
'$listener': function(state) {
state = decodeURIComponent(state);
if (this.state != state) this.setHash(state).changeState(state);
}.bind(this),
'setState': function(state, force) {
if (this.state != state || force) {
if (!force) this.setHash(state).changeState(state, true);
writeState(state);
}
return this;
},
'trace': function() {
var state = this.getHash();
if (state != this.state) writeState(state);
}
});
var check = (function() {
if (iframe.document && iframe.document.body) {
check = $clear(check);
if (!iframe.document.body.innerHTML) this.setState(this.state);
}
}).periodical(50, this);
} else {
if (Browser.Engine.presto915) {
new Element('img', {
'src': "javascript:location.href='javascript:History.trace();';",
'styles': {
'position': 'absolute',
'top': '-1000px'
}
}).inject(document.body);
}
}
this.trace.periodical(150, this);
this.started = true;
return this;
},
changeState: function(state, manual) {
var stateOld = this.state;
this.state = state;
this.fireEvent('changed', [state, stateOld, manual]);
},
trace: function() {
var state = this.getHash();
if (state != this.state) this.changeState(state);
},
getHash: function() {
var href = location.href, pos = href.indexOf('#') + 1;
return (pos) ? href.substr(pos) : '';
},
setHash: function(state) {
location.hash = '#' + state;
return this;
},
setState: function(state) {
if (this.state !== state) this.setHash(state).changeState(state, true);
return this;
},
getState: function() {
return this.state;
}
});
/**
* History.Routing
*
* @version 2.0
*
* @license MIT License
* @author Harald Kirschner <mail [at] digitarald.de>
* @copyright 2008 Author
*/
History.implement(new Options());
History.implement({
options: {
separator: ';'
},
routes: [],
register: function(route) {
if (this.routes.push(route) == 1) this.addEvent('changed', this.match);
},
unregister: function(route) {
this.routes.remove(route);
},
match: function(state, previous, manual) {
if (!manual) this.routes.each(Function.methodize('match', this.state));
},
generate: function() {
return this.routes.map(Function.methodize('generate')).clean().join(this.options.separator);
},
update: function() {
return this.setState(this.generate());
}
});
History.Route = new Class({
Implements: [Events, Options],
/**
* pattern: Regular expression that matches the string updated from onGenerate
* defaults: Default values array, initially empty.
* flags: When regexp is a String, this is the second argument for new RegExp.
* skipDefaults: default true; generate is not called when current values are similar to the default values.
* generate: Should return the string for the state string, values are first argument
* onMatch: Will be called when the regexp matches, with the new values as argument.
*/
options: {
skipDefaults: true,
defaults: [],
pattern: null,
flags: '',
generate: function(values) {
return values[0];
},
onMatch: $empty
},
initialize: function(options){
this.setOptions(options);
this.pattern = this.options.pattern || '(.*)';
if ($type(this.pattern) == 'string') this.pattern = new RegExp(this.pattern, this.options.flags);
this.values = this.defaults = this.options.defaults.slice();
History.register(this);
return this;
},
setValues: function(values) {
if (this.values.toString() == values.toString()) return this;
this.values = values;
History.update();
return this;
},
setValue: function(index, value) {
if (this.values[index] == value) return this;
this.values[index] = value;
History.update();
return this;
},
build: function(values) {
var tmp = this.values.slice();
this.values = values;
var state = History.generate();
this.values = tmp;
return state;
},
destroy: function() {
History.unregister(this);
},
generate: function() {
if (this.options.skipDefaultMatch && (String(this.values) == String(this.defaults))) return null;
return this.options.generate.call(this, this.values);
},
match: function(state) {
var bits = state.match(this.pattern);
var defaults = this.defaults;
if (bits) {
bits.splice(0, 1);
for (var i = 0, j = bits.length; i < j; i++) bits[i] = $pick(bits[i], defaults[i] || null);
if (String(bits) != String(defaults)) this.values = bits;
} else {
this.values = this.defaults.slice();
}
this.fireEvent('onMatch', [this.values, this.defaults]);
}
});
Function.methodize = function(name) {
var args = Array.slice(arguments, 1);
return function(obj) {
return obj[name].apply(obj, args);
};
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment