a zip code crypto-currency system good for red ONLY

url-serializer.js 19KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498
  1. import { InjectionToken } from '@angular/core';
  2. import { isArray, isBlank, isPresent } from '../util/util';
  3. /**
  4. * @hidden
  5. */
  6. var UrlSerializer = (function () {
  7. function UrlSerializer(_app, config) {
  8. this._app = _app;
  9. if (config && isArray(config.links)) {
  10. this.links = normalizeLinks(config.links);
  11. }
  12. else {
  13. this.links = [];
  14. }
  15. }
  16. /**
  17. * Parse the URL into a Path, which is made up of multiple NavSegments.
  18. * Match which components belong to each segment.
  19. */
  20. UrlSerializer.prototype.parse = function (browserUrl) {
  21. if (browserUrl.charAt(0) === '/') {
  22. browserUrl = browserUrl.substr(1);
  23. }
  24. // trim off data after ? and #
  25. browserUrl = browserUrl.split('?')[0].split('#')[0];
  26. return convertUrlToSegments(this._app, browserUrl, this.links);
  27. };
  28. UrlSerializer.prototype.createSegmentFromName = function (navContainer, nameOrComponent) {
  29. var configLink = this.getLinkFromName(nameOrComponent);
  30. if (configLink) {
  31. return this._createSegment(this._app, navContainer, configLink, null);
  32. }
  33. return null;
  34. };
  35. UrlSerializer.prototype.getLinkFromName = function (nameOrComponent) {
  36. return this.links.find(function (link) {
  37. return (link.component === nameOrComponent) ||
  38. (link.name === nameOrComponent);
  39. });
  40. };
  41. /**
  42. * Serialize a path, which is made up of multiple NavSegments,
  43. * into a URL string. Turn each segment into a string and concat them to a URL.
  44. */
  45. UrlSerializer.prototype.serialize = function (segments) {
  46. if (!segments || !segments.length) {
  47. return '/';
  48. }
  49. var sections = segments.map(function (segment) {
  50. if (segment.type === 'tabs') {
  51. if (segment.requiresExplicitNavPrefix) {
  52. return "/" + segment.type + "/" + segment.navId + "/" + segment.secondaryId + "/" + segment.id;
  53. }
  54. return "/" + segment.secondaryId + "/" + segment.id;
  55. }
  56. // it's a nav
  57. if (segment.requiresExplicitNavPrefix) {
  58. return "/" + segment.type + "/" + segment.navId + "/" + segment.id;
  59. }
  60. return "/" + segment.id;
  61. });
  62. return sections.join('');
  63. };
  64. /**
  65. * Serializes a component and its data into a NavSegment.
  66. */
  67. UrlSerializer.prototype.serializeComponent = function (navContainer, component, data) {
  68. if (component) {
  69. var link = findLinkByComponentData(this.links, component, data);
  70. if (link) {
  71. return this._createSegment(this._app, navContainer, link, data);
  72. }
  73. }
  74. return null;
  75. };
  76. /**
  77. * @internal
  78. */
  79. UrlSerializer.prototype._createSegment = function (app, navContainer, configLink, data) {
  80. var urlParts = configLink.segmentParts;
  81. if (isPresent(data)) {
  82. // create a copy of the original parts in the link config
  83. urlParts = urlParts.slice();
  84. // loop through all the data and convert it to a string
  85. var keys = Object.keys(data);
  86. var keysLength = keys.length;
  87. if (keysLength) {
  88. for (var i = 0; i < urlParts.length; i++) {
  89. if (urlParts[i].charAt(0) === ':') {
  90. for (var j = 0; j < keysLength; j++) {
  91. if (urlParts[i] === ":" + keys[j]) {
  92. // this data goes into the URL part (between slashes)
  93. urlParts[i] = encodeURIComponent(data[keys[j]]);
  94. break;
  95. }
  96. }
  97. }
  98. }
  99. }
  100. }
  101. var requiresExplicitPrefix = true;
  102. if (navContainer.parent) {
  103. requiresExplicitPrefix = navContainer.parent && navContainer.parent.getAllChildNavs().length > 1;
  104. }
  105. else {
  106. // if it's a root nav, and there are multiple root navs, we need an explicit prefix
  107. requiresExplicitPrefix = app.getRootNavById(navContainer.id) && app.getRootNavs().length > 1;
  108. }
  109. return {
  110. id: urlParts.join('/'),
  111. name: configLink.name,
  112. component: configLink.component,
  113. loadChildren: configLink.loadChildren,
  114. data: data,
  115. defaultHistory: configLink.defaultHistory,
  116. navId: navContainer.name || navContainer.id,
  117. type: navContainer.getType(),
  118. secondaryId: navContainer.getSecondaryIdentifier(),
  119. requiresExplicitNavPrefix: requiresExplicitPrefix
  120. };
  121. };
  122. return UrlSerializer;
  123. }());
  124. export { UrlSerializer };
  125. export function formatUrlPart(name) {
  126. name = name.replace(URL_REPLACE_REG, '-');
  127. name = name.charAt(0).toLowerCase() + name.substring(1).replace(/[A-Z]/g, function (match) {
  128. return '-' + match.toLowerCase();
  129. });
  130. while (name.indexOf('--') > -1) {
  131. name = name.replace('--', '-');
  132. }
  133. if (name.charAt(0) === '-') {
  134. name = name.substring(1);
  135. }
  136. if (name.substring(name.length - 1) === '-') {
  137. name = name.substring(0, name.length - 1);
  138. }
  139. return encodeURIComponent(name);
  140. }
  141. export var isPartMatch = function (urlPart, configLinkPart) {
  142. if (isPresent(urlPart) && isPresent(configLinkPart)) {
  143. if (configLinkPart.charAt(0) === ':') {
  144. return true;
  145. }
  146. return (urlPart === configLinkPart);
  147. }
  148. return false;
  149. };
  150. export var createMatchedData = function (matchedUrlParts, link) {
  151. var data = null;
  152. for (var i = 0; i < link.segmentPartsLen; i++) {
  153. if (link.segmentParts[i].charAt(0) === ':') {
  154. data = data || {};
  155. data[link.segmentParts[i].substring(1)] = decodeURIComponent(matchedUrlParts[i]);
  156. }
  157. }
  158. return data;
  159. };
  160. export var findLinkByComponentData = function (links, component, instanceData) {
  161. var foundLink = null;
  162. var foundLinkDataMatches = -1;
  163. for (var i = 0; i < links.length; i++) {
  164. var link = links[i];
  165. if (link.component === component) {
  166. // ok, so the component matched, but multiple links can point
  167. // to the same component, so let's make sure this is the right link
  168. var dataMatches = 0;
  169. if (instanceData) {
  170. var instanceDataKeys = Object.keys(instanceData);
  171. // this link has data
  172. for (var j = 0; j < instanceDataKeys.length; j++) {
  173. if (isPresent(link.dataKeys[instanceDataKeys[j]])) {
  174. dataMatches++;
  175. }
  176. }
  177. }
  178. else if (link.dataLen) {
  179. // this component does not have data but the link does
  180. continue;
  181. }
  182. if (dataMatches >= foundLinkDataMatches) {
  183. foundLink = link;
  184. foundLinkDataMatches = dataMatches;
  185. }
  186. }
  187. }
  188. return foundLink;
  189. };
  190. export var normalizeLinks = function (links) {
  191. for (var i = 0, ilen = links.length; i < ilen; i++) {
  192. var link = links[i];
  193. if (isBlank(link.segment)) {
  194. link.segment = link.name;
  195. }
  196. link.dataKeys = {};
  197. link.segmentParts = link.segment.split('/');
  198. link.segmentPartsLen = link.segmentParts.length;
  199. // used for sorting
  200. link.staticLen = link.dataLen = 0;
  201. var stillCountingStatic = true;
  202. for (var j = 0; j < link.segmentPartsLen; j++) {
  203. if (link.segmentParts[j].charAt(0) === ':') {
  204. link.dataLen++;
  205. stillCountingStatic = false;
  206. link.dataKeys[link.segmentParts[j].substring(1)] = true;
  207. }
  208. else if (stillCountingStatic) {
  209. link.staticLen++;
  210. }
  211. }
  212. }
  213. // sort by the number of parts, with the links
  214. // with the most parts first
  215. return links.sort(sortConfigLinks);
  216. };
  217. function sortConfigLinks(a, b) {
  218. // sort by the number of parts
  219. if (a.segmentPartsLen > b.segmentPartsLen) {
  220. return -1;
  221. }
  222. if (a.segmentPartsLen < b.segmentPartsLen) {
  223. return 1;
  224. }
  225. // sort by the number of static parts in a row
  226. if (a.staticLen > b.staticLen) {
  227. return -1;
  228. }
  229. if (a.staticLen < b.staticLen) {
  230. return 1;
  231. }
  232. // sort by the number of total data parts
  233. if (a.dataLen < b.dataLen) {
  234. return -1;
  235. }
  236. if (a.dataLen > b.dataLen) {
  237. return 1;
  238. }
  239. return 0;
  240. }
  241. var URL_REPLACE_REG = /\s+|\?|\!|\$|\,|\.|\+|\"|\'|\*|\^|\||\/|\\|\[|\]|#|%|`|>|<|;|:|@|&|=/g;
  242. /**
  243. * @hidden
  244. */
  245. export var DeepLinkConfigToken = new InjectionToken('USERLINKS');
  246. export function setupUrlSerializer(app, userDeepLinkConfig) {
  247. return new UrlSerializer(app, userDeepLinkConfig);
  248. }
  249. export function navGroupStringtoObjects(navGroupStrings) {
  250. // each string has a known format-ish, convert it to it
  251. return navGroupStrings.map(function (navGroupString) {
  252. var sections = navGroupString.split('/');
  253. if (sections[0] === 'nav') {
  254. return {
  255. type: 'nav',
  256. navId: sections[1],
  257. niceId: sections[1],
  258. secondaryId: null,
  259. segmentPieces: sections.splice(2)
  260. };
  261. }
  262. else if (sections[0] === 'tabs') {
  263. return {
  264. type: 'tabs',
  265. navId: sections[1],
  266. niceId: sections[1],
  267. secondaryId: sections[2],
  268. segmentPieces: sections.splice(3)
  269. };
  270. }
  271. return {
  272. type: null,
  273. navId: null,
  274. niceId: null,
  275. secondaryId: null,
  276. segmentPieces: sections
  277. };
  278. });
  279. }
  280. export function urlToNavGroupStrings(url) {
  281. var tokens = url.split('/');
  282. var keywordIndexes = [];
  283. for (var i = 0; i < tokens.length; i++) {
  284. if (i !== 0 && (tokens[i] === 'nav' || tokens[i] === 'tabs')) {
  285. keywordIndexes.push(i);
  286. }
  287. }
  288. // append the last index + 1 to the list no matter what
  289. keywordIndexes.push(tokens.length);
  290. var groupings = [];
  291. var activeKeywordIndex = 0;
  292. var tmpArray = [];
  293. for (var i = 0; i < tokens.length; i++) {
  294. if (i >= keywordIndexes[activeKeywordIndex]) {
  295. groupings.push(tmpArray.join('/'));
  296. tmpArray = [];
  297. activeKeywordIndex++;
  298. }
  299. tmpArray.push(tokens[i]);
  300. }
  301. // okay, after the loop we've gotta push one more time just to be safe
  302. groupings.push(tmpArray.join('/'));
  303. return groupings;
  304. }
  305. export function convertUrlToSegments(app, url, navLinks) {
  306. var pairs = convertUrlToDehydratedSegments(url, navLinks);
  307. return hydrateSegmentsWithNav(app, pairs);
  308. }
  309. export function convertUrlToDehydratedSegments(url, navLinks) {
  310. var navGroupStrings = urlToNavGroupStrings(url);
  311. var navGroups = navGroupStringtoObjects(navGroupStrings);
  312. return getSegmentsFromNavGroups(navGroups, navLinks);
  313. }
  314. export function hydrateSegmentsWithNav(app, dehydratedSegmentPairs) {
  315. var segments = [];
  316. for (var i = 0; i < dehydratedSegmentPairs.length; i++) {
  317. var navs = getNavFromNavGroup(dehydratedSegmentPairs[i].navGroup, app);
  318. // okay, cool, let's walk through the segments and hydrate them
  319. for (var _i = 0, _a = dehydratedSegmentPairs[i].segments; _i < _a.length; _i++) {
  320. var dehydratedSegment = _a[_i];
  321. if (navs.length === 1) {
  322. segments.push(hydrateSegment(dehydratedSegment, navs[0]));
  323. navs = navs[0].getActiveChildNavs();
  324. }
  325. else if (navs.length > 1) {
  326. // this is almost certainly an async race condition bug in userland
  327. // if you're in this state, it would be nice to just bail here
  328. // but alas we must perservere and handle the issue
  329. // the simple solution is to just use the last child
  330. // because that is probably what the user wants anyway
  331. // remember, do not harm, even if it makes our shizzle ugly
  332. segments.push(hydrateSegment(dehydratedSegment, navs[navs.length - 1]));
  333. navs = navs[navs.length - 1].getActiveChildNavs();
  334. }
  335. else {
  336. break;
  337. }
  338. }
  339. }
  340. return segments;
  341. }
  342. export function getNavFromNavGroup(navGroup, app) {
  343. if (navGroup.navId) {
  344. var rootNav = app.getNavByIdOrName(navGroup.navId);
  345. if (rootNav) {
  346. return [rootNav];
  347. }
  348. return [];
  349. }
  350. // we don't know what nav to use, so just use the root nav.
  351. // if there is more than one root nav, throw an error
  352. return app.getRootNavs();
  353. }
  354. /*
  355. * Let's face the facts: Getting a dehydrated segment from the url is really hard
  356. * because we need to do a ton of crazy looping
  357. * the are chunks of a url that are totally irrelevant at this stage, such as the secondary identifier
  358. * stating which tab is selected, etc.
  359. * but is necessary.
  360. * We look at segment pieces in reverse order to try to build segments
  361. * as in, if you had an array like this
  362. * ['my', 'super', 'cool', 'url']
  363. * we want to look at the pieces in reverse order:
  364. * url
  365. * cool url
  366. * super cool url
  367. * my super cool url
  368. * cool
  369. * super cool
  370. * my super cool
  371. * super
  372. * my super
  373. * my
  374. **/
  375. export function getSegmentsFromNavGroups(navGroups, navLinks) {
  376. var pairs = [];
  377. var usedNavLinks = new Set();
  378. for (var _i = 0, navGroups_1 = navGroups; _i < navGroups_1.length; _i++) {
  379. var navGroup = navGroups_1[_i];
  380. var segments = [];
  381. var segmentPieces = navGroup.segmentPieces.concat([]);
  382. for (var i = segmentPieces.length; i >= 0; i--) {
  383. var created = false;
  384. for (var j = 0; j < i; j++) {
  385. var startIndex = i - j - 1;
  386. var endIndex = i;
  387. var subsetOfUrl = segmentPieces.slice(startIndex, endIndex);
  388. for (var _a = 0, navLinks_1 = navLinks; _a < navLinks_1.length; _a++) {
  389. var navLink = navLinks_1[_a];
  390. if (!usedNavLinks.has(navLink.name)) {
  391. var segment = getSegmentsFromUrlPieces(subsetOfUrl, navLink);
  392. if (segment) {
  393. i = startIndex + 1;
  394. usedNavLinks.add(navLink.name);
  395. created = true;
  396. // sweet, we found a segment
  397. segments.push(segment);
  398. // now we want to null out the url subsection in the segmentPieces
  399. for (var k = startIndex; k < endIndex; k++) {
  400. segmentPieces[k] = null;
  401. }
  402. break;
  403. }
  404. }
  405. }
  406. if (created) {
  407. break;
  408. }
  409. }
  410. if (!created && segmentPieces[i - 1]) {
  411. // this is very likely a tab's secondary identifier
  412. segments.push({
  413. id: null,
  414. name: null,
  415. secondaryId: segmentPieces[i - 1],
  416. component: null,
  417. loadChildren: null,
  418. data: null,
  419. defaultHistory: null
  420. });
  421. }
  422. }
  423. // since we're getting segments in from right-to-left in the url, reverse them
  424. // so they're in the correct order. Also filter out and bogus segments
  425. var orderedSegments = segments.reverse();
  426. // okay, this is the lazy persons approach here.
  427. // so here's the deal! Right now if section of the url is not a part of a segment
  428. // it is almost certainly the secondaryId for a tabs component
  429. // basically, knowing the segment for the `tab` itself is good, but we also need to know
  430. // which tab is selected, so we have an identifer in the url that is associated with the tabs component
  431. // telling us which tab is selected. With that in mind, we are going to go through and find the segments with only secondary identifiers,
  432. // and simply add the secondaryId to the next segment, and then remove the empty segment from the list
  433. for (var i = 0; i < orderedSegments.length; i++) {
  434. if (orderedSegments[i].secondaryId && !orderedSegments[i].id && ((i + 1) <= orderedSegments.length - 1)) {
  435. orderedSegments[i + 1].secondaryId = orderedSegments[i].secondaryId;
  436. orderedSegments[i] = null;
  437. }
  438. }
  439. var cleanedSegments = segments.filter(function (segment) { return !!segment; });
  440. // if the nav group has a secondary id, make sure the first segment also has it set
  441. if (navGroup.secondaryId && segments.length) {
  442. cleanedSegments[0].secondaryId = navGroup.secondaryId;
  443. }
  444. pairs.push({
  445. navGroup: navGroup,
  446. segments: cleanedSegments
  447. });
  448. }
  449. return pairs;
  450. }
  451. export function getSegmentsFromUrlPieces(urlSections, navLink) {
  452. if (navLink.segmentPartsLen !== urlSections.length) {
  453. return null;
  454. }
  455. for (var i = 0; i < urlSections.length; i++) {
  456. if (!isPartMatch(urlSections[i], navLink.segmentParts[i])) {
  457. // just return an empty array if the part doesn't match
  458. return null;
  459. }
  460. }
  461. return {
  462. id: urlSections.join('/'),
  463. name: navLink.name,
  464. component: navLink.component,
  465. loadChildren: navLink.loadChildren,
  466. data: createMatchedData(urlSections, navLink),
  467. defaultHistory: navLink.defaultHistory
  468. };
  469. }
  470. export function hydrateSegment(segment, nav) {
  471. var hydratedSegment = Object.assign({}, segment);
  472. hydratedSegment.type = nav.getType();
  473. hydratedSegment.navId = nav.name || nav.id;
  474. // secondaryId is set on an empty dehydrated segment in the case of tabs to identify which tab is selected
  475. hydratedSegment.secondaryId = segment.secondaryId;
  476. return hydratedSegment;
  477. }
  478. export function getNonHydratedSegmentIfLinkAndUrlMatch(urlChunks, navLink) {
  479. var allSegmentsMatch = true;
  480. for (var i = 0; i < urlChunks.length; i++) {
  481. if (!isPartMatch(urlChunks[i], navLink.segmentParts[i])) {
  482. allSegmentsMatch = false;
  483. break;
  484. }
  485. }
  486. if (allSegmentsMatch) {
  487. return {
  488. id: navLink.segmentParts.join('/'),
  489. name: navLink.name,
  490. component: navLink.component,
  491. loadChildren: navLink.loadChildren,
  492. data: createMatchedData(urlChunks, navLink),
  493. defaultHistory: navLink.defaultHistory
  494. };
  495. }
  496. return null;
  497. }
  498. //# sourceMappingURL=url-serializer.js.map