Skip to content

Commit 08710b4

Browse files
authored
-updated walkify.js
*fixes -fixed routing link issues in updated view -increased code readability -added methods to evade external library conflict
1 parent c04e2b0 commit 08710b4

1 file changed

Lines changed: 137 additions & 95 deletions

File tree

assets/js/libs/walkify.js

Lines changed: 137 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,13 @@
66
class Walkify {
77
constructor(routesObj, viewElem){
88
this.routes = routesObj;
9+
this.supportedPrefix = [
10+
'!', '$', '%', '^', '*', '_', '+', '~', '-', '`', ':', '@', '#'
11+
];
12+
this.variableBracketsStart = '{{';
13+
this.variableBracketsEnd = '}}';
14+
this.variablePrefix = "\\$";
915
this.viewElem = viewElem ? this.mount(viewElem) : null;
10-
this.init();
1116
}
1217
init(){
1318
window.onload = () => {
@@ -16,21 +21,24 @@ class Walkify {
1621
window.onhashchange = () => {
1722
this.navigate();
1823
}
24+
this.setLinksHandler();
25+
}
26+
setLinksHandler(){
1927
let links = document.getElementsByTagName('a');
2028
[... links].forEach((link) => {
2129
let linkHref = link.getAttribute('href');
22-
linkHref = linkHref.startsWith('#') ? linkHref : '#' + linkHref;
2330
let isRouteLink = !(/www|http|https|ftp/ig.test(linkHref));
2431
if(link.hasAttribute('href') && isRouteLink){
2532
link.addEventListener('click', (event) => {
33+
linkHref = linkHref.startsWith('#') ? linkHref : '#' + linkHref;
2634
event.preventDefault();
2735
if(location.hash != linkHref ){
2836
'exist' in this.currentRoute && this.currentRoute.exist();
29-
this.routeTo(linkHref)
37+
this.routeTo(linkHref);
3038
}
3139
})
3240
}
33-
})
41+
});
3442
}
3543
navigate(){
3644
if(this.getHash()){
@@ -65,6 +73,8 @@ class Walkify {
6573
let attribute = viewElem[0] == '.' ? 'classname' : 'id';
6674
if(! rootElem ) throw new Error('could not found element with ' + attribute + ' ' + viewElem.slice(1));
6775
this.viewElem = rootElem;
76+
//start listening to changes in url
77+
this.init();
6878
return this;
6979
}
7080
route(){
@@ -76,17 +86,17 @@ class Walkify {
7686
this.routes[key].matched();
7787
this.currentRoute = this.routes[key];
7888
} else {
79-
let Lkey = this.findObj(key);
80-
if(key && Lkey){
89+
let routeStr = this.findRoute(key);
90+
if(key && routeStr){
8191
let keyArr = key.split('/');
82-
let keyArrObj = (Lkey.split('/'));
92+
let keyArrObj = (routeStr.split('/'));
8393
let isTheSame = this.compare(keyArr, keyArrObj);
8494
if(isTheSame){
85-
if('matched' in this.routes[Lkey]){
95+
if('matched' in this.routes[routeStr]){
8696
let newObj = this.buildObj(keyArr, keyArrObj);
87-
this.routes[Lkey].view = this.view.bind(this, this.routes[Lkey]);
88-
this.routes[Lkey].matched.apply(this.routes[Lkey], Object.values(newObj));
89-
this.currentRoute = this.routes[Lkey];
97+
this.routes[routeStr].view = this.view.bind(this, this.routes[routeStr]);
98+
this.routes[routeStr].matched.apply(this.routes[routeStr], Object.values(newObj));
99+
this.currentRoute = this.routes[routeStr];
90100
return this;
91101
}
92102
throw new Error('walkify expected hook "matched" not found!')
@@ -104,101 +114,88 @@ class Walkify {
104114
}
105115
return this;
106116
}
107-
compare(arr1, arr2){
108-
let len = arr1.length == arr2.length;
109-
let p = arr2.slice(1, -1).every((el) => {
110-
return el.includes('{') && el.includes('}') && !(el.includes(':'))
111-
});
112-
if(arr1.length && arr2.length){
113-
if(arr2.includes('*')){
114-
let str = arr2.join('/').replace('*', '');
115-
let str2 = arr1.join('/');
116-
if(str2.indexOf(str) != -1){
117-
return true
118-
}
119-
} else {
117+
compare(hashArr, routesArr){
118+
if(hashArr.length == routesArr.length){
119+
if(routesArr.indexOf('*') == -1){
120120
let bool = false;
121-
let b = arr2.map((el, i) => {
121+
let b = routesArr.map((el, i) => {
122122
if(el.includes('{') && el.includes('}')){
123-
el = el.slice(1, -1);
123+
el = el.slice(1, -1);
124124
}
125125
if(el.includes(':')){
126126
bool = true;
127-
let regItem = el.split(':')[1];
128-
let reg = new RegExp('^' + regItem + '$');
129-
return (reg.test(arr1[i]));
127+
let regexString = el.split(':')[1];
128+
let regex = new RegExp('^' + regexString + '$');
129+
return (regex.test(hashArr[i]));
130130
} else {
131-
return (el == arr1[i]);
131+
return (el == hashArr[i]);
132132
}
133133
});
134+
134135
b = b.every((el) => {
135136
return el == true;
136137
});
137-
if(!bool) return len && arr1.join('') == arr2.join('');
138-
if(p) return p && len;
139-
return b && len;
140-
}
141-
}
142-
if(p && arr1.length == arr2.length){
143-
return true;
144-
};
145-
return false;
138+
return b;
139+
140+
} else {
141+
// return (hashArr.join('').startsWith(routesArr.join('').slice(0, -1)));
142+
}
143+
} else {
144+
return (hashArr.join('').startsWith(routesArr.join('').slice(0, -1)));
145+
}
146146
}
147-
findObj(urlH){
148-
let routesArr = Object.keys(this.routes);
149-
let hashArr = urlH.split('/');
150-
let arr;
151-
let key;
152-
for(let i = 0; i < routesArr.length; i++){
153-
let listenArr = routesArr[i].split('/');
154-
let h = [];
155-
arr = [...hashArr];
156-
if(routesArr[i] == '*' || routesArr[i] == '!'){
157-
continue;
147+
closestRoute(routesArr, hashArr){
148+
let matchCountObj = {};
149+
routesArr.forEach((route) => {
150+
matchCountObj[route] = {
151+
path : route,
152+
count : 0
158153
}
159-
let a = listenArr;
160-
let b = a.filter((el) => {
161-
return !(el.includes('{'))
162-
});
163-
for(let i = 0; i < arr.length; i++){
164-
for(let v = 0; v < b.length; v++){
165-
if(arr[i] == b[v]){
166-
h.push(arr[i]);
167-
}
168-
}
154+
let currentRouteArr = route.split('/');
155+
for(let i = 0; i < currentRouteArr.length; i++){
156+
if((!!currentRouteArr[i]) && hashArr[i] == currentRouteArr[i]){
157+
matchCountObj[route]['count'] += 1;
158+
}
169159
}
170-
if(JSON.stringify(b) == JSON.stringify(h)){
171-
let bool = this.compare(hashArr, listenArr);
172-
if(bool){
173-
key = routesArr[i];
174-
}
160+
});
161+
let matchCountArr = Object.keys(matchCountObj);
162+
if(matchCountArr.length){
163+
let Highestcount = Math.max.apply(null, matchCountArr.map((route) => {
164+
return (matchCountObj[route]['count']);
165+
}));
166+
let closeRoute = matchCountArr.filter((route) => {
167+
return (matchCountObj[route]['count'] == Highestcount);
168+
})[0];
169+
let closeRouteArr = closeRoute.split('/');
170+
if(closeRoute[0].indexOf('*') == -1){
171+
// let passed = closeRouteArr.length;
172+
if(closeRouteArr.length == Highestcount) return closeRoute;
175173
}
176-
else if(this.compare(hashArr, listenArr)){
177-
let bool = this.compare(hashArr, listenArr);
178-
if(bool){
179-
key = routesArr[i];
180-
}
181-
return key;
182-
}
183-
else{
184-
if(hashArr.length == listenArr.length){
185-
let p = listenArr.slice(1, -1).every((el) => {
186-
if(el.includes('{') && el.includes('}') && !(el.includes(':'))){
187-
key = routesArr[i];
188-
}
189-
});
190-
return key;
191-
}
192-
if(b.includes('*')){
193-
let k = (routesArr[i].slice(0, -1));
194-
let bool = this.compare(hashArr, k.split('/'));
195-
if(bool){
196-
key = routesArr[i];
197-
}
198-
}
174+
if(closeRoute.indexOf('*') != -1){
175+
let astIndex = closeRouteArr.lastIndexOf('*');
176+
let slicedHash = hashArr.slice(0, astIndex);
177+
if(closeRouteArr.slice(0, -1).join('/') == slicedHash.join('/')) return closeRoute
199178
}
179+
if(/{(.+?):(.+?)}/.test(closeRoute)){
180+
return closeRoute;
181+
}
182+
}
183+
}
184+
findRoute(urlHash){
185+
let routes = this.routes;
186+
let hashArr = urlHash.split('/');
187+
let routesArr = Object.keys(routes);
188+
let routesWithSameLength = routesArr.filter((route) => {
189+
return route.split('/').length == hashArr.length;
190+
});
191+
let routesWithAsteriks = routesArr.filter((route) => {
192+
return route.endsWith('*');
193+
});
194+
if( routesWithSameLength.length ){
195+
return this.closestRoute(routesWithSameLength, hashArr);
196+
} else {
197+
return this.closestRoute(routesWithAsteriks, hashArr);
200198
}
201-
return key ? key : false;
202199
}
203200
buildObj(arr1, arr2){
204201
let obj = {};
@@ -221,36 +218,81 @@ class Walkify {
221218
});
222219
return (obj);
223220
}
221+
hasTemplate(templates, viewObj){
222+
return templates.some((template) => {
223+
return template.getAttribute('for') == viewObj.name;
224+
});
225+
}
224226
view(viewObj, data = {}, temp){
225227
if(!temp || data.redirect){
226228
if(!('name' in viewObj)) throw new Error('property "name" missing in route object');
227-
let templates = document.getElementsByTagName('template');
228-
[... templates].forEach((template) => {
229+
let templates = [... document.getElementsByTagName('template')];
230+
if( ! this.hasTemplate(templates, viewObj) ) throw new Error('template not found for current page');
231+
templates.forEach((template) => {
229232
if(template.getAttribute('for') == viewObj.name){
230233
this.mountView(data.redirect ? data.data : data, template);
231234
'mounted' in viewObj && viewObj.mounted();
235+
this.setLinksHandler();
232236
}
233237
}, this);
234238
} else {
235239
this.mountView(data, temp);
236240
'mounted' in viewObj && viewObj.mounted();
241+
this.setLinksHandler();
237242
}
238243
}
239244
mountView(data, template){
245+
let hasNoPrefix = this.noVariablePrefix;
240246
template = (typeof template != 'string') ? template.innerHTML : template;
241-
template = template.replace(/\${{(.+?)}}/ig, matched => {
242-
let key = matched.slice(3, -2).trim();
247+
248+
let regex = '[' + (hasNoPrefix ? this.supportedPrefix.join('|') : this.variablePrefix) +']' + (hasNoPrefix ? '*' : '+') + '?' + ((this.variableBracketsStart) + '+') + '(.+?)' + ((this.variableBracketsEnd) + '+');
249+
250+
template = template.replace(new RegExp(regex, 'ig'), matched => {
251+
let key = (matched).slice(this.variableBracketsStart.length + (hasNoPrefix ? 0 : this.variablePrefix.length - 1), -this.variableBracketsEnd.length).trim();
252+
let hasCharLeft = (key.indexOf(this.variableBracketsEnd[0]) != - 1) || (key.indexOf(this.variableBracketsStart[0]) != - 1);
253+
243254
if(key in data){
244255
return data[key];
245256
} else {
246257
try {
247258
return (eval(key));
248259
} catch (e) {
249-
console.error('property : "' + key + '" not defined');
250-
return undefined
260+
if(!hasCharLeft) console.error('property : "' + key + '" not defined');
261+
return hasCharLeft ? matched : undefined
251262
}
252263
}
253264
});
254265
this.viewElem.innerHTML = template;
255266
}
267+
268+
269+
//methods to resolve external library conflict
270+
//changes the default '$' to the argument passed
271+
272+
setPrefix(variablePrefix){
273+
variablePrefix = !variablePrefix ? variablePrefix : variablePrefix.trim();
274+
let isSupported = this.supportedPrefix.indexOf(variablePrefix) != -1;
275+
276+
if(!isSupported && variablePrefix !== false ) throw new Error('prefix not supported');
277+
if((variablePrefix === false) || !variablePrefix){
278+
this.noVariablePrefix = true;
279+
}
280+
this.variablePrefix = '\\' + variablePrefix;
281+
}
282+
//changes the default curly braces '{{' and '}}'
283+
setVariableBrackets(brackets){
284+
if(this.typeof(brackets) != 'Array'){
285+
throw new Error('parameter for setVariableBrackets method must be of the type Array')
286+
} else if(brackets.length > 2){
287+
throw new Error('array should not be greater than two');
288+
} else if(this.typeof(brackets[0]) != 'String' || this.typeof(brackets[0]) != 'String'){
289+
throw new Error('character types in the array must be string')
290+
} else {
291+
this.variableBracketsStart = brackets[0];
292+
this.variableBracketsEnd = brackets[1];
293+
}
294+
}
295+
typeof(variable){
296+
return (variable).constructor.name;
297+
}
256298
}

0 commit comments

Comments
 (0)