Advertisement
Jakobhorak28

Untitled

Feb 25th, 2022
54
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 23.71 KB | None | 0 0
  1. // ==UserScript==
  2. // @name TISS Quick Registration Script
  3. // @namespace http://www.manuelgeier.com/
  4. // @version 1.6.3
  5. // @description Script to help you to get into the group you want. Opens automatically the right panel, registers automatically and confirms your registration automatically. If you don't want the script to do everything automatically, the focus is already set on the right button, so you only need to confirm. There is also an option available to auto refresh the page, if the registration button is not available yet, so you can open the site and watch the script doing its work. You can also set a specific time when the script should reload the page and start.
  6. // @match https://tiss.tuwien.ac.at/*
  7. // @copyright 2012+, Manuel Geier, THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  8. // @require http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js
  9. // ==/UserScript==
  10.  
  11. /*
  12. Changelog:
  13.  
  14. v.1.6.3 [18.12.2020]
  15. + Added: date of exam support
  16.  
  17. v.1.6.2 [09.01.2020]
  18. + Improve countdown format (Thanks @Heholord, #14)
  19.  
  20. v.1.6.1 [13.10.2019]
  21. ~ Fix: Ignore multiple spaces on group label comparison. (@zarmonious, #13)
  22.  
  23. v.1.6.0 [28.11.2018]
  24. + Added: exam-registration support (Thanks to @XtomtomX, #11)
  25.  
  26. v1.5.3 [29.02.2016]
  27. ~ Added: .gitignore
  28. ~ Fix: missing reference to 'options' object
  29. ~ Fix: wrong option name ('semesterCheckEnabled' instead of 'lvaSemesterCheckEnabled')
  30. ~ String/Number compare with === instead of ==, and !== instead of !=
  31. ~ Fix #9: id no longer available for wrapper element. replace it by element itself and adjust selectors.
  32. ~ Fix: toggle for groups (now without id selector)
  33. ~ Fix: group name selector now matches the exact name and not only if it contains the name
  34.  
  35. v1.5.2 [?]
  36. - went missing :P
  37.  
  38. v1.5.1 [09.10.2015]
  39. ~ Fix: adjusts group label selector
  40. ~ Fix: Remove leading zero for month which leads to unintended octal interpretation
  41.  
  42. v1.5 [04.10.2015]
  43. + allow to enter a study code, if you have multiple ones
  44. + add flags to en-/disable checks
  45. ~ Code cleanup
  46.  
  47. v1.4 [07.09.2013]
  48. + show or hide logoutput on screen (option: showLog [true/false])
  49. + many improvements
  50. ~ Code refactoring
  51.  
  52. v1.3 [01.03.2013]
  53. + Feature: automatically presses the Ok button at the final info page
  54. ~ Code refactoring
  55.  
  56. v1.2 [27.02.2013]
  57. + Feature: ability to register to a LVA; just set 'isGroupRegistration' to 'false'
  58. + Feature: selected group label is now marked with light green
  59. ~ Bugfix/Quickfix: overflow in setTimeout causes an constant refresh of the page; now the page gets refreshed at least often if the specific start time is in the future
  60.  
  61. v1.1
  62. + Feature: Let the script start at a specific time.
  63.  
  64. v1.0 (2012)
  65. Initial release
  66. */
  67.  
  68. (function TissQuickRegistrationClass() {
  69. var self = this;
  70.  
  71. ///////////////////////////////////////////////////////////////////////
  72. // Configurate the script here
  73. //
  74.  
  75. var options = {
  76. // global option to enable or disable the script [true,false]
  77. scriptEnabled: true,
  78.  
  79. // define here the type of registration [lva,group,exam]
  80. registrationType: "exam",
  81.  
  82. // name of you the group you want to join (only for registrationType 'group') [String]
  83. nameOfGroup: "Gruppe 001",
  84.  
  85. // name of the exam which you want to join (only for registrationType 'exam') [String]
  86. nameOfExam: 'Einstufungstest K2',
  87.  
  88. // date of the exam which you want to join, especially when there are multiple exams with the same name (only for registrationType 'exam') [String]
  89. dateOfExam: '04.03.2022',
  90.  
  91. // checks if you are at the correct lva page
  92. lvaCheckEnabled: true,
  93.  
  94. // only if the number is right, the script is enabled [String]
  95. lvaNumber: "185.A91",
  96.  
  97. // if you have multiple study codes, enter here the study code number you want
  98. // to register for eg. '123456' (no blanks). Otherwise leave empty. [String]
  99. studyCode: '',
  100.  
  101. // autoGoToLVA: true, // coming soon
  102.  
  103. // checks if you are at the correct semester
  104. lvaSemesterCheckEnabled: true,
  105.  
  106. // only if the semester is right, the script is enabled [String]
  107. lvaSemester: "2022S",
  108.  
  109. // autoGoToSemester: true, // coming soon
  110.  
  111. // automatically opens the detail panel of a group [true,false]
  112. openPanel: true,
  113.  
  114. // automatically presses the register button if it is available [true,false]
  115. autoRegister: true,
  116.  
  117. // automatically presses the confirm button for your registration [true,false]
  118. autoConfirm: true,
  119.  
  120. // continuously refresh the page until the script can register you [true,false]
  121. autoRefresh: true,
  122.  
  123. // automatically presses the ok button on the confirmation info page [true,false]
  124. autoOkPressAtEnd: true,
  125.  
  126. // a delay on the confirm info page, until the ok button gets pressed
  127. // this is useful if you want to continuously cycle through the registration process
  128. // until you are registered and with this parameter you can define a "cycle delay" at the end.
  129. // This could happen, if (for some reason) you are not on the whitelist for this course.
  130. // [Integer]
  131. okPressAtEndDelayInMs: 1000,
  132.  
  133. // let the script start at a specific time [true,false]
  134. startAtSpecificTime: true,
  135.  
  136. // define the specific time the script should start [Date]
  137. // new Date(year, month, day, hours, minutes, seconds, milliseconds)
  138. // note: months start with 0
  139. specificStartTime: new Date(2022, 2 - 1, 25, 20, 0, 0, 0),
  140.  
  141. // if a specific time is defined, the script will refresh some ms sooner to adjust a delay [Integer]
  142. delayAdjustmentInMs: 100,
  143.  
  144. // show log output of the script on screen [true,false]
  145. showLog: true
  146. };
  147.  
  148. //
  149. // End of configuration
  150. ///////////////////////////////////////////////////////////////////////
  151.  
  152.  
  153. self.init = function () {
  154. self.extendJQuery();
  155. self.tissQuickRegistration();
  156. };
  157.  
  158. self.extendJQuery = function () {
  159. jQuery.fn.justtext = function () {
  160. return $(this).clone()
  161. .children()
  162. .remove()
  163. .end()
  164. .text().trim();
  165. };
  166. };
  167.  
  168. self.tissQuickRegistration = function () {
  169. if (options.scriptEnabled) {
  170. self.pageLog("TISS Quick Registration Script enabled");
  171. self.pageLog("LVA Number: " + self.getLVANumber());
  172. self.pageLog("LVA Name: " + self.getLVAName());
  173. self.pageLog("Selected Tab: " + self.getSelectedTab());
  174.  
  175. if (options.registrationType === "lva") {
  176. options.nameOfGroup = "LVA-Anmeldung";
  177. }
  178.  
  179. // test if the lva and group exists
  180. if (!options.lvaCheckEnabled || self.doLvaCheck()) {
  181. if (!options.lvaSemesterCheckEnabled || self.doSemesterCheck()) {
  182. if (options.registrationType !== "exam") { // own code
  183. var groupLabel = self.doGroupCheck();
  184. if (groupLabel !== null) {
  185. self.highlight(groupLabel);
  186. }
  187. } else {
  188. var examLabel = self.doExamCheck();
  189. if (examLabel !== null) {
  190. self.highlight(examLabel);
  191. self.pageLog("Prüfung: " + examLabel.text().trim());
  192. }
  193. }
  194. }
  195. }
  196.  
  197. if (options.startAtSpecificTime) {
  198. self.pageLog("Scripts starts at: " + self.getFormatedDate(options.specificStartTime));
  199. self.pageLog("Delay adjustment in ms: " + options.delayAdjustmentInMs);
  200. self.startTimer(options.specificStartTime.getTime() - options.delayAdjustmentInMs);
  201. } else {
  202. self.analysePage();
  203. }
  204.  
  205. } else {
  206. self.pageLog("TISS Quick Registration Script disabled");
  207. }
  208. };
  209.  
  210. self.startTimer = function (startTime) {
  211. var offset = startTime - new Date().getTime();
  212. if (offset > 0) {
  213. self.startRefreshTimer(startTime);
  214. } else {
  215. self.analysePage();
  216. }
  217. };
  218.  
  219. self.startRefreshTimer = function (startTime) {
  220. self.printTimeToStart(startTime);
  221.  
  222. var maxMillis = 2147483647;
  223. var offset = startTime - new Date().getTime();
  224.  
  225. // prevent an overflow
  226. if (offset > maxMillis) {
  227. offset = maxMillis;
  228. }
  229.  
  230. window.setTimeout(self.refreshPage, offset);
  231. };
  232.  
  233. self.printTimeToStart = function (startTime) {
  234. var offset = (startTime - new Date().getTime()) / 1000;
  235. var out = "Refresh in: ";
  236. var minutes = offset / 60;
  237. if (minutes > 1) {
  238. var hours = minutes / 60;
  239. if (hours > 1) {
  240. out += Math.floor(hours) + " hours, "
  241. minutes = minutes % 60;
  242. }
  243. out += Math.floor(minutes) + " minutes and ";
  244. }
  245. var seconds = offset % 60;
  246. out += Math.floor(seconds) + " seconds";
  247. self.log(out);
  248.  
  249. self.pageCountdown(out);
  250.  
  251. window.setTimeout(function () {
  252. self.printTimeToStart(startTime);
  253. }, 1000);
  254. };
  255.  
  256. self.refreshPage = function () {
  257. location.reload();
  258. };
  259.  
  260. self.analysePage = function () {
  261.  
  262. var tab = self.getSelectedTab();
  263. var confirmButton = self.getConfirmButton();
  264. var okButton = self.getOkButton();
  265. var studyCodeSelect = self.getStudyCodeSelect();
  266.  
  267. self.log("tab: " + tab);
  268. self.log("confirmButton: " + confirmButton);
  269. self.log("okButton: " + okButton);
  270.  
  271. if (tab === "LVA-Anmeldung") {
  272. self.onLVAPage();
  273. } else if (tab === "Gruppen") {
  274. self.onGroupPage();
  275. } else if (tab === "Prüfungen") {
  276. self.onExamPage();
  277. } else if (studyCodeSelect.length > 0) {
  278. self.onStudyCodeSelectPage();
  279. } else if (confirmButton.length > 0) {
  280. self.onConfirmPage();
  281. } else if (okButton.length > 0) {
  282. self.onConfirmInfoPage();
  283. }
  284. };
  285.  
  286. self.getLVANumber = function () {
  287. return $('#contentInner').find('h1 span:first').text().trim();
  288. };
  289.  
  290. self.getLVAName = function () {
  291. return $('#contentInner').find('h1').justtext();
  292. };
  293.  
  294. self.getSemester = function () {
  295. return $('#contentInner').find('h1 select').val();
  296. };
  297.  
  298. self.getSelectedTab = function () {
  299. return $('li.ui-tabs-selected').text().trim();
  300. };
  301.  
  302. self.getSubHeader = function () {
  303. return $('#contentInner').find('#subHeader').text().trim();
  304. };
  305.  
  306. self.onLVAPage = function () {
  307. self.onGroupPage();
  308. };
  309.  
  310. self.onGroupPage = function () {
  311. if (options.lvaCheckEnabled && !self.doLvaCheck()) {
  312. return;
  313. }
  314.  
  315. if (options.lvaSemesterCheckEnabled && !self.doSemesterCheck()) {
  316. return;
  317. }
  318.  
  319. var groupLabel = self.doGroupCheck();
  320. if (groupLabel === null) {
  321. return;
  322. }
  323. self.highlight(groupLabel);
  324.  
  325. var groupWrapper = groupLabel.closest('.groupWrapper');
  326.  
  327. // open the panel if the option is activated
  328. if (options.openPanel) {
  329. groupWrapper.children().show();
  330. // for some reason, we have to wait some time here and try it again :/
  331. setTimeout(function () {
  332. groupWrapper.children().show();
  333. }, 100);
  334. }
  335.  
  336. // search for the registration button
  337. var regButton = self.getRegistrationButton(groupWrapper);
  338. self.log('regButton: ' + regButton);
  339.  
  340.  
  341. // push the button
  342. if (regButton.length > 0) {
  343.  
  344. self.highlight(regButton);
  345. regButton.focus();
  346.  
  347. if (options.autoRegister) {
  348. regButton.click();
  349. }
  350. } else {
  351. if (self.getGroupCancelButton(groupWrapper).length > 0) {
  352. self.pageOut('you are registered in group: ' + options.nameOfGroup);
  353. } else {
  354. // Only refresh the page if the option is set and if the registration is not yet completed.
  355. if (options.autoRefresh) {
  356. refreshPage();
  357. }
  358. self.pageOut('no registration button found');
  359. }
  360. }
  361. };
  362.  
  363. self.onExamPage = function () {
  364. if (options.lvaCheckEnabled && !self.doLvaCheck()) {
  365. return;
  366. }
  367.  
  368. if (options.lvaSemesterCheckEnabled && !self.doSemesterCheck()) {
  369. return;
  370. }
  371.  
  372. var examLabel = self.doExamCheck();
  373. if (examLabel === null) {
  374. return;
  375. }
  376. self.highlight(examLabel);
  377.  
  378. var examWrapper = examLabel.closest('.groupWrapper');
  379.  
  380. // open the panel if the option is activated
  381. if (options.openPanel) {
  382. examWrapper.children().show();
  383. // for some reason, we have to wait some time here and try it again :/
  384. setTimeout(function () {
  385. examWrapper.children().show();
  386. }, 100);
  387. }
  388.  
  389. // search for the registration button
  390. var regButton = self.getRegistrationButton(examWrapper);
  391. self.log('regButton: ' + regButton);
  392.  
  393.  
  394. // push the button
  395. if (regButton.length > 0) {
  396.  
  397. self.highlight(regButton);
  398. regButton.focus();
  399.  
  400. if (options.autoRegister) {
  401. regButton.click();
  402. }
  403. } else {
  404. if (self.getGroupCancelButton(examWrapper).length > 0) {
  405. self.pageOut('you are registered in exam: ' + options.nameOfExam);
  406. } else {
  407. // Only refresh the page if the option is set and if the registration is not yet completed.
  408. if (options.autoRefresh) {
  409. refreshPage();
  410. }
  411. self.pageOut('no registration button found');
  412. }
  413. }
  414. };
  415.  
  416. self.onStudyCodeSelectPage = function () {
  417. var studyCodeSelect = self.getStudyCodeSelect();
  418. var confirmButton = self.getConfirmButton();
  419. self.highlight(confirmButton);
  420. if (options.studyCode !== undefined && options.studyCode.length > 0) {
  421. self.setSelectValue(studyCodeSelect, options.studyCode);
  422. }
  423. confirmButton.focus();
  424. if (options.autoConfirm) {
  425. confirmButton.click();
  426. }
  427. };
  428.  
  429. self.onConfirmPage = function () {
  430. var confirmButton = self.getConfirmButton();
  431. self.highlight(confirmButton);
  432. confirmButton.focus();
  433. if (options.autoConfirm) {
  434. confirmButton.click();
  435. }
  436. };
  437.  
  438. self.onConfirmInfoPage = function () {
  439. var okButton = self.getOkButton();
  440. self.highlight(okButton);
  441. if (options.autoOkPressAtEnd) {
  442. setTimeout(function () {
  443. var okButton = self.getOkButton();
  444. okButton.click();
  445. }, options.okPressAtEndDelayInMs);
  446. }
  447. };
  448.  
  449. self.pageOut = function (text) {
  450. var out = self.getOutputField();
  451. out.text(text);
  452. };
  453.  
  454. self.pageCountdown = function (text) {
  455. var out = self.getCountdownField();
  456. out.text(text);
  457. };
  458.  
  459. self.pageLog = function (text) {
  460. self.appendToLogField(text);
  461. };
  462.  
  463. self.getOutputField = function () {
  464. var outputField = $('#TQRScriptOutput');
  465. if (outputField.length === 0) {
  466. self.injectOutputField();
  467. outputField = self.getOutputField();
  468. }
  469. return outputField;
  470. };
  471.  
  472. self.getCountdownField = function () {
  473. var countdownField = $('#TQRScriptCountdown');
  474. if (countdownField.length === 0) {
  475. self.injectCountdownField();
  476. countdownField = self.getCountdownField();
  477. }
  478. return countdownField;
  479. };
  480.  
  481. self.getLogField = function () {
  482. var logField = $('#TQRScriptLog');
  483. if (logField.length === 0) {
  484. self.injectLogField();
  485. logField = self.getLogField();
  486. if (options.showLog) {
  487. logField.show();
  488. } else {
  489. logField.hide();
  490. }
  491. }
  492. return logField;
  493. };
  494.  
  495. self.injectOutputField = function () {
  496. var el = $('#contentInner');
  497. var log = $('#TQRScriptLog');
  498. if (log.length) {
  499. el = log;
  500. }
  501. el.before('<div id="TQRScriptOutput" style="color: red; font-weight: bold; font-size: 14pt; padding: 8px 0px;"></div>');
  502. };
  503.  
  504. self.injectCountdownField = function () {
  505. var el = $('#contentInner');
  506. var log = $('#TQRScriptLog');
  507. if (log.length) {
  508. el = log;
  509. }
  510. el.before('<div id="TQRScriptCountdown" style="color: blue; font-weight: bold; font-size: 14pt; padding: 8px 0px;"></div>');
  511. };
  512.  
  513. self.injectLogField = function () {
  514. $('#contentInner').before('<div id="TQRScriptLog" style="color: black; background-color: #FFFCD9; font-size: 10pt;"><b>Information Log:</b></div>');
  515. };
  516.  
  517. self.appendToLogField = function (text) {
  518. var log = self.getLogField();
  519. var newText = log.html() + "<br />" + text;
  520. log.html(newText);
  521. };
  522.  
  523. self.getRegistrationButton = function (groupWrapper) {
  524. var regButton;
  525. if (options.registrationType === "group"
  526. || options.registrationType === "exam"
  527. || options.registrationType === "lva") {
  528. regButton = $(groupWrapper).find("input:submit[value='Anmelden']");
  529. if (regButton.length === 0) {
  530. regButton = $(groupWrapper).find("input:submit[value='Voranmelden']");
  531. if (regButton.length === 0) {
  532. regButton = $(groupWrapper).find("input:submit[value='Voranmeldung']");
  533. }
  534. }
  535. } else {
  536. self.pageLog("registrationType Error: unknown type '" + options.registrationType + "'");
  537. }
  538. return regButton;
  539. };
  540.  
  541. self.getGroupCancelButton = function (groupWrapper) {
  542. var unregButton = null;
  543. if (options.registrationType === "group") {
  544. unregButton = $(groupWrapper).find("input:submit[value='Abmelden']");
  545. } else if (options.registrationType === "exam") {
  546. unregButton = $(groupWrapper).find("input:submit[value='Abmelden']");
  547. } else if (options.registrationType === "lva") {
  548. unregButton = $(groupWrapper).find("input:submit[value='Abmelden']").filter(function (index) {
  549. return $(this).attr("id") !== 'registrationForm:confirmOkBtn';
  550. });
  551. } else {
  552. self.pageLog("registrationType Error: unknown type '" + options.registrationType + "'");
  553. }
  554. return unregButton;
  555. };
  556.  
  557. self.getConfirmButton = function () {
  558. var confirmButton = $("form#regForm input:submit[value='Anmelden']");
  559. if (confirmButton.length === 0) {
  560. confirmButton = $("form#regForm input:submit[value='Voranmelden']");
  561. if (confirmButton.length === 0) {
  562. confirmButton = $("form#regForm input:submit[value='Voranmeldung']");
  563. }
  564. }
  565. return confirmButton;
  566. };
  567.  
  568. self.getOkButton = function () {
  569. return $("form#confirmForm input:submit[value='Ok']");
  570. };
  571.  
  572. self.getStudyCodeSelect = function () {
  573. return $("#regForm").find("select");
  574. };
  575.  
  576. self.getGroupLabel = function (nameOfGroup) {
  577. // Normalize group lables and configured group label before comparing.
  578. var normConfName = nameOfGroup.trim().replace(/\s\s+/gi, ' ');
  579.  
  580. return $(".groupWrapper .header_element span").filter(function () {
  581. var normName = $(this).text().trim().replace(/\s\s+/gi, ' ');
  582.  
  583. return normName === normConfName;
  584. });
  585. };
  586.  
  587. self.getExamLabel = function (nameOfExam) {
  588. return $(".groupWrapper .header_element span").filter(function () {
  589. var tmp = $(this).text().trim();
  590. return tmp.match(nameOfExam);
  591. });
  592. };
  593.  
  594. self.getExamDate = function (nameOfExam, dateOfExam) {
  595. return $(".groupWrapper .header_element").filter(function () {
  596. var examData = $(this).text().trim();
  597. var examLabel = self.getExamLabel(nameOfExam).first().text().trim() + " ";
  598. var examDate = examData.replace(examLabel, '');
  599. return examDate.match(dateOfExam);
  600. });
  601. };
  602.  
  603. self.highlight = function (object) {
  604. object.css("background-color", "lightgreen");
  605. };
  606.  
  607. self.isCorrectSemester = function () {
  608. return self.getSubHeader().contains(options.lvaSemester);
  609. };
  610.  
  611. self.setSelectValue = function ($element, value) {
  612. $element.find('option').removeAttr('selected');
  613. $element.find('option[value="' + value + '"]').attr('selected', 'selected');
  614. };
  615.  
  616. self.doGroupCheck = function () {
  617. var groupLabel = self.getGroupLabel(options.nameOfGroup);
  618. if (groupLabel.length === 0) {
  619. self.pageOut('group not found error: ' + options.nameOfGroup);
  620. return null;
  621. } else {
  622. return groupLabel;
  623. }
  624. };
  625.  
  626. self.doLvaCheck = function () {
  627. var lvaNumber = self.getLVANumber();
  628. lvaNumber = lvaNumber.replace(/[^\d]/, '');
  629. var optionsLvaNumber = options.lvaNumber.replace(/[^\d]/, '');
  630. if (lvaNumber !== optionsLvaNumber) {
  631. self.pageOut('wrong lva number error: expected: ' + optionsLvaNumber + ', got: ' + lvaNumber);
  632. return false;
  633. }
  634. return true;
  635. };
  636.  
  637. self.doSemesterCheck = function () {
  638. var subheader = self.getSubHeader();
  639. if (subheader.indexOf(options.lvaSemester) === -1) {
  640. self.pageOut('wrong semester error: expected: ' + options.lvaSemester + ', got: ' + subheader.substring(0, 5));
  641. return false;
  642. }
  643. return true;
  644. };
  645.  
  646. self.doExamCheck = function () {
  647. var examLabel = self.getExamLabel(options.nameOfExam);
  648. var examData = self.getExamDate(options.nameOfExam, options.dateOfExam);
  649. if (examLabel.length === 0) {
  650. self.pageOut('exam not found error: ' + options.nameOfExam);
  651. return null;
  652. } else if (examData.length === 0) {
  653. self.pageOut('Date not found: ' + options.dateOfExam);
  654. return null;
  655. } else {
  656. return examData;
  657. }
  658. };
  659.  
  660. self.getFormatedDate = function (date) {
  661. return "" + date.getDate() + "." + (date.getMonth() + 1) + "." + date.getFullYear() + " " + date.getHours() + ":" + date.getMinutes() + ":" + date.getSeconds() + ":" + date.getMilliseconds();
  662. };
  663.  
  664. self.log = function (message) {
  665. console.log(message);
  666. };
  667.  
  668.  
  669. // Initialize the script
  670. self.init();
  671. })();
  672.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement