PageSourceSearch

https://insights.dsij.in/widgets/top-stocks-widget.js

js dsij.in collected 2026-10-01 12:07:47 UTC 9,711 bytes, 349 lines download raw bytes

1(function () {
2  const WIDGET_ID = "dsij-top-stocks";
3
4  // branding
5  const scriptTag = document.currentScript;
6  const showBranding = scriptTag.getAttribute("data-branding") === "true";
7
8  // Detect language
9  const htmlLang = (document.documentElement.lang || "en")
10    .toLowerCase()
11    .split("-")[0];
12
13  const SUPPORTED_LOCALES = ["en", "hi", "mr", "gu", "ta"];
14
15  const currentLang = SUPPORTED_LOCALES.includes(htmlLang) ? htmlLang : "en";
16
17  const LANG_CONFIG = {
18    en: {
19      gainers: "Top Gainers",
20      losers: "Top Losers",
21      lastUpdated: "Updated",
22      poweredBy: "Powered by",
23      branding: "Dalal Street Investment Journal",
24      viewMore: "View More",
25      pathPrefix: "",
26    },
27
28    hi: {
29      gainers: "उच्चतम लाभार्थी",
30      losers: "उच्चतम हारने वाले",
31      lastUpdated: "अपडेट",
32      poweredBy: "द्वारा संचालित",
33      branding: "दलाल स्ट्रीट इन्वेस्टमेंट जर्नल",
34      viewMore: "और देखें",
35      pathPrefix: "/hi",
36    },
37
38    mr: {
39      gainers: "प्रमुख लाभार्थी",
40      losers: "प्रमुख तोटा झालेले",
41      lastUpdated: "अपडेट",
42      poweredBy: "द्वारे समर्थित",
43      branding: "दलाल स्ट्रीट इन्व्हेस्टमेंट जर्नल",
44      viewMore: "अधिक पहा",
45      pathPrefix: "/mr",
46    },
47
48    gu: {
49      gainers: "ટોચના નફો કરનારા",
50      losers: "ટોચના ગુમાવનારા",
51      lastUpdated: "અપડેટ",
52      poweredBy: "દ્વારા સંચાલિત",
53      branding: "દલાલ સ્ટ્રીટ ઇન્વેસ્ટમેન્ટ જર્નલ",
54      viewMore: "વધુ જુઓ",
55      viewMore: "மேலும் காண்க",
56      pathPrefix: "/gu",
57    },
58
59    ta: {
60      gainers: "முதலிடம் பெற்றவர்கள்",
61      losers: "அதிக தோல்வியடைந்தவர்கள்",
62      lastUpdated: "கடைசியாக புதுப்பிக்கப்பட்டது",
63      poweredBy: "ஆல் இயக்கப்படுகிறது",
64      branding: "தலால் ஸ்ட்ரீட் முதலீட்டு இதழ்",
65      pathPrefix: "/ta",
66    },
67  };
68
69  const TEXT = LANG_CONFIG[currentLang];
70
71  const BASE_URL = "https://insights.dsij.in"; // Replace with actual base URL of the DSIJ website
72
73  const LOCALE = TEXT.pathPrefix;
74
75  const cache = { gainers: null, losers: null };
76
77  // Auto-create container if missing
78  let container = document.getElementById(WIDGET_ID);
79
80  if (!container) {
81    container = document.createElement("div");
82    container.id = WIDGET_ID;
83
84    document.currentScript.parentNode.insertBefore(
85      container,
86      document.currentScript,
87    );
88  }
89
90  // Inject CSS
91  if (!document.getElementById("dsij-top-stocks-css")) {
92    const link = document.createElement("link");
93
94    link.id = "dsij-top-stocks-css";
95    link.rel = "stylesheet";
96
97    const scriptSrc = document.currentScript.src;
98    const baseUrl = scriptSrc.split("/top-stocks-widget.js")[0];
99
100    link.href = baseUrl + "/top-stocks-widget.css";
101
102    document.head.appendChild(link);
103  }
104
105  container.innerHTML = `
106    <div class="tabs">
107      <div class="tab gainer active" data-type="gainers">
108        <span class="arrow-up">&#8593;</span>
109        ${TEXT.gainers}
110      </div>
111
112      <div class="tab loser" data-type="losers">
113        <span class="arrow-down">&#8595;</span>
114        ${TEXT.losers}
115      </div>
116    </div>
117
118    <div class="content" style="min-height: 240px;"></div>
119    
120    <div class="last-updated"></div>
121
122    <div class="view-more-container"></div>
123
124    ${
125      showBranding
126        ? `
127        <a
128          class="branding"
129          href="https://www.dsij.in${LOCALE}"
130          target="_blank"
131          rel="noopener noreferrer"
132        >
133          <img
134            class="branding-logo"
135            src="https://dsijpub.s3.ap-south-1.amazonaws.com/portals/0/images/DSIJ_favicon.jpg"
136            alt="DS Logo"
137          />
138
139          ${TEXT.poweredBy}
140          <span>${TEXT.branding}</span>
141        </a>
142      `
143        : ""
144    }
145  `;
146
147  const content = container.querySelector(".content");
148
149  const lastUpdatedEl = container.querySelector(".last-updated");
150
151  const viewMoreContainer = container.querySelector(".view-more-container");
152
153  function updateLastUpdated(data) {
154    if (!Array.isArray(data) || !data.length) {
155      lastUpdatedEl.innerHTML = "";
156      return;
157    }
158
159    const maxDate = data.reduce((latest, stock) => {
160      const current = new Date(stock.trade_date);
161
162      return current > latest ? current : latest;
163    }, new Date(0));
164
165    lastUpdatedEl.innerHTML = `
166    <span class="last-updated">
167       ${TEXT.lastUpdated}: ${maxDate.toLocaleString("en-IN", {
168         day: "2-digit",
169         month: "short",
170         year: "numeric",
171         hour: "2-digit",
172         minute: "2-digit",
173       })} (IST)
174    </span>
175  `;
176  }
177
178  function formatNumber(num) {
179    return Number(num || 0).toLocaleString("en-IN");
180  }
181
182  function createSlug(companyname, code) {
183    if (!companyname || !code) return "#";
184
185    return (
186      String(companyname)
187        .trim()
188        .replace(/[^a-zA-Z0-9]+/g, "-")
189        .replace(/-+/g, "-")
190        .replace(/(^-|-$)/g, "")
191        .toLowerCase() +
192      "-" +
193      code
194    );
195  }
196
197  function render(data, type) {
198    if (!data || !data.length) {
199      content.innerHTML = `<div class="center">No data available</div>`;
200      return;
201    }
202
203    const isGainer = type === "gainers";
204
205    let html = "<table>";
206
207    data.slice(0, 5).forEach((stock) => {
208      const pct = Number(stock.change_pct || 0);
209
210      html += `
211        <tr>
212          <td>
213            <div class="stock-info">
214
215              ${
216                isGainer
217                  ? `
218                <div class="chart-line" style="background:#d0ffcf;">
219                  <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 20" width="20" height="20">
220                    <path d="M16 6l2.29 2.29-4.88 4.88-4-4L2 16.59 3.41 18l6-6 4 4 6.3-6.29L22 12V6z" fill="green"></path>
221                  </svg>
222                </div>
223              `
224                  : `
225                <div class="chart-line" style="background:#ffcfcf;">
226                  <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 20">
227                    <path d="M16 18l2.29-2.29-4.88-4.88-4 4L2 7.41 3.41 6l6 6 4-4 6.3 6.29L22 12v6z" fill="red"></path>
228                  </svg>
229                </div>
230              `
231              }
232
233              <div>
234                <a
235                  target="_parent"
236                  rel="noopener noreferrer"
237                  class="ticker-link"
238                  href="${BASE_URL}${LOCALE}/share-price/${createSlug(
239                    stock["company name"],
240                    stock.companycode,
241                  )}"
242                >
243                  ${stock.ticker || "-"}
244                </a>
245
246                <p class="mb-0 small">
247                  ${stock["company name"] || "-"}
248                </p>
249              </div>
250
251            </div>
252          </td>
253
254          <td>
255            ${formatNumber(stock.last_amt)}
256
257            <p class="mb-0 small ${pct > 0 ? "green" : "red"}">
258              ${stock.change_amt || 0} (${pct}%)
259            </p>
260          </td>
261        </tr>
262      `;
263    });
264
265    html += "</table>";
266
267    content.innerHTML = html;
268
269    const viewMoreUrl = isGainer
270      ? "https://www.dsij.in/r/xvBz"
271      : "https://www.dsij.in/r/z7Rd";
272
273    viewMoreContainer.innerHTML = `
274  <a
275    class="view-more-btn"
276    href="${viewMoreUrl}"
277    target="_parent"
278    rel="noopener noreferrer"
279  >
280     ${TEXT.viewMore}
281  </a>
282`;
283  }
284
285  async function load(type) {
286    if (cache[type]) {
287      updateLastUpdated(cache[type]);
288      render(cache[type], type);
289      return;
290    }
291
292    content.innerHTML = `<div class="center">Loading...</div>`;
293
294    const statType = type === "gainers" ? "TopGainer" : "TopLooser";
295
296    try {
297      const res = await fetch(`${BASE_URL}/api/market-stats`, {
298        method: "POST",
299
300        headers: {
301          "Content-Type": "application/json",
302        },
303
304        body: JSON.stringify({
305          apiType: "TopGainerLooser",
306          statType,
307        }),
308      });
309
310      if (!res.ok) {
311        throw new Error("API error");
312      }
313
314      const response = await res.json();
315
316      const data = Array.isArray(response) ? response : response.results || [];
317
318      updateLastUpdated(data);
319
320      cache[type] = data;
321
322      render(data, type);
323    } catch (e) {
324      console.error(e);
325
326      content.innerHTML = `
327    <div class="center">
328      Failed to load
329    </div>
330  `;
331    }
332  }
333
334  // Tab switching
335  container.querySelectorAll(".tab").forEach((tab) => {
336    tab.addEventListener("click", function () {
337      container
338        .querySelectorAll(".tab")
339        .forEach((t) => t.classList.remove("active"));
340
341      this.classList.add("active");
342
343      load(this.dataset.type);
344    });
345  });
346
347  // Initial load
348  load("gainers");
349})();

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.