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