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 - z A - Z - _ .] + ) " \s ? : / g , '$1:' ) ;
167
+
168
+ // Replacing spaces between quotes with underscores
169
+ str = str . replace ( / \x20 (? ! [ ^ " ] * ( " [ ^ " ] * " [ ^ " ] * ) * $ ) / g, '++' ) ;
170
+ return str . replace ( / " ( [ a - z A - Z 0 - 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 - z A - 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 - z A - Z 0 - 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