· 6 years ago · Apr 13, 2020, 12:30 AM
1// ==UserScript==
2// @name DH2
3// @namespace redderke-dh2-utils
4// @version 0.1.0
5// @description Redderke's script for diamond hunt 2.
6// @author Redderke
7// @include *dh2.diamondhunt.co/*
8// @match https://dh2.diamondhunt.co/
9// @grant none
10// @require http://code.jquery.com/jquery-3.4.1.min.js
11// @require https://code.jquery.com/ui/1.12.1/jquery-ui.min.js
12// @require https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.24.0/moment.min.js
13// @require https://cdn.jsdelivr.net/npm/chart.js@2.9.3/dist/Chart.min.js
14// @require https://cdnjs.cloudflare.com/ajax/libs/wnumb/1.2.0/wNumb.min.js
15// @require https://cdnjs.cloudflare.com/ajax/libs/noUiSlider/14.0.3/nouislider.js
16// @require https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.12/js/select2.min.js
17// @require https://cdn.jsdelivr.net/npm/choices.js@9.0.1/public/assets/scripts/choices.min.js
18// @run-at document-idle
19// ==/UserScript==
20/**
21 *
22 * This script has 2 goals:
23 * The first goal is to create a base functionality that eases script development.
24 * The second goal is to use this base and add improvements/functionality.
25 *
26 * Other scripts can use this by adding themselves as a module.
27
28 * Still very WIP
29 *
30 */
31
32// (function () {
33// 'use strict';
34
35//always included
36/* Begin: src\js\base\main.js */
37/**
38 * DataAccess.
39 * @namespace
40 */
41var Main = {
42 _toLoadModules: [],
43 _AllModules: {},
44 _initializing: false,
45 _loadingModules: [],
46 loaded: false,
47 init: function () {
48 this._initializing = true;
49 this._loadModules();
50 this._initializing = false;
51 console.log('Main initialized')
52 },
53
54
55 /**
56 * Add a module to the script.
57 * Load order of the modules is decided using dependencies
58 * @param {Object} module - The module object
59 * @param {string} module.namespace - Namespace of the module
60 * @param {function} module.init - Function that runs to initialize the script
61 */
62 addModule: function (module) {
63 if (typeof module.namespace == 'undefined' || typeof module.init == 'undefined') {
64 if (module.namespace == 'undefined') {
65 console.error('A module is missing the namespace field')
66 } else {
67 console.error(module.namespace + 'is missing a required field')
68 }
69 } else {
70 this._AllModules[module.namespace] = {
71 module: module,
72 loaded: false,
73 loading: false,
74 };
75 if (this.loaded || this._initializing) {
76 this._loadModule(module);
77 } else {
78 this._toLoadModules.push(module);
79 }
80 }
81 },
82
83 /**
84 * Load all modules.
85 * @private
86 */
87 _loadModules: function () {
88 for (const key in this._AllModules) {
89 if (this._AllModules.hasOwnProperty(key)) {
90 // const moduleItem = this._AllModules[key];
91 this._loadModule(key)
92 // moduleItem.module.init();
93 }
94 }
95 this.loaded = true;
96 },
97
98 /**
99 * @private
100 * @param {string} namespace - The namespace of the module.
101 */
102 _loadModule: function (namespace) {
103 try {
104 let moduleItem = this._AllModules[namespace];
105
106 if (!moduleItem.loaded && !moduleItem.loading) {
107 moduleItem.loading = true;
108 moduleItem.module.init();
109 moduleItem.loading = false;
110 moduleItem.loaded = true;
111 console.log(namespace + ' initialized')
112 }
113 } catch (error) {
114 console.error('Error during initializing of ' + module.namespace + ':', error);
115 throw error;
116 }
117 return true;
118 },
119
120 /**
121 * Checks if module is loading
122 * @param {string} namespace - Namespace of the module.
123 */
124 isModuleLoading: function (namespace) {
125 try {
126 return this._AllModules[namespace].loading;
127 } catch (error) {
128 console.error('Module ' + module.namespace + ' not found: ', error);
129 }
130 },
131
132 /**
133 * Checks if module is loaded
134 * @param {string} namespace - Namespace of the module.
135 */
136 isModuleLoaded: function (namespace) {
137 try {
138 return this._AllModules[namespace].loaded;
139 } catch (error) {
140 console.error('Module ' + module.namespace + ' not found: ', error);
141 }
142 },
143
144 /**
145 * Returns module by namespace.
146 * @param {string} namespace - Namespace of the module.
147 * @return {object} The requested module.
148 */
149 getModule: function (namespace) {
150 try {
151 let moduleItem = this._AllModules[namespace];
152 if (!moduleItem.loaded && !moduleItem.loading) {
153 this._loadModule(namespace)
154 }
155 return moduleItem.module;
156 } catch (error) {
157 console.error('Module ' + module.namespace + ' not found: ', error);
158 }
159 },
160
161}
162
163
164/* End: src\js\base\main.js */
165/* Begin: src\js\base\constants.js */
166/**
167 * Constants.
168 * @namespace
169 */
170var Constants = {
171 namespace: 'Constants',
172 init: function () {},
173
174 /**TODO: documentation */
175 defaultValues: {
176 // settings / things that can be changed
177 // (value if not in localStorage)
178 chartColors: ['#1f78b4', '#33a02c', '#e31a1c', '#ff7f00', '#6a3d9a', '#b15928', '#a6cee3', '#b2df8a', '#fb9a99', '#fdbf6f', '#cab2d6', '#ffff99'],
179 marketHistoryUrl: 'http://data.diamondhunt.co/fetcher.php?key=d6Hd6HQa378G32dfgT',
180 marketAveragesUrl: 'https://dh2fixed-market-backend.herokuapp.com/data',
181 corsApiUrl: 'https://cors-anywhere.herokuapp.com/',
182 MHVHeight: window.innerHeight / 2,
183 MHVWidth: window.innerWidth / 2,
184 MHVHistoryData: {}
185 },
186
187 _postItemDialogueOriginal: window.postItemDialogue,
188
189 // elements:
190 /**TODO: documentation */
191 marketTableOriginal: document.querySelector('#market-table'),
192
193 /**TODO: documentation */
194 IdOptionsDiv: 'redderke-options-table',
195
196 /**TODO: documentation */
197 marketList: ["ancientLogs",
198 "ancientTreeSeeds",
199 "arrows",
200 "ashes",
201 "batSkin",
202 "bearFur",
203 "blewitMushroom",
204 "blewitMushroomSeeds",
205 "blueAxeOrb",
206 "blueChiselOrb",
207 "blueFishingRodOrb",
208 "blueHammerOrb",
209 "blueMeditationOrb",
210 "blueOilPipeOrb",
211 "bluePartyHat",
212 "bluePickaxeOrb",
213 "blueRakeOrb",
214 "blueShovelOrb",
215 "blueTrowelOrb",
216 "boneAmulet",
217 "bones",
218 "bow",
219 "bronzeBar",
220 "cannon",
221 "cannonBarrel",
222 "cannonStand",
223 "cannonWheels",
224 "carrotSeeds",
225 "changePromethiumMouldScroll",
226 "changeRuniteMouldScroll",
227 "charcoal",
228 "crystalLeaf",
229 "crystalLeafSeeds",
230 "darkBones",
231 "defender",
232 "diamond",
233 "donorCoins",
234 "dottedGreenLeaf",
235 "dottedGreenLeafSeeds",
236 "dragonSkeletonShield",
237 "emerald",
238 "emptyChisel",
239 "emptyOrbSpellScroll",
240 "enchantStargemPotionSpellScroll",
241 "feather",
242 "fireArrows",
243 "fireFeather",
244 "fishingBait",
245 "ghostAmulet",
246 "ghostScanSpellScroll",
247 "glass",
248 "goldBar",
249 "goldBody",
250 "goldBoots",
251 "goldGloves",
252 "goldHelmet",
253 "goldLeaf",
254 "goldLeafSeeds",
255 "goldLegs",
256 "greenBonemealBinOrb",
257 "greenBowOrb",
258 "greenBrewingKitOrb",
259 "greenCannonOrb",
260 "greenCombatOrb",
261 "greenEmpoweredRockOrb",
262 "greenLeaf",
263 "greenLeafSeeds",
264 "greenOilFactoryOrb",
265 "greenOilStorageOrb",
266 "greenPartyHat",
267 "greenRocketOrb",
268 "iceArrows",
269 "iceBones",
270 "iceFeather",
271 "interstellarGas",
272 "ironBar",
273 "ironDagger",
274 "lifeAmuletCharged",
275 "limeLeaf",
276 "limeLeafSeeds",
277 "logs",
278 "mapleLogs",
279 "mapleTreeSeeds",
280 "marble",
281 "marsRock",
282 "moonBones",
283 "moonstone",
284 "oakLogs",
285 "oakTreeSeeds",
286 "pinkPartyHat",
287 "potatoSeeds",
288 "promethium",
289 "promethiumBar",
290 "promethiumBodyMould",
291 "promethiumBootsMould",
292 "promethiumGlovesMould",
293 "promethiumHelmetMould",
294 "promethiumLegsMould",
295 "quartz",
296 "rapier",
297 "rawSardine",
298 "rawShark",
299 "rawShrimp",
300 "rawSwordfish",
301 "rawTuna",
302 "rawWhale",
303 "redAxeOrb",
304 "redCharcoalFactoryOrb",
305 "redCombatLootOrb",
306 "redFishingBaitOrb",
307 "redMagicWandOrb",
308 "redManaStarOrb",
309 "redMushroom",
310 "redMushroomSeeds",
311 "redPartyHat",
312 "redRocketOrb",
313 "ruby",
314 "runite",
315 "runiteBar",
316 "runiteBodyMould",
317 "runiteBootsMould",
318 "runiteGlovesMould",
319 "runiteHelmetMould",
320 "runiteLegsMould",
321 "sand",
322 "santaHatSigil",
323 "sapphire",
324 "sardine",
325 "scythe",
326 "shark",
327 "shrimp",
328 "silverBar",
329 "skeletonShield",
330 "skeletonSword",
331 "snakeSkin",
332 "snapegrass",
333 "snapegrassSeeds",
334 "snowmanSigil",
335 "stardust",
336 "stardustLogs",
337 "stardustTreeSeeds",
338 "stinger",
339 "stone",
340 "strangeBlueLeaf",
341 "strangeGreenLeaf",
342 "strangeLeafTreeSeeds",
343 "strangeLogs",
344 "strangePinkLeaf",
345 "strangePurpleLeaf",
346 "strangeYellowLeaf",
347 "stripedCrystalLeaf",
348 "stripedCrystalLeafSeeds",
349 "swordfish",
350 "thread",
351 "tomatoSeeds",
352 "treeSeeds",
353 "treeSigil",
354 "tuna",
355 "veryHighWindSpellScroll",
356 "whale",
357 "wheat",
358 "wheatSeeds",
359 "whitePartyHat",
360 "willowLogs",
361 "willowTreeSeeds",
362 "yellowPartyHat"],
363
364 // stringId: [id, name, imageurl]
365 // id is -1 if unknown
366 /**TODO: documentation */
367 itemList: {
368 "ALL": [
369 -1,
370 "All Items",
371 "images/icons/infinity.png"
372 ],
373 "sand": [
374 180,
375 "Sand",
376 "images/sand.png"
377 ],
378 "stone": [
379 -1,
380 "Stone",
381 "images/stone.png"
382 ],
383 "quartz": [
384 414,
385 "Quartz",
386 "images/quartz.png"
387 ],
388 "marble": [
389 49,
390 "Marble",
391 "images/marble.png"
392 ],
393 "promethium": [
394 50,
395 "Promethium",
396 "images/promethium.png"
397 ],
398 "runite": [
399 51,
400 "Runite",
401 "images/runite.png"
402 ],
403 "sapphire": [
404 52,
405 "Sapphire",
406 "images/sapphire.png"
407 ],
408 "emerald": [
409 53,
410 "Emerald",
411 "images/emerald.png"
412 ],
413 "ruby": [
414 54,
415 "Ruby",
416 "images/ruby.png"
417 ],
418 "diamond": [
419 55,
420 "Diamond",
421 "images/diamond.png"
422 ],
423 "charcoal": [
424 673,
425 "Charcoal",
426 "images/charcoal.png"
427 ],
428 "moonstone": [
429 692,
430 "Moonstone",
431 "images/moonstone.png"
432 ],
433 "marsRock": [
434 1137,
435 "Mars Rock",
436 "images/marsRock.png"
437 ],
438 "interstellarGas": [
439 1492,
440 "Interstellar Gas",
441 "images/interstellarGas.png"
442 ],
443 "emptyChisel": [
444 560,
445 "Empty Chisel",
446 "images/emptyChisel.png"
447 ],
448 "glass": [
449 181,
450 "Glass",
451 "images/glass.png"
452 ],
453 "bronzeBar": [
454 65,
455 "Bronze Bar",
456 "images/bronzeBar.png"
457 ],
458 "ironBar": [
459 66,
460 "Iron Bar",
461 "images/ironBar.png"
462 ],
463 "silverBar": [
464 67,
465 "Silver Bar",
466 "images/silverBar.png"
467 ],
468 "goldBar": [
469 68,
470 "Gold Bar",
471 "images/goldBar.png"
472 ],
473 "promethiumBar": [
474 69,
475 "Promethium Bar",
476 "images/promethiumBar.png"
477 ],
478 "runiteBar": [
479 71,
480 "Runite Bar",
481 "images/runiteBar.png"
482 ],
483 "dottedGreenLeafSeeds": [
484 211,
485 "Dotted Green Leaf Seeds",
486 "images/dottedGreenLeafSeeds.png"
487 ],
488 "greenLeafSeeds": [
489 212,
490 "Green Leaf Seeds",
491 "images/greenLeafSeeds.png"
492 ],
493 "limeLeafSeeds": [
494 215,
495 "Lime Leaf Seeds",
496 "images/limeLeafSeeds.png"
497 ],
498 "goldLeafSeeds": [
499 217,
500 "Gold Leaf Seeds",
501 "images/goldLeafSeeds.png"
502 ],
503 "crystalLeafSeeds": [
504 223,
505 "Crystal Leaf Seeds",
506 "images/crystalLeafSeeds.png"
507 ],
508 "stripedCrystalLeafSeeds": [
509 224,
510 "Striped Crystal Leaf Seeds",
511 "images/stripedCrystalLeafSeeds.png"
512 ],
513 "redMushroomSeeds": [
514 186,
515 "Red Mushroom Seeds",
516 "images/redMushroomSeeds.png"
517 ],
518 "blewitMushroomSeeds": [
519 226,
520 "Blewit Mushroom Seeds",
521 "images/blewitMushroomSeeds.png"
522 ],
523 "snapegrassSeeds": [
524 228,
525 "Snapegrass Seeds",
526 "images/snapegrassSeeds.png"
527 ],
528 "treeSeeds": [
529 242,
530 "Tree Seeds",
531 "images/treeSeeds.png"
532 ],
533 "oakTreeSeeds": [
534 252,
535 "Oak Tree Seeds",
536 "images/oakTreeSeeds.png"
537 ],
538 "willowTreeSeeds": [
539 336,
540 "Willow Tree Seeds",
541 "images/willowTreeSeeds.png"
542 ],
543 "mapleTreeSeeds": [
544 462,
545 "Maple Tree Seeds",
546 "images/mapleTreeSeeds.png"
547 ],
548 "stardustTreeSeeds": [
549 607,
550 "Stardust Tree Seeds",
551 "images/stardustTreeSeeds.png"
552 ],
553 "strangeLeafTreeSeeds": [
554 1269,
555 "Strange Leaf Tree Seeds",
556 "images/strangeLeafTreeSeeds.png"
557 ],
558 "ancientTreeSeeds": [
559 1191,
560 "Ancient Tree Seeds",
561 "images/ancientTreeSeeds.png"
562 ],
563 "wheatSeeds": [
564 219,
565 "Wheat Seeds",
566 "images/wheatSeeds.png"
567 ],
568 "carrotSeeds": [
569 724,
570 "Carrot Seeds",
571 "images/carrotSeeds.png"
572 ],
573 "tomatoSeeds": [
574 723,
575 "Tomato Seeds",
576 "images/tomatoSeeds.png"
577 ],
578 "potatoSeeds": [
579 722,
580 "Potato Seeds",
581 "images/potatoSeeds.png"
582 ],
583 "logs": [
584 122,
585 "Logs",
586 "images/logs.png"
587 ],
588 "oakLogs": [
589 123,
590 "Oak Logs",
591 "images/oakLogs.png"
592 ],
593 "willowLogs": [
594 124,
595 "Willow Logs",
596 "images/willowLogs.png"
597 ],
598 "mapleLogs": [
599 125,
600 "Maple Logs",
601 "images/mapleLogs.png"
602 ],
603 "stardustLogs": [
604 606,
605 "Stardust Logs",
606 "images/stardustLogs.png"
607 ],
608 "strangeLogs": [
609 1270,
610 "Strange Logs",
611 "images/strangeLogs.png"
612 ],
613 "ancientLogs": [
614 126,
615 "Ancient Logs",
616 "images/ancientLogs.png"
617 ],
618 "dottedGreenLeaf": [
619 188,
620 "Dotted Green Leaf",
621 "images/dottedGreenLeaf.png"
622 ],
623 "greenLeaf": [
624 189,
625 "Green Leaf",
626 "images/greenLeaf.png"
627 ],
628 "limeLeaf": [
629 194,
630 "Lime Leaf",
631 "images/limeLeaf.png"
632 ],
633 "goldLeaf": [
634 190,
635 "Gold Leaf",
636 "images/goldLeaf.png"
637 ],
638 "crystalLeaf": [
639 191,
640 "Crystal Leaf",
641 "images/crystalLeaf.png"
642 ],
643 "stripedCrystalLeaf": [
644 192,
645 "Striped Crystal Leaf",
646 "images/stripedCrystalLeaf.png"
647 ],
648 "redMushroom": [
649 185,
650 "Red Mushroom",
651 "images/redMushroom.png"
652 ],
653 "blewitMushroom": [
654 244,
655 "Blewit Mushroom",
656 "images/blewitMushroom.png"
657 ],
658 "snapegrass": [
659 245,
660 "Snapegrass",
661 "images/snapegrass.png"
662 ],
663 "fishingBait": [
664 367,
665 "Fishing Bait",
666 "images/fishingBait.png"
667 ],
668 "rawShrimp": [
669 306,
670 "Raw Shrimp",
671 "images/rawShrimp.png"
672 ],
673 "rawSardine": [
674 307,
675 "Raw Sardine",
676 "images/rawSardine.png"
677 ],
678 "rawTuna": [
679 308,
680 "Raw Tuna",
681 "images/rawTuna.png"
682 ],
683 "rawSwordfish": [
684 309,
685 "Raw Swordfish",
686 "images/rawSwordfish.png"
687 ],
688 "rawShark": [
689 310,
690 "Raw Shark",
691 "images/rawShark.png"
692 ],
693 "rawWhale": [
694 311,
695 "Raw Whale",
696 "images/rawWhale.png"
697 ],
698 "wheat": [
699 395,
700 "Wheat",
701 "images/wheat.png"
702 ],
703 "shrimp": [
704 313,
705 "Shrimp",
706 "images/shrimp.png"
707 ],
708 "sardine": [
709 314,
710 "Sardine",
711 "images/sardine.png"
712 ],
713 "tuna": [
714 315,
715 "Tuna",
716 "images/tuna.png"
717 ],
718 "swordfish": [
719 316,
720 "Swordfish",
721 "images/swordfish.png"
722 ],
723 "shark": [
724 317,
725 "Shark",
726 "images/shark.png"
727 ],
728 "whale": [
729 318,
730 "Whale",
731 "images/whale.png"
732 ],
733 "snakeSkin": [
734 449,
735 "Snake Skin",
736 "images/snakeSkin.png"
737 ],
738 "batSkin": [
739 474,
740 "Bat Skin",
741 "images/batSkin.png"
742 ],
743 "bearFur": [
744 514,
745 "Bear Fur",
746 "images/bearFur.png"
747 ],
748 "stinger": [
749 443,
750 "Stinger",
751 "images/stinger.png"
752 ],
753 "ironDagger": [
754 450,
755 "Iron Dagger",
756 "images/ironDagger.png"
757 ],
758 "skeletonSword": [
759 477,
760 "Skeleton Sword",
761 "images/skeletonSword.png"
762 ],
763 "bow": [
764 640,
765 "Bow",
766 "images/bow.png"
767 ],
768 "arrows": [
769 641,
770 "Arrows",
771 "images/arrows.png"
772 ],
773 "fireArrows": [
774 642,
775 "Fire Arrows",
776 "images/fireArrows.png"
777 ],
778 "iceArrows": [
779 -1,
780 "Ice Arrows",
781 "images/iceArrows.png"
782 ],
783 "feather": [
784 280,
785 "Feather",
786 "images/feather.png"
787 ],
788 "fireFeather": [
789 591,
790 "Fire Feather",
791 "images/fireFeather.png"
792 ],
793 "iceFeather": [
794 645,
795 "Ice Feather",
796 "images/iceFeather.png"
797 ],
798 "scythe": [
799 715,
800 "Scythe",
801 "images/scythe.png"
802 ],
803 "rapier": [
804 1895,
805 "Rapier",
806 "images/rapier.png"
807 ],
808 "cannonBarrel": [
809 1164,
810 "Cannon Barrel",
811 "images/cannonBarrel.png"
812 ],
813 "cannonStand": [
814 1165,
815 "Cannon Stand",
816 "images/cannonStand.png"
817 ],
818 "cannonWheels": [
819 1166,
820 "Cannon Wheels",
821 "images/cannonWheels.png"
822 ],
823 "cannon": [
824 1160,
825 "Cannon",
826 "images/cannon.png"
827 ],
828 "skeletonShield": [
829 476,
830 "Skeleton Shield",
831 "images/skeletonShield.png"
832 ],
833 "dragonSkeletonShield": [
834 1995,
835 "Dragon Skeleton Shield",
836 "images/dragonSkeletonShield.png"
837 ],
838 "defender": [
839 1589,
840 "Defender",
841 "images/defender.png"
842 ],
843 "boneAmulet": [
844 475,
845 "Bone Amulet",
846 "images/boneAmulet.png"
847 ],
848 "ghostAmulet": [
849 714,
850 "Ghost Amulet",
851 "images/ghostAmulet.png"
852 ],
853 "lifeAmuletCharged": [
854 1591,
855 "Life Amulet Charged",
856 "images/lifeAmuletCharged.png"
857 ],
858 "goldHelmet": [
859 499,
860 "Gold Helmet",
861 "images/goldHelmet.png"
862 ],
863 "goldBody": [
864 -1,
865 "Gold Body",
866 "images/goldBody.png"
867 ],
868 "goldLegs": [
869 -1,
870 "Gold Legs",
871 "images/goldLegs.png"
872 ],
873 "goldBoots": [
874 502,
875 "Gold Boots",
876 "images/goldBoots.png"
877 ],
878 "goldGloves": [
879 503,
880 "Gold Gloves",
881 "images/goldGloves.png"
882 ],
883 "promethiumHelmetMould": [
884 1023,
885 "Promethium Helmet Mould",
886 "images/promethiumHelmetMould.png"
887 ],
888 "promethiumBodyMould": [
889 1024,
890 "Promethium Body Mould",
891 "images/promethiumBodyMould.png"
892 ],
893 "promethiumLegsMould": [
894 -1,
895 "Promethium Legs Mould",
896 "images/promethiumLegsMould.png"
897 ],
898 "promethiumBootsMould": [
899 -1,
900 "Promethium Boots Mould",
901 "images/promethiumBootsMould.png"
902 ],
903 "promethiumGlovesMould": [
904 1027,
905 "Promethium Gloves Mould",
906 "images/promethiumGlovesMould.png"
907 ],
908 "runiteHelmetMould": [
909 1414,
910 "Runite Helmet Mould",
911 "images/runiteHelmetMould.png"
912 ],
913 "runiteBodyMould": [
914 1415,
915 "Runite Body Mould",
916 "images/runiteBodyMould.png"
917 ],
918 "runiteLegsMould": [
919 1416,
920 "Runite Legs Mould",
921 "images/runiteLegsMould.png"
922 ],
923 "runiteBootsMould": [
924 1417,
925 "Runite Boots Mould",
926 "images/runiteBootsMould.png"
927 ],
928 "runiteGlovesMould": [
929 1418,
930 "Runite Gloves Mould",
931 "images/runiteGlovesMould.png"
932 ],
933 "thread": [
934 1320,
935 "Thread",
936 "images/thread.png"
937 ],
938 "strangeGreenLeaf": [
939 1274,
940 "Strange Green Leaf",
941 "images/strangeGreenLeaf.png"
942 ],
943 "strangeYellowLeaf": [
944 1273,
945 "Strange Yellow Leaf",
946 "images/strangeYellowLeaf.png"
947 ],
948 "strangeBlueLeaf": [
949 1271,
950 "Strange Blue Leaf",
951 "images/strangeBlueLeaf.png"
952 ],
953 "strangePinkLeaf": [
954 1272,
955 "Strange Pink Leaf",
956 "images/strangePinkLeaf.png"
957 ],
958 "strangePurpleLeaf": [
959 1275,
960 "Strange Purple Leaf",
961 "images/strangePurpleLeaf.png"
962 ],
963 "enchantStargemPotionSpellScroll": [
964 1344,
965 "Enchant Stargem Potion Spell Scroll",
966 "images/enchantStargemPotionSpellScroll.png"
967 ],
968 "veryHighWindSpellScroll": [
969 1346,
970 "Very High Wind Spell Scroll",
971 "images/veryHighWindSpellScroll.png"
972 ],
973 "emptyOrbSpellScroll": [
974 1345,
975 "Empty Orb Spell Scroll",
976 "images/emptyOrbSpellScroll.png"
977 ],
978 "ghostScanSpellScroll": [
979 1422,
980 "Ghost Scan Spell Scroll",
981 "images/ghostScanSpellScroll.png"
982 ],
983 "changePromethiumMouldScroll": [
984 1487,
985 "Change Promethium Mould Scroll",
986 "images/changePromethiumMouldScroll.png"
987 ],
988 "changeRuniteMouldScroll": [
989 1488,
990 "Change Runite Mould Scroll",
991 "images/changeRuniteMouldScroll.png"
992 ],
993 "blueFishingRodOrb": [
994 547,
995 "Blue Fishing Rod Orb",
996 "images/blueFishingRodOrb.png"
997 ],
998 "blueShovelOrb": [
999 548,
1000 "Blue Shovel Orb",
1001 "images/blueShovelOrb.png"
1002 ],
1003 "blueAxeOrb": [
1004 549,
1005 "Blue Axe Orb",
1006 "images/blueAxeOrb.png"
1007 ],
1008 "blueRakeOrb": [
1009 550,
1010 "Blue Rake Orb",
1011 "images/blueRakeOrb.png"
1012 ],
1013 "blueTrowelOrb": [
1014 740,
1015 "Blue Trowel Orb",
1016 "images/blueTrowelOrb.png"
1017 ],
1018 "bluePickaxeOrb": [
1019 551,
1020 "Blue Pickaxe Orb",
1021 "images/bluePickaxeOrb.png"
1022 ],
1023 "blueOilPipeOrb": [
1024 552,
1025 "Blue Oil Pipe Orb",
1026 "images/blueOilPipeOrb.png"
1027 ],
1028 "blueHammerOrb": [
1029 553,
1030 "Blue Hammer Orb",
1031 "images/blueHammerOrb.png"
1032 ],
1033 "blueChiselOrb": [
1034 554,
1035 "Blue Chisel Orb",
1036 "images/blueChiselOrb.png"
1037 ],
1038 "blueMeditationOrb": [
1039 1388,
1040 "Blue Meditation Orb",
1041 "images/blueMeditationOrb.png"
1042 ],
1043 "greenOilFactoryOrb": [
1044 1123,
1045 "Green Oil Factory Orb",
1046 "images/greenOilFactoryOrb.png"
1047 ],
1048 "greenCombatOrb": [
1049 1124,
1050 "Green Combat Orb",
1051 "images/greenCombatOrb.png"
1052 ],
1053 "greenBrewingKitOrb": [
1054 1125,
1055 "Green Brewing Kit Orb",
1056 "images/greenBrewingKitOrb.png"
1057 ],
1058 "greenBowOrb": [
1059 1126,
1060 "Green Bow Orb",
1061 "images/greenBowOrb.png"
1062 ],
1063 "greenCannonOrb": [
1064 1878,
1065 "Green Cannon Orb",
1066 "images/greenCannonOrb.png"
1067 ],
1068 "greenBonemealBinOrb": [
1069 1127,
1070 "Green Bonemeal Bin Orb",
1071 "images/greenBonemealBinOrb.png"
1072 ],
1073 "greenRocketOrb": [
1074 1128,
1075 "Green Rocket Orb",
1076 "images/greenRocketOrb.png"
1077 ],
1078 "greenOilStorageOrb": [
1079 1139,
1080 "Green Oil Storage Orb",
1081 "images/greenOilStorageOrb.png"
1082 ],
1083 "greenEmpoweredRockOrb": [
1084 1390,
1085 "Green Empowered Rock Orb",
1086 "images/greenEmpoweredRockOrb.png"
1087 ],
1088 "redMagicWandOrb": [
1089 1451,
1090 "Red Magic Wand Orb",
1091 "images/redMagicWandOrb.png"
1092 ],
1093 "redCharcoalFactoryOrb": [
1094 1452,
1095 "Red Charcoal Factory Orb",
1096 "images/redCharcoalFactoryOrb.png"
1097 ],
1098 "redManaStarOrb": [
1099 1453,
1100 "Red Mana Star Orb",
1101 "images/redManaStarOrb.png"
1102 ],
1103 "redCombatLootOrb": [
1104 1454,
1105 "Red Combat Loot Orb",
1106 "images/redCombatLootOrb.png"
1107 ],
1108 "redAxeOrb": [
1109 1455,
1110 "Red Axe Orb",
1111 "images/redAxeOrb.png"
1112 ],
1113 "redFishingBaitOrb": [
1114 1456,
1115 "Red Fishing Bait Orb",
1116 "images/redFishingBaitOrb.png"
1117 ],
1118 "redRocketOrb": [
1119 1457,
1120 "Red Rocket Orb",
1121 "images/redRocketOrb.png"
1122 ],
1123 "bones": [
1124 282,
1125 "Bones",
1126 "images/bones.png"
1127 ],
1128 "ashes": [
1129 593,
1130 "Ashes",
1131 "images/ashes.png"
1132 ],
1133 "iceBones": [
1134 644,
1135 "Ice Bones",
1136 "images/iceBones.png"
1137 ],
1138 "moonBones": [
1139 -1,
1140 "Moon Bones",
1141 "images/moonBones.png"
1142 ],
1143 "darkBones": [
1144 1167,
1145 "Dark Bones",
1146 "images/darkBones.png"
1147 ],
1148 "stardust": [
1149 138,
1150 "Stardust",
1151 "images/stardust.png"
1152 ],
1153 "donorCoins": [
1154 398,
1155 "Donor Coins",
1156 "images/donorCoins.png"
1157 ],
1158 "santaHatSigil": [
1159 -1,
1160 "Santa Hat Sigil",
1161 "images/santaHatSigil.png"
1162 ],
1163 "treeSigil": [
1164 410,
1165 "Tree Sigil",
1166 "images/treeSigil.png"
1167 ],
1168 "greenPartyHat": [
1169 -1,
1170 "Green Party Hat",
1171 "images/greenPartyHat.png"
1172 ],
1173 "pinkPartyHat": [
1174 -1,
1175 "Pink Party Hat",
1176 "images/pinkPartyHat.png"
1177 ],
1178 "redPartyHat": [
1179 -1,
1180 "Red Party Hat",
1181 "images/redPartyHat.png"
1182 ],
1183 "yellowPartyHat": [
1184 -1,
1185 "Yellow Party Hat",
1186 "images/yellowPartyHat.png"
1187 ],
1188 "whitePartyHat": [
1189 1264,
1190 "White Party Hat",
1191 "images/whitePartyHat.png"
1192 ],
1193 "bluePartyHat": [
1194 -1,
1195 "Blue Party Hat",
1196 "images/bluePartyHat.png"
1197 ],
1198 "snowmanSigil": [
1199 1928,
1200 "Snowman Sigil",
1201 "images/snowmanSigil.png"
1202 ]
1203 }
1204
1205}
1206Main.addModule(Constants);/* End: src\js\base\constants.js */
1207/* Begin: src\js\base\generalFunctions.js */
1208/**
1209 * General functions.
1210 * @namespace
1211 */
1212var GeneralFunctions = {
1213 namespace: 'GeneralFunctions',
1214 _CTE: null,
1215 _DA: null,
1216 _SETTINGS: null,
1217
1218 _runOnMarketUpdateList: [],
1219 //TODO: Move to Main
1220 _runOnFirstTickList: [],
1221 _initialPlaytime: playtime,
1222 //TODO: Move to Main
1223 _ranFirstTick: false,
1224
1225 /**
1226 * TODO: documentation
1227 */
1228 init: function () {
1229 this._CTE = Main.getModule('Constants');
1230 this._DA = Main.getModule('DataAccess');
1231 this._SETTINGS = Main.getModule('Settings');
1232
1233 this._waitForFirstTick();
1234
1235 // TODO: convert oncommand
1236 // replace original function that updates the market with new one
1237 let previousFunc = window.postItemDialogue;
1238 window.postItemDialogue = function (buySellElement, strId, inputElement, options) {
1239 Main.getModule('GeneralFunctions')._runOnMarketUpdateList.forEach(item => {
1240 if (item[0].includes(strId)) {
1241 item[1](buySellElement, strId, inputElement, options);
1242 }
1243 });
1244 previousFunc(buySellElement, strId, inputElement);
1245 }
1246 this._initSettings();
1247
1248 this.runOnFirstTick(() => {
1249 this._checkNotificationPermission();
1250 });
1251 },
1252
1253 _initSettings: function () {
1254 this._SETTINGS.addCategory('generalFunctions', 'General')
1255
1256 this._SETTINGS.addSetting({
1257 category: 'generalFunctions',
1258 key: 'generalFunctions-allow-push-notifications',
1259 type: 'checkbox',
1260 text: 'Enable push notifications',
1261 defaultValue: false,
1262 onChange: function () { Main.getModule('GeneralFunctions')._checkNotificationPermission() },
1263 })
1264 },
1265
1266 /**TODO: documentation */
1267 addCss: function (fileName) {
1268
1269 var head = document.head;
1270 var link = document.createElement("link");
1271
1272 link.type = "text/css";
1273 link.rel = "stylesheet";
1274 link.href = fileName;
1275
1276 head.appendChild(link);
1277 },
1278
1279 /**TODO: documentation */
1280 setMarketPage: function (strId, options) {
1281 console.log('changed to ' + strId)
1282 var buySellElement = document.getElementById("dialogue-market-postitem-buyorsell")
1283 buySellElement.setAttribute('value', 'buy')
1284 var inputElement = document.querySelector('#dialogue-market-items-area input[data-item-name="' + this._CTE.itemList[strId][1] + '"]')
1285 window.postItemDialogue(buySellElement, strId, inputElement, options)
1286 },
1287
1288 /**TODO: documentation */
1289 runOnMarketUpdate: function (func, items = ['ALL']) {
1290 this._runOnMarketUpdateList.push([func, items]);
1291 },
1292
1293 //TODO: Move to Main
1294 /**TODO: documentation */
1295 runOnFirstTick: function (func) {
1296 if (this._ranFirstTick) {
1297 func();
1298 } else {
1299 this._runOnFirstTickList.push(func);
1300 }
1301 },
1302
1303 //TODO: Move to Main
1304 _waitForFirstTick: function () {
1305 if (this._initialPlaytime == playtime) {
1306 setTimeout(function () { Main.getModule('GeneralFunctions')._waitForFirstTick() }, 1000);
1307 } else if (!this._ranFirstTick) {
1308 this._ranFirstTick = true;
1309 this._runOnFirstTickList.forEach(func => {
1310 func();
1311 });
1312 }
1313 },
1314
1315 // TODO leave here or move to market history viewer?
1316 /**TODO: documentation */
1317 updateHistoryData: function (onload) {
1318 if (!this.history_data_downloading) {
1319 this.history_data_downloading = true;
1320 this._history_data = {};
1321 this._DA.getApi(this._DA.get('marketHistoryUrl'), x => {
1322 this._DA._history_data_raw = JSON.parse(x.responseText);
1323 this._DA.history_data_downloading = false;
1324 onload();
1325 });
1326 }
1327 },
1328
1329 /**TODO: documentation */
1330 deepCopy: function (object) {
1331 return jQuery.extend(true, {}, object)
1332 },
1333
1334 /**TODO: documentation */
1335 shallowCopy: function (object) {
1336 return jQuery.extend({}, object)
1337 },
1338
1339 //TODO: make prepend and append options?
1340 /**
1341 * Add functionality to a function. (use window as container to extend global functions)
1342 * @param {object} container - TODO: documentation
1343 * @param {string} funcName - TODO: documentation
1344 * @param {function} append - TODO: documentation
1345 * @param {function} prepend - TODO: documentation
1346 */
1347 extendFunction: function (container, funcName, append, prepend) {
1348 (function () {
1349 let proxied = container[funcName];
1350 container[funcName] = function () {
1351 if (prepend) prepend.apply(this, arguments);
1352 let result = proxied.apply(this, arguments);
1353 if (append) append.apply(this, arguments);
1354 return result;
1355 };
1356 })();
1357 },
1358
1359 /**
1360 * Send a desktop notification.
1361 * @param {string} title - Title of the notification.
1362 * @param {string} body - Body of the notification.
1363 * @param {string} [iconUrl=''] - Url to icon image of notification.
1364 * @return {boolean} - True if successful.
1365 */
1366 sendNotification: function (title, body, iconUrl = '') {
1367 if (!window.Notification) {
1368 console.info('Browser does not support notifications.');
1369 return false;
1370 } else {
1371 if (Notification.permission === 'granted' && Main.getModule('DataAccess').get('generalFunctions-allow-push-notifications')) {
1372 new Notification(title, {
1373 body: body,
1374 icon: iconUrl,
1375 });
1376 }
1377 }
1378 },
1379
1380 _checkNotificationPermission: function () {
1381 if (Notification.permission !== 'granted' && this._DA.get('generalFunctions-allow-push-notifications')) {
1382 Notification.requestPermission().then(function (p) {
1383 if (p === 'granted') {
1384 console.info('Notifications allowed')
1385 } else {
1386 console.info('Notifications blocked')
1387 }
1388 }).catch(function (err) {
1389 console.error(err);
1390 });
1391 }
1392 }
1393}
1394Main.addModule(GeneralFunctions);
1395/* End: src\js\base\generalFunctions.js */
1396/* Begin: src\js\base\dataAccess.js */
1397/**
1398 * DataAccess.
1399 * @namespace
1400 */
1401var DataAccess = {
1402 namespace: 'DataAccess',
1403 _CTE: null,
1404 _GEN: null,
1405
1406 _history_data_raw: null,
1407 _history_data: {},
1408 /** True if the history data is being downloaded */
1409 history_data_downloading: false,
1410 _localStoragePrefix: 'redderke.',
1411
1412 /**
1413 * TODO: documentation
1414 */
1415 init: function () {
1416 this._CTE = Main.getModule('Constants');
1417 this._GEN = Main.getModule('GeneralFunctions');
1418
1419 let clearList = this.get('DataAccessClearList')
1420 if (clearList != null) {
1421 clearList.forEach((x) => {
1422 this.clear(x)
1423 })
1424 }
1425 this.clear('DataAccessClearList')
1426 },
1427
1428 /**
1429 * Store data at the key.
1430 * @param {string} key - The key where data is stored.
1431 * @param {any} value - The value stored at the key.
1432 */
1433 set: function (key, value) {
1434 localStorage.setItem(this._localStoragePrefix + key, JSON.stringify(value));
1435 },
1436
1437 /**
1438 * Retrieve data stored at the key.
1439 * @param {string} key - The key where data is stored.
1440 * @return {any} - The data stored at the key.
1441 */
1442 get: function (key) {
1443 let localData = JSON.parse(localStorage.getItem(this._localStoragePrefix + key));
1444 if (localData !== null) {
1445 return localData
1446 } else {
1447 return typeof this._CTE.defaultValues[key] === 'undefined' ? null : this._CTE.defaultValues[key]
1448 }
1449 },
1450
1451 /**
1452 * Clear the data stored at the key.
1453 * @param {string} key - The key.
1454 */
1455 clear: function (key) {
1456 localStorage.removeItem(key);
1457 },
1458
1459 /**
1460 * Clear all stored data.
1461 */
1462 clearAll: function () {
1463 for (var key in localStorage) {
1464 if (key.startsWith(this._localStoragePrefix)) {
1465 localStorage.removeItem(key);
1466 }
1467 }
1468 },
1469
1470 /**
1471 * Will remove data stored at the key on next load of the page.
1472 * @param {string} key - The key.
1473 */
1474 clearOnReload: function (key) {
1475 let clearList = this.get('DataAccessClearList')
1476 clearList.push(key);
1477 this.set('DataAccessClearList', clearList)
1478 },
1479
1480 /**
1481 * Returns the string id linked to the number id.
1482 * @param {int} id - The id in number format.
1483 * @return {string} - The id in string format.
1484 */
1485 getStrIdById: function (id) {
1486 for (const key in this._CTE.itemList) {
1487 if (this._CTE.itemList.hasOwnProperty(key) && this._CTE.itemList[key][0] == id) {
1488 return key;
1489 }
1490 }
1491 },
1492
1493 /**
1494 * Returns the string id that uses this name.
1495 * @param {string} name - The name.
1496 * @return {string} - The id in string format.
1497 */
1498 getStrIdByName: function (name) {
1499 for (const key in this._CTE.itemList) {
1500 if (this._CTE.itemList.hasOwnProperty(key) && this._CTE.itemList[key][1] == name) {
1501 return key;
1502 }
1503 }
1504 },
1505
1506 /**
1507 * TODO make async
1508 * Executes a api get call and executes the function with the XMLHttpRequest object as parameter.
1509 * @param {string} url - The url.
1510 * @param {function} onload - The function to run on load.
1511 * @return {XMLHttpRequest} - The XMLHttpRequest object.
1512 */
1513 getApi: function (url, onload) {
1514 if (!url.startsWith('https')) {
1515 url = this.get('corsApiUrl') + url;
1516 }
1517
1518 var xhr = new XMLHttpRequest();
1519 xhr.addEventListener("progress", () => console.log('progress'), false);
1520 xhr.addEventListener("load", () => onload(xhr), false);
1521 //xhr.addEventListener("error", transferFailed, false);
1522 //xhr.addEventListener("abort", transferCanceled, false);
1523 xhr.open('GET', url);
1524 xhr.send();
1525 return xhr;
1526 },
1527
1528 // TODO: This function depends on a module
1529 // solution: move the local option to main
1530 // TODO: make the pai url configurable
1531 // TODO: use onload exclusively for consistency
1532 /**
1533 * Gets the history data from localStorage or api depending on key:MHVLocal
1534 * @param {string} url - The url.
1535 * @param {function} onload - The function to run on load.
1536 * @return {unknown} - TODO: documentation.
1537 */
1538 getHistory: function (strId, onload = (() => { })) {
1539 if (this.get('MHVLocal')) {
1540 return this.get('MHVHistoryData')[strId];
1541 } else {
1542 if (typeof this._history_data[strId] === 'undefined') {
1543 if (this._history_data_raw === null) {
1544 this._GEN.updateHistoryData(() => {
1545 this._generateHistoryCache(strId);
1546 onload()
1547 });
1548 } else {
1549 this._generateHistoryCache(strId);
1550 }
1551 }
1552 }
1553 return this._history_data[strId];
1554 },
1555
1556 // TODO: remove?
1557 // TODO: use onload exclusively for consistency
1558 /**
1559 * Gets the latest known prices from history data.
1560 * @param {function} onload - The function to run on load.
1561 * @return {XMLHttpRequest} - The XMLHttpRequest object.
1562 */
1563 getLastPrices: function (onload = (() => { })) {
1564 let lastPriceList = {}
1565 this.getHistory(this._CTE.marketList[0], onload)
1566
1567 this._CTE.marketList.forEach(strId => {
1568 const priceList = this.getHistory(strId);
1569 if (typeof priceList !== 'undefined') {
1570 lastPriceList[strId] = priceList[priceList.length - 1]
1571 }
1572 });
1573 return lastPriceList;
1574 },
1575
1576 /**
1577 * TODO: documentation
1578 * @private
1579 */
1580 _generateHistoryCache: function (strId) {
1581 this._history_data[strId] = [];
1582 for (var key in this._history_data_raw) {
1583 if (this._history_data_raw.hasOwnProperty(key)) {
1584 var entry = this._history_data_raw[key];
1585 if (typeof entry !== 'undefined' && entry[0] == strId) {
1586 this._history_data[strId].push(
1587 [
1588 entry[1],
1589 entry[2],
1590 Date.parse(entry[3] + ' CST')
1591 ]
1592 );
1593 }
1594 }
1595 }
1596 },
1597}
1598Main.addModule(DataAccess);/* End: src\js\base\dataAccess.js */
1599/* Begin: src\js\base\settings.js */
1600/**
1601 * Settings.
1602 * @namespace
1603 */
1604var Settings = {
1605 namespace: 'Settings',
1606 _CTE: null,
1607 _DA: null,
1608
1609 inputClass: 'redderke-settings-input',
1610 KeyAttributeName: 'redderke-key',
1611 _defaultOptions: {
1612 type: 'checkbox',
1613 category: '',
1614 key: '',
1615 text: '',
1616 defaultValue: '',
1617 buttonText: 'show',
1618 onChange: ()=>{},
1619 imageUrl: '',
1620 style: '',
1621 },
1622
1623 /**
1624 * TODO: documentation
1625 * @private
1626 */
1627 init: function () {
1628 this._CTE = Main.getModule('Constants');
1629 this._DA = Main.getModule('DataAccess');
1630
1631 this._initUI();
1632 },
1633
1634 /**
1635 * TODO: documentation
1636 * @private
1637 */
1638 _initUI: function () {
1639 var optionsHtml = '<div id="' + this._CTE.IdOptionsDiv + '"><table class="table-style1" width="40%"><tr style="background-color:gold;"><th>Redderke\'s script settings</th><th style="width:1px"></th></tr></table></div>'
1640 var profileTable = document.getElementById('profile-toggleTable')
1641 profileTable.insertAdjacentHTML('afterend', optionsHtml)
1642 profileTable.insertAdjacentHTML('afterend', '<br>')
1643 profileTable.insertAdjacentHTML('afterend', '<br>')
1644 },
1645
1646 /**
1647 * TODO: documentation
1648 * @private
1649 */
1650 _getSettingElement: function(){
1651 //TODO
1652 },
1653
1654 /**TODO: documentation */
1655 addEventListener: function(){
1656 //TODO
1657 },
1658
1659 /**
1660 * TODO: documentation
1661 * @private
1662 */
1663 _getCategoryTable: function(category){
1664 return document.querySelector('#' + this._CTE.IdOptionsDiv + ' table[category="' + category + '"]');
1665 },
1666
1667 /**
1668 * TODO: documentation
1669 */
1670 addSelectorSetting: function(options){
1671 return document.querySelector('#' + this._CTE.IdOptionsDiv + ' table[category="' + category + '"]');
1672 },
1673
1674 /**
1675 * Add a setting to the settings table.
1676 * @param {Object} options
1677 * @param {string} [options.type='checkbox'] - See HTML input types. (TODO: type file not done yet)
1678 * @param {string} options.category - Category to put the setting under.
1679 * @param {string} options.key - The key where the value is stored.
1680 * @param {string} [options.text=''] - The description.
1681 * @param {string} [options.defaultValue=''] - Default value.
1682 * @param {string} [options.buttonText='show'] - Text inside button.
1683 * @param {string} [options.onChange=()=>{}] - Function to call on change of the value or button click.
1684 * @param {string} [options.imageUrl=''] - Url to image for input element.
1685 * @param {string} [options.style=''] - Custom style for the row element.
1686 */
1687 addSetting: function (options) {
1688 let opts = Object.assign({}, this._defaultOptions, options);
1689
1690 let rowElement = this._generateRowElement(options);
1691 let inputElement = rowElement.querySelector('input')
1692
1693 if (this._DA.get(opts.key) === null) {
1694 this._DA.set(opts.key, opts.defaultValue);
1695 }
1696 switch (opts.type) {
1697 case 'checkbox': case 'radio': {
1698 inputElement.checked = this._DA.get(opts.key)
1699 inputElement.addEventListener('change', event => {
1700 this._DA.set(opts.key, inputElement.checked);
1701 opts.onChange(event);
1702 console.log('changed')
1703 })
1704 break;
1705 }
1706 case 'button': case 'reset': case 'submit': case 'image': {
1707 inputElement.value = opts.buttonText;
1708 inputElement.addEventListener('click', event => {
1709 opts.onChange(event);
1710 })
1711 break;
1712 }
1713 case 'date': case 'datetime-local': case 'month': case 'time': case 'week': {
1714 let value = this._DA.get(opts.key);
1715 if (moment(value, moment.ISO_8601, true).isValid()) {
1716 inputElement.valueAsDate = new Date(value)
1717 }
1718 inputElement.addEventListener('change', event => {
1719 this._DA.set(opts.key, inputElement.valueAsDate);
1720 opts.onChange(event);
1721 })
1722 break;
1723 }
1724 case 'range': {
1725 //TODO
1726 // range: default range from 0 to 100, returns correctly
1727 // might need extra options to control range or show current value
1728 inputElement.value = this._DA.get(opts.key)
1729 inputElement.addEventListener('change', event => {
1730 this._DA.set(opts.key, inputElement.value);
1731 opts.onChange(event);
1732 })
1733 console.log('TODO: improve range input type')
1734 break;
1735 }
1736 case 'number': case 'password': case 'hidden': case 'email': case 'color': case 'search': case 'tel': case 'text': case 'url': {
1737 inputElement.style.width = '40px'
1738 inputElement.value = this._DA.get(opts.key)
1739 inputElement.addEventListener('change', event => {
1740 this._DA.set(opts.key, inputElement.value);
1741 opts.onChange(event);
1742 })
1743 break;
1744 }
1745 default: {
1746 // TODO: file
1747 console.log('setting type not (yet) supported!')
1748 }
1749 break;
1750 }
1751 },
1752
1753 /**TODO: documentation */
1754 addCategory: function (category, text) {
1755 var optionsDiv = document.getElementById(this._CTE.IdOptionsDiv)
1756 if (optionsDiv.querySelector('table[category="' + category + '"]') === null) {
1757 optionsDiv.insertAdjacentHTML('beforeend', '<table class="table-style1" width="40%" category="' + category + '"><tr style="background-color:grey;"><th>' + text + '</th><th style="width:1px"></th></tr></table>')
1758 }
1759 },
1760
1761 /**
1762 * TODO: documentation
1763 * @private
1764 */
1765 _generateRowElement: function(options){
1766 let opts = Object.assign({}, this._defaultOptions, options);
1767
1768 var optionsTable = this._getCategoryTable(opts.category)
1769 if (optionsTable === null) {
1770 this.addCategory(category, category)
1771 optionsTable = this._getCategoryTable(opts.category)
1772 }
1773
1774 var textColElement = document.createElement('td')
1775 textColElement.innerText = opts.text
1776
1777 var inputElement = document.createElement('input')
1778 inputElement.setAttribute('type', opts.type)
1779 inputElement.setAttribute('class', this.inputClass)
1780 inputElement.setAttribute(this.KeyAttributeName, opts.key)
1781 inputElement.setAttribute('src', opts.imageUrl)
1782
1783 var inputColElement = document.createElement('td')
1784 inputColElement.insertAdjacentElement('beforeend', inputElement)
1785
1786 var rowElement = document.createElement('tr')
1787 rowElement.insertAdjacentElement('beforeend', textColElement)
1788 rowElement.insertAdjacentElement('beforeend', inputColElement)
1789 inputElement.setAttribute('style', opts.style)
1790 // add it to the table
1791 optionsTable.insertAdjacentElement('beforeend', rowElement)
1792 return rowElement;
1793 },
1794
1795}
1796Main.addModule(Settings);/* End: src\js\base\settings.js */
1797/* Begin: src\js\base\automation.js */
1798// todo
1799// e.g. auto update market data
1800
1801/**
1802 * Automation.
1803 * @namespace
1804 */
1805var Automation = {
1806 namespace: 'Automation',
1807 _DA: null,
1808 _GEN: null,
1809 _SETTINGS: null,
1810
1811 _initialPlaytime: playtime,
1812 _onCommandFunctions: {},
1813
1814 /**
1815 * TODO: documentation
1816 */
1817 init: function () {
1818 this._DA = Main.getModule('DataAccess');
1819 this._GEN = Main.getModule('GeneralFunctions');
1820 this._SETTINGS = Main.getModule('Settings');
1821
1822 this._initSettings();
1823 this._initCommandProcessing()
1824 },
1825
1826 /**
1827 * TODO: documentation
1828 * @private
1829 */
1830 _initSettings: function () {
1831 this._SETTINGS.addCategory('Automation', 'Automation')
1832 },
1833
1834 /**
1835 * Run a function periodically and control it's execution and delay.
1836 * The delay is in milliseconds.
1837 * @param {string} delayKey - The DataAccess key that contains delay amount.
1838 * @param {string} enableKey - The DataAccess key that contains a true/false to control execution.
1839 * @param {function} func - The function to run periodically.
1840 * @param {int} [delayMultiplier=1] - The delay stored at the key location will be multiplied by this value.
1841 */
1842 automateControlled: function (delayKey, enableKey, func, delayMultiplier = 1) {
1843 if (this._DA.get(enableKey)) {
1844 func();
1845 }
1846 setTimeout(function () {
1847 this._automateControlled(delayKey, enableKey, func, delayMultiplier)
1848 }, this._DA.get(delayKey) * delayMultiplier);
1849
1850 },
1851
1852 /**
1853 * Run a function periodically.
1854 * @param {int} delay - Delay between executions.
1855 * @param {function} func - The function to run periodically.
1856 */
1857 automate: function (delay, func) {
1858 func();
1859 setTimeout(function () {
1860 this._automate(delay, func)
1861 }, delay);
1862
1863 },
1864
1865 /**
1866 * Run function when a specific command is called.
1867 * @param {function} func - A function with as parameters: (string, array) where the string is the command and the array is all command parameters.
1868 * @param {string} [command=''] - The command (empty string (default) is any command)
1869 */
1870 onCommand: function (func, command = '') {
1871 if (typeof this._onCommandFunctions[command] == 'undefined') this._onCommandFunctions[command] = [];
1872 this._onCommandFunctions[command].push(func);
1873 },
1874
1875 /**
1876 * TODO: documentation
1877 * @private
1878 */
1879 _initCommandProcessing: function () {
1880 this._GEN.extendFunction(window, 'doCommand', this._processCommand);
1881 },
1882
1883 /**
1884 * TODO: documentation
1885 * @private
1886 */
1887 _processCommand: function (cmdStr) {
1888 var cmd = cmdStr.split("=")[0];
1889 var params = cmdStr.slice(cmd.length + 1).split('~')
1890 // TODO save price history
1891 // TODO: save chat history (and split level into own)
1892 switch (cmd) {
1893 case '':
1894
1895 break;
1896
1897 default:
1898 break;
1899 }
1900 let funcList = Main.getModule('Automation')._onCommandFunctions;
1901 if (typeof funcList[cmd] != 'undefined') {
1902 funcList[cmd].forEach(func => {
1903 func(cmd, params);
1904 })
1905 }
1906 if (typeof funcList[''] != 'undefined') {
1907 funcList[''].forEach(func => {
1908 func(cmd, params);
1909 })
1910 }
1911 },
1912
1913}
1914Main.addModule(Automation);/* End: src\js\base\automation.js */
1915/* Begin: src\js\base\dialog.js */
1916/**
1917 * Description.
1918 * @namespace
1919 */
1920var Dialog = {
1921 namespace: 'Dialog', // the name used by other modules
1922 // add/remove as needed
1923 _CTE: null,
1924 _DA: null,
1925 _GEN: null,
1926 _AUTO: null,
1927 _SETTINGS: null,
1928 _prefixId: 'redderke-dialog-',
1929 _prefixLocal: 'redderke-dialog-',
1930 _titles: {},
1931
1932 /**
1933 * Initialize Dialog
1934 */
1935 init: function () {
1936 this._CTE = Main.getModule('Constants');
1937 this._DA = Main.getModule('DataAccess');
1938 this._GEN = Main.getModule('GeneralFunctions');
1939 this._AUTO = Main.getModule('Automation');
1940 this._SETTINGS = Main.getModule('Settings');
1941 },
1942
1943 /**
1944 * TODO Documentation
1945 * html is string
1946 */
1947 create: function (id, title, html) {
1948 this.setTitle(id, title)
1949
1950 let dialog = document.createElement('dialog');
1951 dialog.id = this._prefixId + id;
1952 dialog.insertAdjacentHTML('beforeend', '<div style="margin-right: 20px;"></div>');
1953 document.body.appendChild(dialog);
1954
1955 this.setHTML(id, html);
1956
1957 new ResizeObserver(() => {
1958 dialog.getAttribute('style').replace(/\s/g, '')
1959 .split(';').forEach(x => {
1960 var t = x.split(':');
1961 if (t[0] == 'width' && t[1] != 'auto') {
1962 this._DA.set(this._prefixLocal + '.' + id + '.width', parseFloat(t[1].replace('px', '')) + 60);
1963 }
1964 if (t[0] == 'height' && t[1] != 'auto') {
1965 this._DA.set(this._prefixLocal + '.' + id + '.height', parseFloat(t[1].replace('px', '')) + 60);
1966 }
1967 });
1968 }).observe(dialog);
1969 },
1970
1971 /**
1972 * TODO Documentation
1973 * html is string
1974 */
1975 setHTML: function (id, html) {
1976 document.querySelector('#' +this._prefixId + id + ' div').innerHTML = html;
1977 },
1978
1979 /**
1980 * TODO Documentation
1981 */
1982 setTitle: function (id, title) {
1983 this._titles[id] = title;
1984 },
1985
1986 /**
1987 * TODO Documentation
1988 */
1989 open: function (id) {
1990 let height = this._DA.get(this._prefixLocal + '.' + id + '.height')
1991 let width = this._DA.get(this._prefixLocal + '.' + id + '.width')
1992 $('#' + this._prefixId + id).dialog({
1993 title: this._titles[id],
1994 height: height,
1995 width: width,
1996 });
1997 },
1998
1999 /**
2000 * TODO Documentation
2001 * @return The body div of the dialog
2002 */
2003 getElement: function (id) {
2004 return document.querySelector('#' +this._prefixId + id + ' div');
2005 },
2006
2007
2008
2009}
2010Main.addModule(Dialog);/* End: src\js\base\dialog.js */
2011/* Begin: src\js\base\dev.js */
2012/**
2013 * Functions that are helpful when developing.
2014 * @namespace
2015 */
2016var Dev = {
2017 namespace: 'Dev',
2018 init: function () {
2019 window.redDev = this;
2020 },
2021
2022 /**
2023 * Add functionality to a function. (use window as container to extend global functions)
2024 * @param {object} container
2025 * @param {string} funcName
2026 * @param {function} prepend
2027 * @param {function} append
2028 */
2029 extender: function extender(container, funcName, prepend, append) {
2030 (function () {
2031 let proxied = container[funcName];
2032 container[funcName] = function () {
2033 if (prepend) prepend.apply(this);
2034 let result = proxied.apply(this, arguments);
2035 if (append) append.apply(this);
2036 return result;
2037 };
2038 })();
2039 },
2040
2041 /**
2042 * TODO
2043 */
2044 logArguments: function logArguments(funcName) {
2045 (function () {
2046 let log = function () {
2047 var counter = 0;
2048 [].forEach.call(arguments, function (el) {
2049 console.log(funcName + ' ' + counter + ': ' + el);
2050 counter++;
2051 });
2052 };
2053
2054 let proxied = window[funcName];
2055
2056 window[funcName] = function () {
2057 log.apply(this, arguments);
2058 let result = proxied.apply(this, arguments);
2059 return result;
2060 };
2061
2062 })();
2063 }
2064}
2065Main.addModule(Dev);/* End: src\js\base\dev.js */
2066// modules
2067/* Begin: src\js\modules\marketHistoryViewer.js */
2068var MarketHistoryViewer = {
2069 namespace: 'MarketHistoryViewer',
2070 _CTE: null,
2071 _DA: null,
2072 _GEN: null,
2073 _AUTO: null,
2074 _SETTINGS: null,
2075 _DIALOG: null,
2076
2077 _chart: null,
2078 rangeMin: new Date(new Date() - 24 * 60 * 60 * 1000),
2079 rangeMax: new Date(),
2080 _activeItems: [],
2081 defaultWindowInSec: 2 * 7 * 24 * 60 * 60 * 1000,
2082
2083 init: function () {
2084 this._CTE = Main.getModule('Constants');
2085 this._DA = Main.getModule('DataAccess');
2086 this._GEN = Main.getModule('GeneralFunctions');
2087 this._AUTO = Main.getModule('Automation');
2088 this._SETTINGS = Main.getModule('Settings');
2089 this._DIALOG = Main.getModule('Dialog');
2090
2091 this._initDialog();
2092 this._initChart();
2093 this._initSlider();
2094 this._initSelector();
2095 this._initUI();
2096 this._initSettings();
2097 },
2098
2099 _initDialog: function () {
2100 let el = '';
2101 el += '<select id="chart-history-selector" multiple="multiple"></select>';
2102 el += '<canvas id="chart-history" width="auto" height="auto"></canvas>';
2103 el += '<div id="chart-history-slider" style="margin:10px"></div>';
2104 el += '<div id="chart-history-time"></div>';
2105 this._DIALOG.create('market-history-viewer-chart', 'Price history', el)
2106 },
2107
2108 _initChart: function () {
2109 let ctx = document.getElementById('chart-history').getContext('2d')
2110 this._chart = new Chart(ctx, {
2111 type: 'line',
2112 data: [],
2113 options: {
2114 scales: {
2115 xAxes: [{
2116 type: 'time',
2117 distribution: 'linear',
2118 time: {
2119 unit: 'day'
2120 },
2121 ticks: {
2122 min: this.rangeMin,
2123 max: this.rangeMax
2124 }
2125 }]
2126 }
2127 }
2128 });
2129 this._updateChartRange();
2130 },
2131
2132 _initSlider: function () {
2133 var slider = document.getElementById('chart-history-slider');
2134 let currentTime = new Date();
2135 let maxTime = currentTime.getTime();
2136 let minTime = new Date(currentTime - this.defaultWindowInSec).getTime();
2137 noUiSlider.create(slider, {
2138 range: {
2139 min: minTime,
2140 max: maxTime
2141 },
2142 // Steps of one hour
2143 step: 60 * 60 * 1000,
2144 // Two more timestamps indicate the handle starting positions.
2145 start: [new Date(currentTime - 24 * 60 * 60 * 1000).getTime(), maxTime],
2146 // No decimals
2147 format: wNumb({
2148 decimals: 0
2149 }),
2150 connect: true
2151 });
2152
2153 slider.noUiSlider.on('set', function (values, handle) {
2154 Main.getModule('MarketHistoryViewer').rangeMin = new Date(+values[0]);
2155 Main.getModule('MarketHistoryViewer').rangeMax = new Date(+values[1]);
2156 Main.getModule('MarketHistoryViewer')._updateChartRange();
2157 });
2158 },
2159
2160 _initSelector: function () {
2161 var selector = document.getElementById('chart-history-selector');
2162 this._CTE.marketList.forEach(item => {
2163 selector.insertAdjacentHTML('beforeend', '<option value="' + item + '">' + this._CTE.itemList[item][1] + '</option>');
2164 })
2165 $('#chart-history-selector').select2({ width: '100%' });
2166 $('#chart-history-selector').on('change', function (e) {
2167 var items = [];
2168 $('#chart-history-selector').select2('data').forEach(item => {
2169 items.push(item['id']);
2170 });
2171 Main.getModule('MarketHistoryViewer')._activeItems = items;
2172 Main.getModule('MarketHistoryViewer')._drawChart();
2173 });
2174 // TODO make names look better instead of same as value
2175
2176 /** TODO optgroup can be uset to split into groups
2177 * example html:
2178 <select class="js-states form-control">
2179 <optgroup label="Alaskan/Hawaiian Time Zone">
2180 <option value="AK">Alaska</option>
2181 <option value="HI">Hawaii</option>
2182 </optgroup>
2183 <optgroup label="Pacific Time Zone">
2184 <option value="CA">California</option>
2185 <option value="NV">Nevada</option>
2186 <option value="OR">Oregon</option>
2187 <option value="WA">Washington</option>
2188 </optgroup>
2189 </select>
2190 */
2191 },
2192
2193 _initUI: function () {
2194 var chartButtonHtml = '<span id="history-chart-btn" class="market-browse-button" ><img src="images/icons/marketHistory2.png" class="image-icon-30" title=""> graph</span>'
2195 $('#market-table')[0].parentNode.insertAdjacentHTML('afterbegin', chartButtonHtml);
2196 $('#history-chart-btn')[0].addEventListener('click', () => Main.getModule('MarketHistoryViewer').open());
2197
2198 // var testButtonHtml = '<span id="history-test-btn" class="market-browse-button" >save market</span>'
2199 // $('#market-table')[0].parentNode.insertAdjacentHTML('afterbegin', testButtonHtml);
2200 // $('#history-test-btn')[0].addEventListener('click', () => Main.getModule('MarketHistoryViewer').saveMarketToLocal());
2201
2202 },
2203
2204 _initSettings: function () {
2205 this._SETTINGS.addCategory('MHV', 'Market history viewer')
2206 this._SETTINGS.addSetting({
2207 type: 'checkbox',
2208 category: 'MHV',
2209 key: 'MHVLocal',
2210 text: 'Use local data',
2211 defaultValue: 'true',
2212 })
2213 },
2214
2215 open: function () {
2216 var slider = document.getElementById('chart-history-slider');
2217 var currentTime = new Date();
2218 slider.noUiSlider.updateOptions({
2219 range: {
2220 'min': new Date(currentTime - this.defaultWindowInSec).getTime(),
2221 'max': currentTime.getTime()
2222 }
2223 });
2224 this._DIALOG.open('market-history-viewer-chart')
2225 },
2226
2227 _addDataSet: function (label, color, data) {
2228 this._chart.data.datasets.push({
2229 label: label,
2230 borderColor: color,
2231 backgroundColor: '#0000',
2232 data: data
2233 });
2234 this._chart.update();
2235 },
2236
2237 _addItem: function (strId, color) {
2238 let data = [];
2239 const hdata = this._DA.getHistory(strId, () => this._drawChart())
2240 if (typeof hdata !== 'undefined') {
2241 hdata.forEach(x => {
2242 data.push({
2243 x: x[2],
2244 y: x[1]
2245 });
2246 });
2247 }
2248 const name = this._CTE.itemList[strId][1];
2249 this._addDataSet(name, color, data);
2250 },
2251
2252 _clearChart: function () {
2253 this._chart.data.datasets = [];
2254 },
2255
2256 _updateChartRange: function () {
2257 this._chart.options.scales.xAxes[0].ticks.min = this.rangeMin;
2258 this._chart.options.scales.xAxes[0].ticks.max = this.rangeMax;
2259 var Element = document.getElementById('chart-history-time');
2260 Element.innerHTML = 'From ' + this.rangeMin.toLocaleString() + ' to ' + this.rangeMax.toLocaleString();
2261 this._chart.update();
2262 $('#chart-history-selector').find(':selected');
2263 this._chart.update();
2264 },
2265
2266 _drawChart: function () {
2267 this._clearChart();
2268 var color = 0;
2269 this._activeItems.forEach(x => {
2270 const colors = this._DA.get('chartColors');
2271 this._addItem(x, colors[color]);
2272 color = (color < colors.length - 1) ? color + 1 : 0;
2273 });
2274 },
2275
2276 saveMarketToLocal: function () {
2277 let historyData = this._DA.get('MHVHistoryData');
2278 let priceList = {};
2279 document.querySelectorAll('#market-table tbody tr')
2280 .forEach(x => {
2281 let amount = x.getAttribute('data-market-amount');
2282 let id = x.getAttribute('data-market-itemid')
2283 // let strId = this._DA.getStrIdById(id);
2284 let strId;
2285 let price = parseInt(x.getAttribute('data-market-price'));
2286 if (id !== null) {
2287 strId = this._DA.getStrIdByName(x.querySelector('td').innerText);
2288 }
2289 if (typeof this._DA.getStrIdById(id) === 'undefined') {
2290 console.log('new id found! name: ' + x.querySelector('td').innerText + ' id: ' + id);
2291 }
2292
2293 if (typeof strId !== 'undefined') {
2294 if (typeof priceList[strId] === 'undefined' || priceList[strId][0] > price) {
2295 priceList[strId] = [amount, price, new Date()];
2296 }
2297 } else if (id !== null) {
2298 console.log('new item found! name: ' + x.querySelector('td').innerText + ' id: ' + id);
2299 }
2300 });
2301 for (const key in priceList) {
2302 if (priceList.hasOwnProperty(key)) {
2303 if (typeof historyData[key] === 'undefined') {
2304 historyData[key] = [];
2305 }
2306 historyData[key].push(priceList[key])
2307 }
2308 }
2309 this._DA.set('MHVHistoryData', historyData)
2310 }
2311}
2312Main.addModule(MarketHistoryViewer);/* End: src\js\modules\marketHistoryViewer.js */
2313/* Begin: src\js\modules\notifications.js */
2314/**
2315 * Description.
2316 * @namespace
2317 */
2318var Notifications = {
2319 namespace: 'Notifications', // the name used by other modules
2320 // add/remove as needed
2321 _CTE: null,
2322 _DA: null,
2323 _GEN: null,
2324 _AUTO: null,
2325 _SETTINGS: null,
2326 _DIALOG: null,
2327
2328 _choices: null,
2329
2330 /**
2331 * Initialize Notifications
2332 */
2333 init: function () {
2334 this._CTE = Main.getModule('Constants');
2335 this._DA = Main.getModule('DataAccess');
2336 this._GEN = Main.getModule('GeneralFunctions');
2337 this._AUTO = Main.getModule('Automation');
2338 this._SETTINGS = Main.getModule('Settings');
2339 this._DIALOG = Main.getModule('Dialog');
2340
2341 // code that has to be run upon initialization
2342
2343 this._GEN.runOnFirstTick(() => {
2344 // code that has to be run after logon
2345 });
2346 this._AUTO.onCommand(this._processChatArray, 'CHAT')
2347 this._initSettings();
2348
2349 },
2350
2351 _initSettings: function () {
2352 this._SETTINGS.addCategory('notifications', 'Notifications')
2353
2354 this._SETTINGS.addSetting({
2355 category: 'notifications',
2356 key: 'notifications-keywords',
2357 type: 'button',
2358 text: 'Change keywords.',
2359 onChange: this._openKeywordsDialog,
2360 })
2361 this._DIALOG.create('notifications-keywords', 'Keywords', '"_" as first or last character is seen as space!</br><input class="form-control" id="redderke-notifications-keywords" type="text" placeholder="This is a placeholder" class="" />')
2362 const element = document.querySelector('#redderke-notifications-keywords');
2363 this._choices = new Choices(element, {
2364 duplicateItemsAllowed: false,
2365 removeItemButton: true,
2366 });
2367 if(this._DA.get('notificationsKeywords') == null) this._DA.set('notificationsKeywords', []);
2368 this._choices.setValue(this._DA.get('notificationsKeywords'));
2369 this._choices.passedElement.element.addEventListener(
2370 'change',
2371 function(event) {
2372 Main.getModule('DataAccess').set('notificationsKeywords', Main.getModule('Notifications')._choices.getValue(true))
2373 },
2374 false,
2375 );
2376 },
2377
2378 _processChatArray: function (cmd, params) {
2379 let str = params[3].substring(params[3].indexOf(":") + 1);
2380 let matchStr = '';
2381 Main.getModule('DataAccess').get('notificationsKeywords').forEach(item => {
2382 let itemArr = Array.from(item);
2383 if(itemArr[0] == '_') itemArr[0] = ' ';
2384 if(itemArr[itemArr.length-1] == '_') itemArr[itemArr.length-1] = ' ';
2385 item = itemArr.join('');
2386 if(str.toLowerCase().includes(item.toLowerCase())){
2387 matchStr += ' ' + item;
2388 }
2389 });
2390 if(matchStr != '' && params[2] != 5) Main.getModule('GeneralFunctions').sendNotification(matchStr, params[0] + ': ' + str)
2391 },
2392
2393 _openKeywordsDialog: function () {
2394 Main.getModule('Dialog').open('notifications-keywords')
2395 },
2396}
2397Main.addModule(Notifications);/* End: src\js\modules\notifications.js */
2398
2399
2400window.redderkeUtils = Main;
2401Main.init();
2402
2403// quick fixes
2404Main.getModule('GeneralFunctions').runOnFirstTick(function () {
2405 window._$_4e08[38] = 'https://dh2.diamondhunt.co/stats/lookup.php?user=';
2406 setTimeout(() => {
2407 let originalFullProfileUrl = document.querySelector('#moreStats-a-tag').href;
2408 document.querySelector('#moreStats-a-tag').href = originalFullProfileUrl.replace('www', 'dh2');
2409 }, 10000);
2410})
2411
2412Main.getModule('GeneralFunctions').addCss('https://cdnjs.cloudflare.com/ajax/libs/noUiSlider/14.0.3/nouislider.min.css');
2413Main.getModule('GeneralFunctions').addCss('https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.12/css/select2.min.css');
2414Main.getModule('GeneralFunctions').addCss('https://cdn.jsdelivr.net/npm/choices.js@9.0.1/public/assets/styles/choices.min.css');
2415
2416 /**
2417 * select all from selector
2418 $("#chart-history-selector > option").prop("selected","selected");
2419 $("#chart-history-selector").trigger("change");
2420 */
2421//})();