forked from couchapp/couchapp
/
jquery.pathbinder.js
143 lines (128 loc) · 3.72 KB
/
jquery.pathbinder.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
(function($) {
// functions for handling the path
// thanks sammy.js
var PATH_REPLACER = "([^\/]+)",
PATH_NAME_MATCHER = /:([\w\d]+)/g,
QUERY_STRING_MATCHER = /\?([^#]*)$/,
_currentPath,
_lastPath,
_pathInterval;
function hashChanged() {
_currentPath = getPath();
// if path is actually changed from what we thought it was, then react
if (_lastPath != _currentPath) {
return triggerOnPath(_currentPath);
}
}
$.pathbinder = {
changeFuns : [],
paths : [],
begin : function(defaultPath) {
// this should trigger the defaultPath if there's not a path in the URL
// otherwise it should trigger the URL's path
$(function() {
var loadPath = getPath();
if (loadPath) {
triggerOnPath(loadPath);
} else {
goPath(defaultPath);
triggerOnPath(defaultPath);
}
})
},
go : function(path) {
goPath(path);
triggerOnPath(path);
},
onChange : function (fun) {
$.pathbinder.changeFuns.push(fun);
}
};
function pollPath(every) {
function hashCheck() {
_currentPath = getPath();
// path changed if _currentPath != _lastPath
if (_lastPath != _currentPath) {
setTimeout(function() {
$(window).trigger('hashchange');
}, 1);
}
};
hashCheck();
_pathInterval = setInterval(hashCheck, every);
$(window).bind('unload', function() {
clearInterval(_pathInterval);
});
}
function triggerOnPath(path) {
$.pathbinder.changeFuns.forEach(function(fun) {fun(path)});
var pathSpec, path_params, params = {};
for (var i=0; i < $.pathbinder.paths.length; i++) {
pathSpec = $.pathbinder.paths[i];
if ((path_params = pathSpec.matcher.exec(path)) !== null) {
path_params.shift();
for (var j=0; j < path_params.length; j++) {
params[pathSpec.param_names[j]] = decodeURIComponent(path_params[j]);
};
// $.log("path trigger for "+path);
pathSpec.callback(params);
return true;
}
};
};
// bind the event
$(function() {
if ('onhashchange' in window) {
// we have a native event
} else {
pollPath(10);
}
// setTimeout(hashChanged,50);
$(window).bind('hashchange', hashChanged);
});
function registerPath(pathSpec) {
$.pathbinder.paths.push(pathSpec);
};
function setPath(pathSpec, params) {
var newPath = $.mustache(pathSpec.template, params);
goPath(newPath);
};
function goPath(newPath) {
window.location = '#'+newPath;
_lastPath = getPath();
};
function getPath() {
var matches = window.location.toString().match(/^[^#]*(#.+)$/);
return matches ? matches[1] : '';
};
function makePathSpec(path, callback) {
var param_names = [];
var template = "";
PATH_NAME_MATCHER.lastIndex = 0;
while ((path_match = PATH_NAME_MATCHER.exec(path)) !== null) {
param_names.push(path_match[1]);
}
return {
param_names : param_names,
matcher : new RegExp(path.replace(PATH_NAME_MATCHER, PATH_REPLACER) + "$"),
template : path.replace(PATH_NAME_MATCHER, function(a, b) {
return '{{'+b+'}}';
}),
callback : callback
};
};
$.fn.pathbinder = function(name, path) {
var self = $(this);
var pathSpec = makePathSpec(path, function(params) {
// $.log("path cb", name, path, self)
self.trigger(name, [params]);
});
self.bind(name, function(ev, params) {
// set the path when triggered
// $.log("set path", name, pathSpec)
setPath(pathSpec, params);
});
// trigger when the path matches
registerPath(pathSpec);
};
})(jQuery);