Skip to content

Commit 4ce7bc3

Browse files
committed
NuCivic/nucivic-internal#483 Add url state
1 parent 5e37e21 commit 4ce7bc3

File tree

3 files changed

+196
-0
lines changed

3 files changed

+196
-0
lines changed

examples/index.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
</div>
5252

5353
<script type="text/javascript" src="../vendor/recline.view.nvd3.js/dist/recline.view.nvd3.min.js"></script>
54+
<script type="text/javascript" src="../src/recline.URLState.js"></script>
5455
<script type="text/javascript" src="../src/recline.view.nvd3.baseControl.js"></script>
5556
<script type="text/javascript" src="../src/controls.js"></script>
5657
<script type="text/javascript" src="../src/recline.view.nvd3.embedControl.js"></script>

examples/view.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
<body>
4545
<div id="chart"></div>
4646
<script type="text/javascript" src="../vendor/recline.view.nvd3.js/dist/recline.view.nvd3.min.js"></script>
47+
<script type="text/javascript" src="../src/recline.URLState.js"></script>
4748
</body>
4849

4950
</html>

src/recline.URLState.js

Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
/*jshint multistr:true*/
2+
3+
this.recline = this.recline || {};
4+
5+
;(function($, my) {
6+
'use strict';
7+
8+
/**
9+
* Router object
10+
* @param {recline.ObjectState} state
11+
* @param {Object} first
12+
*/
13+
my.URLState = function(options){
14+
var self = this;
15+
var parser;
16+
var dependencies = {};
17+
var router;
18+
self.currentState = {};
19+
20+
options = options || {};
21+
if(options.parser) {
22+
parser = new options.parser();
23+
} else {
24+
parser = new my.Parser();
25+
}
26+
27+
28+
function inv(method){
29+
var args = _.rest(_.toArray(arguments));
30+
return function(ctrl){
31+
return _.isFunction(ctrl[method]) && ctrl[method].apply(ctrl, args);
32+
};
33+
}
34+
35+
/**
36+
* Init first state
37+
* @param {[String]} serializedState Url serialized state
38+
*/
39+
self._init = function(serializedState){
40+
var state = self.transform(serializedState, self.toState);
41+
_.each(dependencies, inv('update', state));
42+
self.currentState = state;
43+
if(options.init){
44+
options.init(state);
45+
}
46+
};
47+
48+
49+
/**
50+
* Adds a dependency to this router. Something to track and
51+
* to execute when tracked thing changes
52+
* @param {Function} ctrl Constructor with the implementation
53+
* of this observer
54+
* @return {undefined}
55+
*/
56+
self.addDependency = function(ctrl){
57+
dependencies[ctrl.name] = ctrl;
58+
};
59+
60+
61+
self.getCurrentState = function(){
62+
return self.currentState;
63+
};
64+
65+
self.getSerializedState = function(state){
66+
return self.transform(state, self.toParams);
67+
};
68+
/**
69+
* Applies a transform function to the input and return de result.
70+
* @param {String} input
71+
* @param {Function} transformFunction
72+
* @return {String}
73+
*/
74+
self.transform = function(input, transformFunction){
75+
var result;
76+
// try{
77+
result = transformFunction(input);
78+
// } catch(e){
79+
// result = null;
80+
// }
81+
return result;
82+
};
83+
84+
/**
85+
* Converts a serialized state string to an object.
86+
* @param {String} serializedState
87+
* @return {Object}
88+
*/
89+
self.toState = function(serializedState){
90+
var stringObject = parser.inflate(decodeURI(serializedState));
91+
return JSON.parse(stringObject);
92+
};
93+
94+
/**
95+
* Converts an object state to a string.
96+
* @param {Object} state
97+
* @return {String}
98+
*/
99+
self.toParams = function(state){
100+
var stringObject = JSON.stringify(state);
101+
return parser.compress(stringObject);
102+
};
103+
104+
/**
105+
* Listen for changes in the state. It computes the differences
106+
* between the initial state and the current state. Creates a patch object
107+
* from this difference. Converts this new object to params and finally
108+
* navigates to that state.
109+
* @param {Event} event
110+
*/
111+
self.navigateToState = function(state){
112+
self.currentState = state;
113+
router.navigate(self.transform(state, self.toParams));
114+
options.stateChange && options.stateChange(state); // jshint ignore:line
115+
};
116+
117+
var Router = Backbone.Router.extend({
118+
routes: {
119+
'*state': 'defaultRoute',
120+
},
121+
defaultRoute: self._init
122+
});
123+
router = new Router();
124+
Backbone.history.start();
125+
};
126+
127+
/**
128+
* Url parser
129+
*/
130+
my.Parser = function(){
131+
var self = this;
132+
133+
/**
134+
* Reduces the size of the url removing unnecesary characters.
135+
* @param {String} str
136+
* @return {String}
137+
*/
138+
self.compress = function(str){
139+
140+
// Replace words
141+
// Remove start and end brackets
142+
// Replace true by 1 and false by 0
143+
return self.escapeStrings(str);
144+
};
145+
146+
/**
147+
* Inflates a compressed url string.
148+
* @param {String} str
149+
* @return {String}
150+
*/
151+
self.inflate = function(str){
152+
return self.parseStrings(str);
153+
};
154+
155+
/**
156+
* Escape all the string prepending a ! character to each one.
157+
* @param {String} str
158+
* @return {String}
159+
*/
160+
self.escapeStrings = function(str){
161+
162+
// % presence could lead to malformed url.
163+
str = str.replace('%', '@@');
164+
165+
// Stripping quotes from keys
166+
str = str.replace(/"([a-zA-Z-_.]+)"\s?:/g , '$1:');
167+
168+
// Replacing spaces between quotes with underscores
169+
str = str.replace(/\x20(?![^"]*("[^"]*"[^"]*)*$)/g, '++');
170+
return str.replace(/"([a-zA-Z0-9-#_.-|+]+)?"/g , '!$1');
171+
};
172+
173+
/**
174+
* Converts all escaped strings to javascript strings.
175+
* @param {String} str
176+
* @return {String}
177+
*/
178+
self.parseStrings = function(str){
179+
180+
// Converting all the @@ to %.
181+
str = str.replace('@@', '%');
182+
183+
// Adding quotes to keys
184+
str = str.replace(/([{,])([a-zA-Z-_.\+]+)\s?:/g , '$1\"$2\":');
185+
// Replacing underscores with spaces for any word that start with !
186+
// TODO: make space replacement configurable
187+
str = str.replace(/![a-zA-Z0-9_. -\+]+/g, function(x) {
188+
return x.replace(/\+\+/g, ' ');
189+
});
190+
return str.replace(new RegExp('!([a-zA-Z0-9-_# .-:%]+)?', 'g'), '\"$1\"');
191+
};
192+
};
193+
194+
})(jQuery, this.recline);

0 commit comments

Comments
 (0)