Instagram - 为用户添加备注(别名/标签)

为用户添加备注(别名/标签)功能,以帮助识别和搜索,并支持 WebDAV 同步功能

当前为 2023-04-08 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Instagram - Add notes to the user
  3. // @name:zh-CN Instagram - 为用户添加备注(别名/标签)
  4. // @name:zh-TW Instagram - 為使用者新增備註(別名/標籤)
  5. // @namespace https://greasyfork.org/zh-CN/users/193133-pana
  6. // @homepage https://greasyfork.org/zh-CN/users/193133-pana
  7. // @icon data:image/svg+xml;base64,PHN2ZyByb2xlPSJpbWciIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjI0cHgiIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAwIDI0IDI0IiBhcmlhLWxhYmVsbGVkYnk9Im5ld0ljb25UaXRsZSIgc3Ryb2tlPSJyZ2JhKDI5LDE2MSwyNDIsMS4wMCkiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InNxdWFyZSIgc3Ryb2tlLWxpbmVqb2luPSJtaXRlciIgZmlsbD0ibm9uZSIgY29sb3I9InJnYmEoMjksMTYxLDI0MiwxLjAwKSI+IDx0aXRsZSBpZD0ibmV3SWNvblRpdGxlIj5OZXc8L3RpdGxlPiA8cGF0aCBkPSJNMTkgMTRWMjJIMi45OTk5N1Y0SDEzIi8+IDxwYXRoIGQ9Ik0xNy40NjA4IDQuMDM5MjFDMTguMjQxOCAzLjI1ODE3IDE5LjUwODIgMy4yNTgxNiAyMC4yODkyIDQuMDM5MjFMMjAuOTYwOCA0LjcxMDc5QzIxLjc0MTggNS40OTE4NCAyMS43NDE4IDYuNzU4MTcgMjAuOTYwOCA3LjUzOTIxTDExLjU4NTggMTYuOTE0MkMxMS4yMTA3IDE3LjI4OTMgMTAuNzAyIDE3LjUgMTAuMTcxNiAxNy41TDcuNSAxNy41TDcuNSAxNC44Mjg0QzcuNSAxNC4yOTggNy43MTA3MSAxMy43ODkzIDguMDg1NzkgMTMuNDE0MkwxNy40NjA4IDQuMDM5MjFaIi8+IDxwYXRoIGQ9Ik0xNi4yNSA1LjI1TDE5Ljc1IDguNzUiLz4gPC9zdmc+
  8. // @version 6.1.0
  9. // @description Add notes (aliases/tags) for users to help identify and search, and support WebDAV sync
  10. // @description:zh-CN 为用户添加备注(别名/标签)功能,以帮助识别和搜索,并支持 WebDAV 同步功能
  11. // @description:zh-TW 為使用者新增備註(別名/標籤)功能,以幫助識別和搜尋,並支援 WebDAV 同步功能
  12. // @license GNU General Public License v3.0 or later
  13. // @compatible chrome
  14. // @compatible firefox
  15. // @author pana
  16. // @match *://*.instagram.com/*
  17. // @require https://gcore.jsdelivr.net/npm/arrive@2.4.1/minified/arrive.min.js
  18. // @require https://gcore.jsdelivr.net/gh/LightAPIs/greasy-fork-library@67b6b108dcea7594a8343b6501094fbb81fcf4a1/Note_Obj.js
  19. // @@connect *
  20. // @noframes
  21. // @grant GM_info
  22. // @grant GM_getValue
  23. // @grant GM_setValue
  24. // @grant GM_deleteValue
  25. // @grant GM_listValues
  26. // @grant GM_openInTab
  27. // @grant GM_addStyle
  28. // @grant GM_registerMenuCommand
  29. // @grant GM_unregisterMenuCommand
  30. // @grant GM_addValueChangeListener
  31. // @grant GM_removeValueChangeListener
  32. // ==/UserScript==
  33.  
  34. (function () {
  35. 'use strict';
  36. const UPDATED = '2023-02-27';
  37. const INS_ICON = {
  38. NOTE_BLACK: 'url(data:image/svg+xml;base64,PHN2ZyByb2xlPSJpbWciIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjI0cHgiIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAwIDI0IDI0IiBhcmlhLWxhYmVsbGVkYnk9Im5ld0ljb25UaXRsZSIgc3Ryb2tlPSJyZ2IoMzgsIDM4LCAzOCkiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InNxdWFyZSIgc3Ryb2tlLWxpbmVqb2luPSJtaXRlciIgZmlsbD0ibm9uZSIgY29sb3I9InJnYigzOCwgMzgsIDM4KSI+IDx0aXRsZSBpZD0ibmV3SWNvblRpdGxlIj5OZXc8L3RpdGxlPiA8cGF0aCBkPSJNMTkgMTRWMjJIMi45OTk5N1Y0SDEzIi8+IDxwYXRoIGQ9Ik0xNy40NjA4IDQuMDM5MjFDMTguMjQxOCAzLjI1ODE3IDE5LjUwODIgMy4yNTgxNiAyMC4yODkyIDQuMDM5MjFMMjAuOTYwOCA0LjcxMDc5QzIxLjc0MTggNS40OTE4NCAyMS43NDE4IDYuNzU4MTcgMjAuOTYwOCA3LjUzOTIxTDExLjU4NTggMTYuOTE0MkMxMS4yMTA3IDE3LjI4OTMgMTAuNzAyIDE3LjUgMTAuMTcxNiAxNy41TDcuNSAxNy41TDcuNSAxNC44Mjg0QzcuNSAxNC4yOTggNy43MTA3MSAxMy43ODkzIDguMDg1NzkgMTMuNDE0MkwxNy40NjA4IDQuMDM5MjFaIi8+IDxwYXRoIGQ9Ik0xNi4yNSA1LjI1TDE5Ljc1IDguNzUiLz4gPC9zdmc+)'
  39. };
  40. const INS_STYLE = `
  41. .note-obj-ins-background-box {
  42. display: inline-block;
  43. align-items: center;
  44. white-space: nowrap;
  45. border-radius: 50px;
  46. padding: 0px 10px;
  47. background-color: #336699;
  48. color: #fff;
  49. }
  50. .note-obj-ins-add-btn {
  51. background-image: ${INS_ICON.NOTE_BLACK};
  52. background-size: 24px;
  53. background-repeat: no-repeat;
  54. background-position: center;
  55. margin-left: 5px;
  56. cursor: pointer;
  57. width: 24px;
  58. height: 24px;
  59. }
  60. .note-obj-ins-homepage-btn {
  61. margin: 6px !important;
  62. }
  63. .note-obj-ins-homepage-btn:hover {
  64. opacity: 0.5;
  65. }
  66. .note-obj-ins-userpage-btn {
  67. margin-top: 2px;
  68. }
  69. .note-obj-ins-userpage-tag {
  70. display: block;
  71. font-size: 20px;
  72. margin-bottom: 20px;
  73. white-space: nowrap;
  74. }
  75. .note-obj-ins-font-bold {
  76. font-weight: bold;
  77. }
  78. .note-obj-veryins-blue-tag {
  79. background-color: #3c81df;
  80. color: #fff;
  81. display: inline-flex;
  82. align-items: center;
  83. padding: 0px 10px;
  84. white-space: nowrap;
  85. line-height: 100%;
  86. border-radius: 50px;
  87. padding: 2px 10px;
  88. }
  89. .note-obj-veryins-userpage-btn {
  90. display: inline-block;
  91. vertical-align: middle;
  92. }
  93. .note-obj-settings-frame-card label {
  94. color: #2f2f2f;
  95. }
  96. .note-obj-interface-dark .note-obj-settings-frame-card label {
  97. color: #fff;
  98. }
  99. .note-obj-group-frame-tbody input {
  100. color: #000;
  101. }`;
  102. const selector = {
  103. homepage: {
  104. article: '[role="main"] article',
  105. id: '._aaqt a',
  106. icon: 'span._aamz',
  107. commentId: '._ab8x .xt0psk2 a.notranslate',
  108. commentAt: '._ab8x ._aacl a.notranslate'
  109. },
  110. homepageStories: {
  111. id: '._aad6',
  112. idShell: 'li [role="menuitem"]'
  113. },
  114. homepageRecommend: {
  115. id: '._aak3 ._ab8x .xt0psk2 a'
  116. },
  117. userPage: {
  118. frame: '._aa_y',
  119. id: 'h2',
  120. bar: '.x8j4wrb',
  121. box: 'ul',
  122. common: 'span._aaai',
  123. suggest: '._acj1 a.notranslate',
  124. infoAt: '.notranslate',
  125. userName: '._aa_c > span'
  126. },
  127. watchList: {
  128. initialItem: '[role="dialog"] [aria-labelledby]',
  129. laterItem: '._aaei',
  130. id: '._ab8w a.notranslate'
  131. },
  132. stories: {
  133. id: 'a.notranslate',
  134. idShell: '._ac0o',
  135. cardId: '._afgd ._aacw'
  136. },
  137. dialog: {
  138. frame: '[role="dialog"] article',
  139. commentId: '._a9zc ._ab8w a, ._aacx._aacu a',
  140. commentAt: '._a9zs .notranslate'
  141. },
  142. request: {
  143. follow: '._aajc ._ab8x .xt0psk2 a'
  144. },
  145. suggest: {
  146. user: '._aa0- ._ab8x .xt0psk2 a'
  147. }
  148. };
  149. const noteObj = new Note_Obj({
  150. id: 'myInstagramNote',
  151. script: {
  152. author: {
  153. name: 'pana',
  154. homepage: 'https://greasyfork.org/zh-CN/users/193133-pana'
  155. },
  156. url: 'https://greasyfork.org/scripts/387871',
  157. updated: UPDATED,
  158. library: [{
  159. name: 'arrive.js',
  160. version: '2.4.1',
  161. url: 'https://github.com/uzairfarooq/arrive'
  162. }]
  163. },
  164. style: INS_STYLE,
  165. primaryColor: '#336699',
  166. settings: {
  167. replaceHomepageID: {
  168. type: 'checkbox',
  169. lang: {
  170. en: 'Allow replacing user IDs on the home page',
  171. zhHans: '允许替换首页上的用户 ID',
  172. zhHant: '允許替換首頁上的使用者 ID'
  173. },
  174. default: true,
  175. event: instagramHomepageEvent
  176. }
  177. },
  178. changeEvent: instagramChangeEvent
  179. });
  180. function homepageNote(ele, changeId) {
  181. const user = noteObj.fn.queryAnchor(ele, selector.homepage.id);
  182. if (user) {
  183. const replaceHomepageID = noteObj.getOtherConfig().replaceHomepageID === true;
  184. const eleId = noteObj.fn.getIdFromUrl(user.href);
  185. if (!changeId || changeId === eleId) {
  186. noteObj.handler(eleId, user, undefined, {
  187. add: replaceHomepageID ? undefined : 'sapn',
  188. className: replaceHomepageID ? undefined : ['note-obj-ins-background-box']
  189. });
  190. }
  191. }
  192. }
  193. function homepageCommentNote(ele, changeId) {
  194. for (const comment of noteObj.fn.queryAllAnchor(ele, selector.homepage.commentId, 'info')) {
  195. const commentId = noteObj.fn.getIdFromUrl(comment.href);
  196. if (!changeId || changeId === commentId) {
  197. noteObj.handler(commentId, comment);
  198. }
  199. }
  200. }
  201. function homepageCommentAtNote(ele, changeId) {
  202. const commentAtId = noteObj.fn.getIdFromUrl(ele.href);
  203. if (!changeId || changeId === commentAtId) {
  204. noteObj.handler(commentAtId, ele, undefined, {
  205. prefix: '@',
  206. title: true
  207. });
  208. }
  209. }
  210. function dialogCommentNote(ele, chagneId) {
  211. const picCommentId = noteObj.fn.getIdFromUrl(ele.href);
  212. if (!chagneId || chagneId === picCommentId) {
  213. noteObj.handler(picCommentId, ele);
  214. }
  215. }
  216. function dialogCommentAtNote(ele, changeId) {
  217. if (!ele.classList.contains(selector.homepage.commentId.replace(/^\.|\s+.*$/g, ''))) {
  218. const picCommentAtId = noteObj.fn.getIdFromUrl(ele.href);
  219. if (!changeId || changeId === picCommentAtId) {
  220. noteObj.handler(picCommentAtId, ele, undefined, {
  221. prefix: '@',
  222. title: true
  223. });
  224. }
  225. }
  226. }
  227. function homepageStoriesNote(ele, changeId) {
  228. const homepageStoriesId = noteObj.fn.getText(ele, selector.homepageStories.id);
  229. if (!changeId || changeId === homepageStoriesId) {
  230. ele.title = noteObj.getUserTag(homepageStoriesId);
  231. }
  232. }
  233. function anchorElementNote(ele, changeId) {
  234. const itemId = noteObj.fn.getIdFromUrl(ele.href);
  235. if (!changeId || changeId === itemId) {
  236. noteObj.handler(itemId, ele);
  237. }
  238. }
  239. function userPageNote(ele, changeId) {
  240. const userPageId = noteObj.fn.getText(ele, selector.userPage.id);
  241. const userPageBox = noteObj.fn.query(ele, selector.userPage.box);
  242. if (userPageId && userPageBox) {
  243. if (changeId) {
  244. if (changeId === userPageId) {
  245. noteObj.handler(userPageId, ele, undefined, {
  246. add: 'div',
  247. after: userPageBox,
  248. maskSecondaryColor: true,
  249. offsetWidth: -20,
  250. className: ['note-obj-ins-userpage-tag', 'note-obj-ins-font-bold']
  251. });
  252. }
  253. } else {
  254. const userNameText = noteObj.fn.getText(ele, selector.userPage.userName, 'warn');
  255. noteObj.handler(userPageId, ele, undefined, {
  256. add: 'div',
  257. after: userPageBox,
  258. maskSecondaryColor: true,
  259. offsetHeight: -20,
  260. className: ['note-obj-ins-userpage-tag', 'note-obj-ins-font-bold']
  261. }, userNameText);
  262. }
  263. }
  264. }
  265. function userPageCommonNote(ele, changeId) {
  266. for (const commonUser of noteObj.fn.queryAll(ele, selector.userPage.common, 'info')) {
  267. const commonUserId = commonUser.textContent?.trim();
  268. if (commonUserId) {
  269. if (!changeId || changeId === commonUserId) noteObj.handler(commonUserId, commonUser, undefined, {
  270. title: true,
  271. notModify: true
  272. });
  273. }
  274. }
  275. }
  276. function userPageInfoAtNote(ele, changeId) {
  277. for (const infoAtUser of noteObj.fn.queryAllAnchor(ele, selector.userPage.infoAt, 'info')) {
  278. const infoAtUserId = noteObj.fn.getIdFromUrl(infoAtUser.href);
  279. if (!changeId || changeId === infoAtUserId) {
  280. noteObj.handler(infoAtUserId, infoAtUser, undefined, {
  281. prefix: '@',
  282. title: true
  283. });
  284. }
  285. }
  286. }
  287. function storiesNote(ele, changeId) {
  288. itemNote(ele, selector.stories.id, changeId);
  289. noteObj.fn.docQueryAll(selector.stories.cardId).forEach(item => {
  290. const itemId = item.textContent?.trim() || '';
  291. if (!changeId || changeId === itemId) {
  292. noteObj.handler(itemId, item, undefined, {
  293. notModify: true,
  294. title: true
  295. });
  296. }
  297. });
  298. }
  299. function watchListItemNote(ele, changeId) {
  300. itemNote(ele, selector.watchList.id, changeId);
  301. }
  302. function itemNote(ele, idSelector, changeId) {
  303. const item = noteObj.fn.queryAnchor(ele, idSelector);
  304. if (item) {
  305. const itemId = noteObj.fn.getIdFromUrl(item.href);
  306. if (!changeId || changeId === itemId) {
  307. noteObj.handler(itemId, item);
  308. }
  309. }
  310. }
  311. function instagramChangeEvent(changeId) {
  312. for (const article of noteObj.fn.docQueryAll(selector.homepage.article, 'none')) {
  313. homepageNote(article, changeId);
  314. homepageCommentNote(article, changeId);
  315. for (const commentAt of noteObj.fn.docQueryAllAnchor(selector.homepage.commentAt, 'none')) {
  316. homepageCommentAtNote(commentAt, changeId);
  317. }
  318. for (const picCommentUser of noteObj.fn.docQueryAllAnchor(selector.dialog.commentId, 'none')) {
  319. dialogCommentNote(picCommentUser, changeId);
  320. }
  321. for (const picCommentAt of noteObj.fn.docQueryAllAnchor(selector.dialog.commentAt, 'none')) {
  322. dialogCommentAtNote(picCommentAt, changeId);
  323. }
  324. }
  325. for (const homepageStories of noteObj.fn.docQueryAll(selector.homepageStories.idShell, 'none')) {
  326. homepageStoriesNote(homepageStories, changeId);
  327. }
  328. for (const homepageRecommend of noteObj.fn.docQueryAllAnchor(selector.homepageRecommend.id, 'none')) {
  329. anchorElementNote(homepageRecommend, changeId);
  330. }
  331. for (const userPage of noteObj.fn.docQueryAll(selector.userPage.frame, 'none')) {
  332. userPageNote(userPage, changeId);
  333. userPageCommonNote(userPage, changeId);
  334. userPageInfoAtNote(userPage, changeId);
  335. }
  336. for (const storiesShell of noteObj.fn.docQueryAll(selector.stories.idShell, 'none')) {
  337. storiesNote(storiesShell, changeId);
  338. }
  339. for (const initial of noteObj.fn.docQueryAll(selector.watchList.initialItem, 'none')) {
  340. watchListItemNote(initial, changeId);
  341. }
  342. for (const later of noteObj.fn.docQueryAll(selector.watchList.laterItem, 'none')) {
  343. watchListItemNote(later, changeId);
  344. }
  345. for (const dialog of noteObj.fn.docQueryAll(selector.dialog.frame, 'none')) {
  346. homepageNote(dialog, changeId);
  347. homepageCommentNote(dialog, changeId);
  348. for (const commentUser of noteObj.fn.docQueryAllAnchor(selector.dialog.commentId, 'none')) {
  349. dialogCommentNote(commentUser, changeId);
  350. }
  351. for (const commentAt of noteObj.fn.docQueryAllAnchor(selector.dialog.commentAt, 'none')) {
  352. dialogCommentAtNote(commentAt, changeId);
  353. }
  354. }
  355. for (const follow of noteObj.fn.docQueryAllAnchor(selector.request.follow, 'none')) {
  356. anchorElementNote(follow, changeId);
  357. }
  358. for (const suggestUser of noteObj.fn.docQueryAllAnchor(selector.suggest.user, 'none')) {
  359. anchorElementNote(suggestUser, changeId);
  360. }
  361. for (const suggest of noteObj.fn.docQueryAllAnchor(selector.userPage.suggest, 'none')) {
  362. anchorElementNote(suggest, changeId);
  363. }
  364. }
  365. function instagramHomepageEvent(newValue, oldValue) {
  366. for (const article of noteObj.fn.docQueryAll(selector.homepage.article)) {
  367. const articleUser = noteObj.fn.queryAnchor(article, selector.homepage.id);
  368. if (articleUser) {
  369. const articleUserId = noteObj.fn.getIdFromUrl(articleUser.href);
  370. noteObj.handler(articleUserId, articleUser, undefined, {
  371. add: oldValue ? undefined : 'span',
  372. className: oldValue ? undefined : ['note-obj-ins-background-box'],
  373. title: oldValue,
  374. restore: true
  375. });
  376. noteObj.handler(articleUserId, articleUser, undefined, {
  377. add: newValue ? undefined : 'span',
  378. className: newValue ? undefined : ['note-obj-ins-background-box'],
  379. title: newValue
  380. });
  381. }
  382. }
  383. }
  384. function initInstagram() {
  385. const arriveOption = {
  386. fireOnAttributesModification: true,
  387. existing: true
  388. };
  389. document.body.arrive(selector.homepage.article, arriveOption, article => {
  390. const homepageIcon = noteObj.fn.query(article, selector.homepage.icon);
  391. const articleUserId = noteObj.fn.getUrlId(article, selector.homepage.id);
  392. if (homepageIcon && articleUserId) {
  393. homepageIcon.insertAdjacentElement('beforebegin', noteObj.createNoteBtn(articleUserId, undefined, ['note-obj-ins-add-btn', 'note-obj-ins-homepage-btn'], 'span'));
  394. }
  395. homepageNote(article);
  396. homepageCommentNote(article);
  397. article.arrive(selector.homepage.commentAt, arriveOption, commentAt => {
  398. homepageCommentAtNote(commentAt);
  399. });
  400. article.arrive(selector.dialog.commentId, arriveOption, picCommentUser => {
  401. dialogCommentNote(picCommentUser);
  402. });
  403. article.arrive(selector.dialog.commentAt, arriveOption, picCommentAt => {
  404. dialogCommentAtNote(picCommentAt);
  405. });
  406. });
  407. document.body.arrive(selector.homepageStories.idShell, arriveOption, homepageStories => {
  408. homepageStoriesNote(homepageStories);
  409. });
  410. document.body.arrive(selector.homepageRecommend.id, arriveOption, homepageRecommend => {
  411. anchorElementNote(homepageRecommend);
  412. });
  413. document.body.arrive(selector.userPage.frame, arriveOption, userPage => {
  414. const userPageBar = noteObj.fn.query(userPage, selector.userPage.bar);
  415. const userPageId = noteObj.fn.getText(userPage, selector.userPage.id);
  416. if (userPageBar && userPageId) {
  417. const userNameText = noteObj.fn.getText(userPage, selector.userPage.userName, 'info');
  418. userPageBar.after(noteObj.createNoteBtn(userPageId, userNameText, ['note-obj-ins-add-btn', 'note-obj-ins-userpage-btn']));
  419. }
  420. userPageNote(userPage);
  421. userPageCommonNote(userPage);
  422. userPageInfoAtNote(userPage);
  423. });
  424. document.body.arrive(selector.stories.idShell, arriveOption, storiesShell => {
  425. storiesNote(storiesShell);
  426. const stories = noteObj.fn.queryAnchor(storiesShell, selector.stories.id);
  427. if (stories) {
  428. const userIdChange = new MutationObserver(() => {
  429. const newUserId = noteObj.fn.getIdFromUrl(stories.href);
  430. if (noteObj.judgeUsers(newUserId)) {
  431. noteObj.handler(newUserId, stories);
  432. } else {
  433. noteObj.handler(newUserId, stories, undefined, {
  434. restore: true
  435. });
  436. }
  437. });
  438. userIdChange.observe(stories, {
  439. attributeFilter: ['href']
  440. });
  441. }
  442. });
  443. document.body.arrive(selector.watchList.initialItem, arriveOption, initial => {
  444. watchListItemNote(initial);
  445. });
  446. document.body.arrive(selector.watchList.laterItem, arriveOption, later => {
  447. watchListItemNote(later);
  448. });
  449. document.body.arrive(selector.dialog.frame, arriveOption, dialog => {
  450. const homepageIcon = noteObj.fn.query(dialog, selector.homepage.icon);
  451. const dialogUserId = noteObj.fn.getUrlId(dialog, selector.homepage.id);
  452. if (homepageIcon && dialogUserId) {
  453. homepageIcon.insertAdjacentElement('beforebegin', noteObj.createNoteBtn(dialogUserId, undefined, ['note-obj-ins-add-btn', 'note-obj-ins-homepage-btn'], 'span'));
  454. }
  455. homepageNote(dialog);
  456. homepageCommentNote(dialog);
  457. dialog.arrive(selector.dialog.commentId, arriveOption, commentUser => {
  458. dialogCommentNote(commentUser);
  459. });
  460. dialog.arrive(selector.dialog.commentAt, arriveOption, commentAt => {
  461. dialogCommentAtNote(commentAt);
  462. });
  463. });
  464. document.body.arrive(selector.request.follow, arriveOption, follow => {
  465. anchorElementNote(follow);
  466. });
  467. document.body.arrive(selector.suggest.user, arriveOption, suggestUser => {
  468. anchorElementNote(suggestUser);
  469. });
  470. document.body.arrive(selector.userPage.suggest, arriveOption, suggest => {
  471. anchorElementNote(suggest);
  472. });
  473. }
  474. initInstagram();
  475. })();