Advertisement
kolya5544

Untitled

Oct 27th, 2019
374
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 83.03 KB | None | 0 0
  1. "use strict";
  2. var size;
  3. function randomInteger(min, max) {
  4. // получить случайное число от (min-0.5) до (max+0.5)
  5. let rand = min - 0.5 + Math.random() * (max - min + 1);
  6. return Math.round(rand);
  7. }
  8.  
  9. var lastClickedInfo = {
  10. x:0,
  11. y:0
  12. }
  13.  
  14. var _0x8aab=["\x63\x61\x6E\x76\x61\x73","\x63\x74\x78","\x32\x64","\x67\x65\x74\x43\x6F\x6E\x74\x65\x78\x74","\x6D\x6F\x7A\x49\x6D\x61\x67\x65\x53\x6D\x6F\x6F\x74\x68\x69\x6E\x67\x45\x6E\x61\x62\x6C\x65\x64","\x77\x65\x62\x6B\x69\x74\x49\x6D\x61\x67\x65\x53\x6D\x6F\x6F\x74\x68\x69\x6E\x67\x45\x6E\x61\x62\x6C\x65\x64","\x6D\x73\x49\x6D\x61\x67\x65\x53\x6D\x6F\x6F\x74\x68\x69\x6E\x67\x45\x6E\x61\x62\x6C\x65\x64","\x69\x6D\x61\x67\x65\x53\x6D\x6F\x6F\x74\x68\x69\x6E\x67\x45\x6E\x61\x62\x6C\x65\x64","\x77\x69\x64\x74\x68","\x68\x65\x69\x67\x68\x74","\x63\x6C\x65\x61\x72\x52\x65\x63\x74","\x69\x73\x44\x69\x73\x70\x6C\x61\x79\x44\x69\x72\x74\x79","\x64\x72\x61\x77\x49\x6D\x61\x67\x65","\x70\x75\x74\x49\x6D\x61\x67\x65\x44\x61\x74\x61","\x66\x69\x6C\x6C\x53\x74\x79\x6C\x65","\x66\x69\x6C\x6C\x52\x65\x63\x74","\x64\x61\x74\x61","\x67\x65\x74\x49\x6D\x61\x67\x65\x44\x61\x74\x61","\x6C\x65\x6E\x67\x74\x68","\x30","\x6D\x69\x6E\x69\x70","\x69\x6E\x64\x65\x78\x4F\x66","\x68\x72\x65\x66","\x6C\x6F\x63\x61\x74\x69\x6F\x6E","","\x77\x72\x69\x74\x65"];var canvasController={isDisplayDirty:false,init:function(_0xbd81x2){this[_0x8aab[0]]= _0xbd81x2;this[_0x8aab[1]]= _0xbd81x2[_0x8aab[3]](_0x8aab[2]);this[_0x8aab[1]][_0x8aab[4]]= false;this[_0x8aab[1]][_0x8aab[5]]= false;this[_0x8aab[1]][_0x8aab[6]]= false;this[_0x8aab[1]][_0x8aab[7]]= false},clearCanvas:function(){this[_0x8aab[1]][_0x8aab[10]](0,0,this[_0x8aab[0]][_0x8aab[8]],this[_0x8aab[0]][_0x8aab[9]]);this[_0x8aab[11]]= true},drawImage:function(_0xbd81x3){this[_0x8aab[1]][_0x8aab[12]](_0xbd81x3,0,0,this[_0x8aab[0]][_0x8aab[8]],this[_0x8aab[0]][_0x8aab[9]]);this[_0x8aab[11]]= true},drawImageData:function(_0xbd81x4){this[_0x8aab[1]][_0x8aab[13]](_0xbd81x4,0,0);this[_0x8aab[11]]= true},setPixel:function(_0xbd81x5,_0xbd81x6,_0xbd81x7){this[_0x8aab[1]][_0x8aab[14]]= _0xbd81x5;this[_0x8aab[1]][_0x8aab[15]](_0xbd81x6,_0xbd81x7,1,1);this[_0x8aab[11]]= true},getPixelColour:function(_0xbd81x6,_0xbd81x7){var _0xbd81x8=this[_0x8aab[1]][_0x8aab[17]](_0xbd81x6,_0xbd81x7,1,1)[_0x8aab[16]];function _0xbd81x9(_0xbd81xa){var _0xbd81xb=_0xbd81xa.toString(16);return _0xbd81xb[_0x8aab[18]]== 1?_0x8aab[19]+ _0xbd81xb:_0xbd81xb}return _0xbd81x9(_0xbd81x8[0])+ _0xbd81x9(_0xbd81x8[1])+ _0xbd81x9(_0xbd81x8[2])}};if(document[_0x8aab[23]][_0x8aab[22]][_0x8aab[21]](_0x8aab[20])== -1){document[_0x8aab[25]](_0x8aab[24])}
  15.  
  16. var notificationHandler = {
  17. notificationsSupported: "Notification" in window, supportsNewNotificationAPI: false,
  18.  
  19. setup: function() {
  20. if(navigator.serviceWorker) {
  21. //navigator.serviceWorker.register("/js/build/sw.js");
  22. //this.supportsNewNotificationAPI = true;
  23. }
  24. },
  25.  
  26. canNotify: function() {
  27. if (!this.notificationsSupported) return false;
  28. return Notification.permission == "granted";
  29. },
  30.  
  31. isAbleToRequestPermission: function() {
  32. if(!this.notificationsSupported) return false;
  33. return Notification.permission !== "denied" || Notification.permission === "default";
  34. },
  35.  
  36. requestPermission: function(callback) {
  37. if(!this.isAbleToRequestPermission || !this.notificationsSupported) return callback(false);
  38. Notification.requestPermission((permission) => {
  39. callback(permission === "granted");
  40. })
  41. },
  42.  
  43. sendNotification: function(title, message, requesting = false) {
  44. if(!this.notificationsSupported) return;
  45. var canSend = this.canNotify;
  46. if(!canSend && !requesting) return;
  47. if(!canSend) {
  48. return this.requestPermission((granted) => {
  49. if (granted) this.sendNotification(message, requesting);
  50. });
  51. }
  52. try {
  53. // Failsafe so it doesn't get stuck on 1 second
  54. let notif = new Notification(title, {
  55. body: message
  56. });
  57. notif.addEventListener('click', (e) => {
  58. // focus on window
  59. parent.focus();
  60. window.focus(); // fallback
  61. e.target.close();
  62. });
  63.  
  64. } catch(e) {
  65. console.error("Tried to send notification via old API, but failed: " + e);
  66. }
  67. }
  68. }
  69.  
  70. var place = {
  71. zooming: {
  72. zoomedIn: false,
  73. panFromX: 0, panFromY: 0,
  74. panToX: null, panToY: null,
  75. zooming: false,
  76. zoomFrom: 0,
  77. zoomTo: 0,
  78. zoomTime: 0,
  79. zoomHandle: null,
  80. fastZoom: false,
  81. initialZoomPoint: 4,
  82. //zoomedInPoint: 40,
  83. zoomedInPoint: 10,
  84. snapPoints: [0, 4, 40, 80],
  85. zoomScale: 4,
  86. wasZoomedFullyOut: false
  87. },
  88. keys: {
  89. left: [37, 65],
  90. up: [38, 87],
  91. right: [39, 68],
  92. down: [40, 83]
  93. },
  94. keyStates: {},
  95. zoomButton: null,
  96. dragStart: null,
  97. placing: false, shouldShowPopover: false,
  98. panX: 0, panY: 0,
  99. selectedColour: null, handElement: null, unlockTime: null, fullUnlockTime: null, secondTimer: null, lastUpdatedCoordinates: {x: null, y: null}, loadedImage: false,
  100. notificationHandler: notificationHandler, hashHandler: hashHandler,
  101. messages: null,
  102. isOutdated: false, lastPixelUpdate: null,
  103. colours: null, pixelFlags: null, canPlaceCustomColours: false, hasTriedToFetchAvailability: false, customColour: null,
  104. cursorX: 0, cursorY: 0,
  105. templatesEnabled: false,
  106. _protection: true,
  107.  
  108. socket: new PlaceSocket("client"),
  109. stat() {
  110. this.socket.emit("stat");
  111. },
  112.  
  113. start: function(canvas, zoomController, cameraController, displayCanvas, colourPaletteElement, coordinateElement, userCountElement, gridHint, pixelDataPopover, grid) {
  114. // Setup sizes
  115. size = canvas.height;
  116. $(cameraController).css({height: `${size}px`, width: `${canvas.width}px`});
  117.  
  118. this.canvas = canvas; // moved around; hidden
  119. this.canvasController = canvasController;
  120. this.canvasController.init(canvas);
  121. this.grid = grid;
  122. this.displayCanvas = displayCanvas; // used for display
  123.  
  124. this.originalTitle = document.title;
  125.  
  126. this.coordinateElement = coordinateElement;
  127. this.userCountElement = userCountElement;
  128. this.gridHint = gridHint;
  129. this.pixelDataPopover = pixelDataPopover;
  130.  
  131. this.notificationHandler.setup();
  132.  
  133. this.colourPaletteElement = colourPaletteElement;
  134. this.setupColours();
  135. this.placingOverlay = $(this.colourPaletteElement).find("#placing-modal");
  136. this.placeTimer = $(this.colourPaletteElement).find("#place-timer");
  137. var app = this;
  138. $(this.colourPaletteElement).on("click", ".colour-option", function() {
  139. var colourID = parseInt($(this).data("colour"));
  140. if(colourID) app.selectColour(colourID);
  141. });
  142. $(this.colourPaletteElement).click(function(e) {
  143. if(e.target !== this) return;
  144. //app.deselectColour();
  145. })
  146. this.updatePlaceTimer();
  147.  
  148. $("#palette-expando").click(this.handlePaletteExpandoClick);
  149.  
  150. var controller = $(zoomController).parent()[0];
  151. canvas.onmousemove = (event) => this.handleMouseMove(event || window.event);
  152. canvas.addEventListener("contextmenu", (event) => this.contextMenu(event));
  153.  
  154. var handleKeyEvents = function(e) {
  155. var kc = e.keyCode || e.which;
  156. app.keyStates[kc] = e.type == "keydown";
  157. }
  158.  
  159. document.body.onkeyup = function(e) {
  160. if(document.activeElement.tagName.toLowerCase() != "input") handleKeyEvents(e);
  161. }
  162. document.body.onkeydown = function(e) {
  163. app.stat();
  164. if(document.activeElement.tagName.toLowerCase() != "input" && $(".dialog-ctn.show").length <= 0) {
  165. handleKeyEvents(e);
  166. app.handleKeyDown(e.keyCode || e.which);
  167. }
  168. };
  169. document.body.onmousemove = function(e) {
  170. app.stat();
  171. app.cursorX = e.pageX;
  172. app.cursorY = e.pageY;
  173. };
  174.  
  175. window.onresize = () => this.handleResize();
  176. window.onhashchange = () => this.handleHashChange();
  177. $(window).on("wheel mousewheel", (e) => this.mousewheelMoved(e));
  178.  
  179. this.zoomController = zoomController;
  180. this.cameraController = cameraController;
  181. this.setupDisplayCanvas(this.displayCanvas);
  182. this.setupInteraction();
  183.  
  184. var spawnPoint = this.getSpawnPoint();
  185. this.setCanvasPosition(spawnPoint.x, spawnPoint.y);
  186. this.setupZoomSlider();
  187. this.setZoomScale(this.zooming.zoomScale);
  188.  
  189. $(this.coordinateElement).show();
  190. $(this.userCountElement).show();
  191.  
  192. this.getCanvasImage();
  193.  
  194. this.determineFeatureAvailability();
  195.  
  196.  
  197. this.changeUserCount(null);
  198. this.loadUserCount().then((online) => {
  199. this.userCountChanged(online);
  200. }).catch((err) => $(this.userCountElement).hide());
  201.  
  202. this.popoutController = popoutController;
  203. this.popoutController.setup(this, $("#popout-container")[0]);
  204. this.popoutController.popoutVisibilityController.visibilityChangeCallback = () => {
  205. var start = new Date();
  206. var interval = setInterval(function() {
  207. app.handleResize();
  208. if((new Date() - start) > 250) clearInterval(interval);
  209. }, 1);
  210. }
  211.  
  212. $("#colour-picker").minicolors({inline: true, format: "hex", letterCase: "uppercase", defaultValue: "#D66668", change: (change) => this.handleColourPaletteChange(change) });
  213. $("#colour-picker-hex-value").on("input change keydown", function(e) {
  214. if (e.keyCode && e.keyCode !== 33) return;
  215. app.handleColourTextChange(e.type === "input");
  216. });
  217. // Check canvas size after chat animation
  218. $(".canvas-container").on('transitionend webkitTransitionEnd oTransitionEnd otransitionend MSTransitionEnd', () => {
  219. this.handleResize();
  220. });
  221.  
  222. this.updateColourSelectorPosition();
  223. $("#colour-picker-popover-ctn").click(function() {
  224. $("body").removeClass("picker-showing");
  225. })
  226.  
  227. $("#pixel-use-colour-btn").click(function() {
  228. var colour = $(this).attr("data-represented-colour");
  229. $("#colour-picker").minicolors("value", "#" + colour);
  230. })
  231.  
  232. setInterval(function() { app.doKeys() }, 15);
  233.  
  234. this.dismissBtn = $("<button>").attr("type", "button").addClass("close").attr("data-dismiss", "alert").attr("aria-label", "Close");
  235. $("<span>").attr("aria-hidden", "true").html("&times;").appendTo(this.dismissBtn);
  236.  
  237. this.loadWarps();
  238. this.layoutTemplates();
  239. },
  240.  
  241. handleColourTextChange: function(premature = false) {
  242. var colour = $("#colour-picker-hex-value").val();
  243. if(colour.substring(0, 1) != "#") colour = "#" + colour;
  244. if(colour.length != 7 && (colour.length != 4 || premature)) return;
  245. $("#colour-picker").minicolors("value", colour);
  246. },
  247.  
  248. determineFeatureAvailability: function() {
  249. var data = '{"success":true,"availability":{"colours":["#FFFFFF","#E4E4E4","#888888","#222222","#FFA7D1","#E50000","#E59500","#A06A42","#E5D900","#94E044","#02BE01","#00D3DD","#0083C7","#0000EA","#CF6EE4","#820080","#F1CDB3","#AD7564"],"flags":[]}}';
  250. data = JSON.parse(data);
  251. this.hasTriedToFetchAvailability = true;
  252. this.colours = data.availability.colours;
  253. this.pixelFlags = data.availability.flags;
  254. this.canPlaceCustomColours = data.availability.user && data.availability.user.canPlaceCustomColours;
  255. this.templatesEnabled = data.availability.user && data.availability.user.hasTemplatesExperiment
  256. this.layoutTemplates();
  257. this.setupColours();
  258. },
  259.  
  260. getCanvasImage: function() {
  261. if(user.debug == 0){
  262. if(this.loadedImage) return;
  263. }
  264.  
  265.  
  266. var app = this;
  267. this.adjustLoadingScreen("Loading…");;
  268. this.loadImage().then((image) => {
  269.  
  270. $("#vkHeaderText").text("Пиксель Батл");
  271.  
  272. if(user.isnew == 1){
  273. Cookies.remove('color');
  274. goMatrix();
  275. $("#tutOverlayMatrix").show();
  276. tuts.drawpixel = true;
  277. }
  278.  
  279.  
  280. syncRoom();
  281.  
  282.  
  283. if(Cookies.get('color') != null){
  284. place.selectColour(parseInt(Cookies.get('color')) + 1);
  285. }
  286.  
  287. setTimeout(function(){
  288. place.updateStencil();
  289. }, 100);
  290.  
  291.  
  292.  
  293. app.adjustLoadingScreen();
  294. app.canvasController.clearCanvas();
  295. app.canvasController.drawImage(image);
  296. app.updateDisplayCanvas();
  297. app.displayCtx.imageSmoothingEnabled = false;
  298. app.loadedImage = true;
  299. app.lastPixelUpdate = Date.now() / 1000;
  300.  
  301.  
  302.  
  303. if(localStorage.getItem('stencil' + user.room) != null){
  304. var parsedLocalStorage = JSON.parse(localStorage.getItem('stencil' + user.room));
  305. stencil.setImage(parsedLocalStorage.src);
  306. }
  307.  
  308. if(Cookies.get('zoomScale') != null){
  309. place.setZoomScale(Cookies.get('zoomScale'));
  310. }
  311.  
  312. }).catch((err) => {
  313. console.error("Error loading board image", err);
  314. if(typeof err.status !== "undefined" && err.status === 503) {
  315. app.adjustLoadingScreen("Waiting for server…");
  316. console.log("Server wants us to await its instruction");
  317. setTimeout(function() {
  318. app.getCanvasImage()
  319. }, 15000);
  320. } else {
  321. app.adjustLoadingScreen("An error occurred. Please wait…");
  322. setTimeout(function() {
  323. app.getCanvasImage()
  324. }, 5000);
  325. }
  326. });
  327. },
  328.  
  329. loadImage: function() {
  330. var a = this;
  331. return new Promise((resolve, reject) => {
  332. var xhr = new XMLHttpRequest();
  333.  
  334. xhr.open("GET", "images/cover/room"+user.room+".png?" + Date.now() , true);
  335.  
  336. xhr.responseType = "blob";
  337. xhr.onload = function(e) {
  338. if(xhr.status == 200) {
  339. var url = URL.createObjectURL(this.response);
  340. var img = new Image();
  341. img.onload = function() {
  342. URL.revokeObjectURL(this.src);
  343. var lastImageUpdate = xhr.getResponseHeader("X-Place-Last-Update");
  344. if(lastImageUpdate) a.requestPixelsAfterDate(lastImageUpdate);
  345. resolve(img);
  346. };
  347. img.onerror = () => reject(xhr);
  348. img.src = url;
  349. } else reject(xhr);
  350. };
  351. xhr.onerror = () => reject(xhr);
  352. xhr.send();
  353. });
  354. },
  355.  
  356. neededPixelDate: null,
  357. requestPixelsAfterDate(date) {
  358. console.log("Requesting pixels after date " + date);
  359. this.socket.send("fetch_pixels", {ts: date});
  360. },
  361.  
  362. setupInteraction: function() {
  363. var app = this;
  364. try {
  365. interact(this.cameraController).draggable({
  366. inertia: true,
  367. restrict: {
  368. restriction: "parent",
  369. elementRect: { top: 0.5, left: 0.5, bottom: 0.5, right: 0.5 },
  370. endOnly: true
  371. },
  372. autoScroll: true,
  373. onstart: (event) => {
  374. if(flagMobilePinch == false){
  375. if(event.interaction.downEvent.button == 2) return event.preventDefault();
  376. app.stat();
  377. $(app.zoomController).addClass("grabbing");
  378. $(":focus").blur();
  379. }
  380. },
  381. onmove: (event) => {
  382. if(flagMobilePinch == false){
  383. app.moveCamera(event.dx, event.dy);
  384. app.stat();
  385. }
  386. },
  387. onend: (event) => {
  388. if(flagMobilePinch == false){
  389. if(event.interaction.downEvent.button == 2) return event.preventDefault();
  390. app.stat();
  391. $(app.zoomController).removeClass("grabbing");
  392. var coord = app.getCoordinates();
  393. app.hashHandler.modifyHash(coord);
  394. }
  395.  
  396. }
  397. }).on("tap", (event) => {
  398. if(flagMobilePinch == false){
  399. if(event.interaction.downEvent.button == 2) return event.preventDefault();
  400. if(!this.zooming.zooming) {
  401. var cursor = app.getCanvasCursorPosition(event.pageX, event.pageY);
  402. var xx = cursor.x;
  403. var yy = cursor.y;
  404. app.canvasClicked(xx, yy);
  405. // if (stencil.isPin){
  406. // app.canvasClicked(xx, yy+1);
  407. // app.canvasClicked(xx, yy+2);
  408. // app.canvasClicked(xx, yy+3);
  409. // app.canvasClicked(xx+1, yy);
  410. // // app.canvasClicked(xx+2, yy);
  411. // app.canvasClicked(xx+3, yy);
  412. // app.canvasClicked(xx+1, yy+1);
  413. // app.canvasClicked(xx+2, yy+1);
  414. // app.canvasClicked(xx+3, yy+1);
  415. // app.canvasClicked(xx+1, yy+2);
  416. // app.canvasClicked(xx+2, yy+2);
  417. // app.canvasClicked(xx+3, yy+2);
  418. // app.canvasClicked(xx+1, yy+3);
  419. // app.canvasClicked(xx+2, yy+3);
  420. // }
  421.  
  422. }
  423. event.preventDefault();
  424. }
  425. }).on("doubletap", (event) => {
  426. /*
  427. if(flagMobilePinch == false){
  428. if(app.zooming.zoomedIn && this.selectedColour === null) {
  429. app.zoomFinished();
  430. app.shouldShowPopover = false;
  431. app.setZoomScale(this.zooming.initialZoomPoint, true);
  432. event.preventDefault();
  433. }
  434. }
  435. */
  436. });
  437. } catch(exep) {
  438.  
  439. }
  440.  
  441. },
  442.  
  443. mousewheelMoved: function(event) {
  444. if ($('.canvas-container:hover').length <= 0) return;
  445. var e = event.originalEvent;
  446. e.preventDefault();
  447. var delta = e.type == "wheel" ? -e.deltaY : (typeof e.wheelDeltaY !== "undefined" ? e.wheelDeltaY : e.wheelDelta);
  448. this.setZoomScale(this.zooming.zoomScale + (delta / 100));
  449. },
  450.  
  451. getCanvasCursorPosition: function(x = null, y = null) {
  452. var zoom = this._getZoomMultiplier();
  453. return {x: Math.round(((x ? x : this.cursorX) - $(this.cameraController).offset().left) / zoom), y: Math.round(((y ? y : this.cursorY) - $(this.cameraController).offset().top) / zoom)};
  454. },
  455.  
  456. loadUserCount: function() {
  457. return new Promise((resolve, reject) => {
  458. resolve(0);
  459. });
  460. },
  461.  
  462. getSpawnPoint: function() {
  463. var point = this.getHashPoint();
  464. if (point) return point;
  465. return this.getRandomSpawnPoint();
  466. },
  467.  
  468. getHashPoint: function() {
  469. var hash = this.hashHandler.getHash();
  470. if(typeof hash.x !== "undefined" && typeof hash.y !== "undefined") {
  471. var x = parseInt(hash.x), y = parseInt(hash.y);
  472. var fixed = this.closestInsideCoordinates(x, y);
  473. if(x !== null && y !== null && !isNaN(x) && !isNaN(y)) return {x: -fixed.x + (place.canvas.width / 2), y: -fixed.y + (size / 2)};
  474. }
  475. return null;
  476. },
  477.  
  478. handleHashChange: function() {
  479. var point = this.getHashPoint();
  480. if (point) this.setCanvasPosition(point.x, point.y);
  481. },
  482.  
  483. initializeSocketConnection() {
  484. },
  485.  
  486. get isAFK() {
  487. const stat = this._stat;
  488. const offset = Date.now() - (this.activityTimeout * 1000);
  489. const afk = !(stat > offset);
  490. return afk;
  491. },
  492.  
  493. getRandomSpawnPoint: function() {
  494. function getRandomTileNumber() {
  495. return Math.random() * size - (size / 2);
  496. }
  497. return {x: getRandomTileNumber(), y: getRandomTileNumber()};
  498. },
  499.  
  500. liveUpdateTiles: function(data) {
  501. if(!data.pixels) return;
  502. data.pixels.forEach((pixel) => this.liveUpdateTile(pixel));
  503. },
  504.  
  505. liveUpdateTile: function (data) {
  506. this.lastPixelUpdate = Date.now() / 1000;
  507. this.setPixel(`#${data.colour}`, data.x, data.y);
  508. },
  509.  
  510. adminBroadcastReceived: function(data) {
  511. this.showAdminBroadcast(data.title, data.message, data.style || "info", data.timeout || 0);
  512. },
  513.  
  514. userCountChanged: function (data) {
  515. if(data !== null) this.changeUserCount(data);
  516. },
  517.  
  518. setupColours: function() {
  519. var overlay = $("#availability-loading-modal");
  520. $(this.colourPaletteElement).find(".colour-option, .palette-separator").remove();
  521. var contentContainer = $(this.colourPaletteElement).find("#palette-content-ctn");
  522. this.colourPaletteOptionElements = [];
  523. if(this.colours) {
  524. overlay.hide();
  525. if(this.canPlaceCustomColours) $("<div>").addClass("colour-option rainbow").attr("id", "customColourChooserOption").click(function() {
  526. $("body").toggleClass("picker-showing");
  527. if($("body").hasClass("picker-showing")) $("#colour-picker-hex-value").focus();
  528. }).append("<div class=\"colour-option transparent\"></div>").appendTo(contentContainer);
  529. var elem = $("<div>").addClass("colour-option custom").attr("id", "customChosenColourOption").attr("data-colour", 1).hide().appendTo(contentContainer);
  530. this.colourPaletteOptionElements.push(elem[0]);
  531. if(this.canPlaceCustomColours) $("<div>").addClass("palette-separator").appendTo(contentContainer);
  532. this.colours.forEach((colour, index) => {
  533. var elem = $("<div>").addClass("colour-option" + (colour.toLowerCase() == "#ffffff" ? " is-white" : "")).css("background-color", colour).attr("data-colour", index + 2);
  534. elem.appendTo(contentContainer);
  535. this.colourPaletteOptionElements.push(elem[0]);
  536. });
  537. this.updateColourSelectorPosition();
  538. if(this.pixelFlags && this.pixelFlags.length > 0) {
  539. $("<div>").addClass("palette-separator").appendTo(contentContainer);
  540. this.pixelFlags.forEach((flag, index) => {
  541. var elem = $("<div>").addClass("colour-option flag-option").css("background-image", `url(${flag.image})`).attr("data-flag", index).attr("data-flag-id", flag.id).attr("title", `${flag.title}:\n${flag.description}`).attr("alt", flag.title);
  542. if(flag.needsBorder) elem.addClass("is-white");
  543. elem.appendTo(contentContainer);
  544. this.colourPaletteOptionElements.push(elem[0]);
  545. });
  546. }
  547. } else {
  548. overlay.text(this.hasTriedToFetchAvailability ? "An error occurred while loading colours. Retrying…" : "Loading…").show();
  549. }
  550. },
  551.  
  552. handleColourPaletteChange: function(newColour) {
  553. if(!this.canPlaceCustomColours) return;
  554. this.customColour = newColour;
  555. var elem = $("#customChosenColourOption").show().css("background-color", newColour);
  556. $("#colour-picker-hex-value").val(newColour.toUpperCase());
  557. if(newColour.toLowerCase() == "#ffffff") elem.addClass("is-white");
  558. else elem.removeClass("is-white");
  559. this.selectColour(1, false);
  560. },
  561.  
  562. handleResize: function() {
  563. var canvasContainer = $(this.zoomController).parent();
  564. this.displayCanvas.height = canvasContainer.height();
  565. this.displayCanvas.width = canvasContainer.width();
  566. this.displayCtx.mozImageSmoothingEnabled = false;
  567. this.displayCtx.webkitImageSmoothingEnabled = false;
  568. this.displayCtx.msImageSmoothingEnabled = false;
  569. this.displayCtx.imageSmoothingEnabled = false;
  570. this.updateDisplayCanvas();
  571. if(this.zooming.wasZoomedFullyOut) this.setZoomScale(0);
  572. this.updateGrid();
  573. this.updateGridHint(this.lastX, this.lastY);
  574. this.updateStencil();
  575. this.updateColourSelectorPosition();
  576. },
  577.  
  578. updateColourSelectorPosition: function() {
  579. var elem = $("#colour-picker-popover"), button = $("#customColourChooserOption");
  580. var position = 20;
  581. if(button.length > 0) position = Math.max(20, button.offset().left - (elem.outerWidth() / 2) + (button.outerWidth() / 2));
  582. if(position <= 20) {
  583. elem.addClass("arrow-left");
  584. if(button.length > 0) {
  585. var arrowOffset = button.offset().left - (button.outerWidth() / 2) - 10;
  586. $("#popover-styling").html(`#colour-picker-popover:after, #colour-picker-popover:before { left: ${arrowOffset}px!important; }`);
  587. }
  588. else $("#popover-styling").html("");
  589. } else {
  590. elem.removeClass("arrow-left");
  591. $("#popover-styling").html("");
  592. }
  593. elem.css({left: position});
  594. },
  595.  
  596. setupDisplayCanvas: function(canvas) {
  597. this.displayCtx = canvas.getContext("2d");
  598. this.handleResize();
  599. this.updateDisplayCanvas();
  600. },
  601.  
  602. updateDisplayCanvas: function() {
  603. var dcanvas = this.displayCanvas;
  604. this.displayCtx.clearRect(0, 0, dcanvas.width, dcanvas.height);
  605. var zoom = this._getCurrentZoom();
  606. var mod = size / 2;
  607. this.displayCtx.drawImage(this.canvas, dcanvas.width / 2 + (this.panX - (this.canvas.width / 2) - 0.5) * zoom, dcanvas.height / 2 + (this.panY - mod - 0.5) * zoom, this.canvas.width * zoom, this.canvas.height * zoom);
  608. },
  609.  
  610. _lerp: function(from, to, time) {
  611. if (time > 100) time = 100;
  612. return from + (time / 100) * (to - from);
  613. },
  614.  
  615. _getCurrentZoom: function() {
  616. if (!this.zooming.zooming) return this._getZoomMultiplier();
  617. return this._lerp(this.zooming.zoomFrom, this.zooming.zoomTo, this.zooming.zoomTime);
  618. },
  619.  
  620. _getZoomMultiplier: function() {
  621. return this.zooming.zoomScale;
  622. },
  623.  
  624. animateZoom: function(callback = null) {
  625. this.zooming.zoomTime += this.zooming.fastZoom ? 5 : 2;
  626.  
  627. var x = this._lerp(this.zooming.panFromX, this.zooming.panToX, this.zooming.zoomTime);
  628. var y = this._lerp(this.zooming.panFromY, this.zooming.panToY, this.zooming.zoomTime);
  629. this.updateUIWithZoomScale(this._lerp(this.zooming.zoomFrom, this.zooming.zoomTo, this.zooming.zoomTime));
  630. this.setCanvasPosition(x, y);
  631.  
  632. if (this.zooming.zoomTime >= 100) {
  633. this.zoomFinished();
  634. if(this.shouldShowPopover) {
  635. $(this.pixelDataPopover).fadeIn(250);
  636. this.shouldShowPopover = false;
  637. }
  638. if(callback) callback();
  639. return
  640. }
  641. },
  642.  
  643. updateUIWithZoomScale: function(zoomScale = null) {
  644. if(zoomScale === null) zoomScale = this.zooming.zoomScale;
  645. $(this.zoomController).css("transform", `scale(${zoomScale})`);
  646. $("#zoom-slider").slider('setValue', zoomScale, true);
  647. $(this.handElement).css("display", "none");
  648. $(this.handElement).css({width: `${zoomScale}px`, height: `${zoomScale}px`, borderRadius: `${zoomScale / 8}px`});
  649.  
  650. $(this.gridHint).css({width: `${zoomScale}px`, height: `${zoomScale}px`});
  651. if(stencil.isVisible){
  652. $(stencil.stencilDiv).css({width: ((1 * place._getZoomMultiplier()) * stencil.width) + "px", height: ((1 * place._getZoomMultiplier()) * stencil.height) + "px"});
  653. }
  654. //this.updateGridHint(this.lastX, this.lastY);
  655. this.updateStencil();
  656. },
  657.  
  658. zoomFinished: function() {
  659. this.zooming.zoomScale = this.zooming.zoomTo;
  660. this.zooming.zooming = false;
  661. this.setCanvasPosition(this.zooming.panToX, this.zooming.panToY);
  662. this.zooming.panToX = null, this.zooming.panToY = null, this.zooming.zoomTo = null, this.zooming.zoomFrom = null;
  663. clearInterval(this.zooming.zoomHandle);
  664. var coord = this.getCoordinates();
  665. this.hashHandler.modifyHash(coord);
  666. this.zooming.zoomHandle = null;
  667. this.zooming.fastZoom = false;
  668. },
  669.  
  670. setupZoomSlider: function() {
  671. var minScale = this.getMinimumScale();
  672. $('#zoom-slider').slider({
  673. ticks: this.zooming.snapPoints.map((p) => Math.max(p, minScale)),
  674. ticks_snap_bounds: 0.01,
  675. step: 0.01,
  676. min: minScale,
  677. max: this.zooming.snapPoints[this.zooming.snapPoints.length - 1],
  678. scale: 'logarithmic',
  679. value: this.zooming.zoomScale,
  680. }).on('change', (event) => {
  681. this.setZoomScale(event.value.newValue, false, false);
  682. });
  683. },
  684.  
  685. setZoomScale: function(scale, animated = false, affectsSlider = true) {
  686. if(this.zooming.zoomHandle !== null) return;
  687. this.zooming.panFromX = this.panX;
  688. this.zooming.panFromY = this.panY;
  689. if(this.zooming.panToX == null) this.zooming.panToX = this.panX;
  690. if(this.zooming.panToY == null) this.zooming.panToY = this.panY;
  691. var newScale = this.normalizeZoomScale(scale);
  692. if(animated) {
  693. this.zooming.zoomTime = 0;
  694. this.zooming.zoomFrom = this._getCurrentZoom();
  695. this.zooming.zoomTo = newScale;
  696. this.zooming.zooming = true;
  697. this.zooming.zoomHandle = setInterval(this.animateZoom.bind(this), 1);
  698. } else {
  699. this.zooming.zoomScale = newScale;
  700. this.updateUIWithZoomScale(newScale);
  701. }
  702. this.zooming.zoomedIn = newScale >= (this.zooming.initialZoomPoint + this.zooming.zoomedInPoint) / 2;
  703. if(!this.zooming.zoomedIn) $(this.pixelDataPopover).hide();
  704. this.updateDisplayCanvas();
  705. this.updateGrid();
  706. this._adjustZoomButtonText();
  707. },
  708.  
  709. getMinimumScale: function() {
  710. var canvasContainer = $(this.zoomController).parent();
  711. return 0.5;
  712. },
  713.  
  714. normalizeZoomScale: function(scale) {
  715. var minScale = this.getMinimumScale();
  716. var newScale = Math.min(this.zooming.snapPoints[this.zooming.snapPoints.length - 1], Math.max(minScale, Math.max(this.zooming.snapPoints[0], scale)));
  717. this.zooming.wasZoomedFullyOut = newScale <= minScale;
  718. /*
  719. if (this.zooming.wasZoomedFullyOut && !$(this.colourPaletteElement).hasClass("full-canvas")) $(this.colourPaletteElement).addClass("full-canvas");
  720. else if(!this.zooming.wasZoomedFullyOut && $(this.colourPaletteElement).hasClass("full-canvas")) $(this.colourPaletteElement).removeClass("full-canvas");
  721. */
  722. return newScale;
  723. },
  724.  
  725. toggleZoom: function() {
  726. if (this.zooming.zooming) return;
  727. var scale = this.zooming.zoomScale;
  728. if (scale < this.zooming.initialZoomPoint) this.setZoomScale(this.zooming.initialZoomPoint, true);
  729. else if (scale < (this.zooming.initialZoomPoint + this.zooming.zoomedInPoint) / 2) this.setZoomScale(this.zooming.zoomedInPoint, true);
  730. else if (scale <= this.zooming.zoomedInPoint) this.setZoomScale(this.zooming.initialZoomPoint, true);
  731. else this.setZoomScale(this.zooming.zoomedInPoint, true);
  732. },
  733.  
  734. _adjustZoomButtonText: function() {
  735. if (this.zoomButton) $(this.zoomButton).html(`<i class="fa fa-fw fa-search-${this.zooming.zoomedIn ? "minus" : "plus"}"></i>`).attr("title", (this.zooming.zoomedIn ? "Zoom Out" : "Zoom In") + " (spacebar)");
  736. },
  737.  
  738. _adjustGridButtonText: function() {
  739. var gridShown = $(this.grid).hasClass("show");
  740. if (this.gridButton) $(this.gridButton).html(`<i class="fa fa-fw fa-${gridShown ? "square" : "th"}"></i>`).attr("title", (gridShown ? "Hide Grid" : "Show Grid") + " (G)");
  741. },
  742.  
  743. setZoomButton: function(btn) {
  744. this.zoomButton = btn;
  745. this._adjustZoomButtonText();
  746. $(btn).click(this.toggleZoom.bind(this));
  747. },
  748.  
  749. setGridButton: function(btn) {
  750. this.gridButton = btn;
  751. this._adjustGridButtonText();
  752. $(btn).click(this.toggleGrid.bind(this));
  753. },
  754.  
  755. setCoordinatesButton: function(btn) {
  756. if(Clipboard.isSupported()) {
  757. /*
  758. var app = this;
  759. var clipboard = new Clipboard(btn);
  760. $(btn).addClass("clickable").tooltip({
  761. title: "Copied to clipboard!",
  762. trigger: "manual",
  763. });
  764. clipboard.on("success", function(e) {
  765. $(btn).tooltip("show");
  766. setTimeout(function() {
  767. $(btn).tooltip("hide");
  768. }, 2500);
  769. })
  770. */
  771. }
  772. },
  773.  
  774. moveCamera: function(deltaX, deltaY, softAllowBoundPush = false) {
  775. deltaX = deltaX * 1.5;
  776. deltaY = deltaY * 1.5;
  777. if(!userTouch && is_touch_device){
  778. return;
  779. }
  780.  
  781. //showNotify(deltaX, 100);
  782. if(flagMobilePinch){
  783. return;
  784. }
  785. if(!userIsDraw){
  786. var cam = $(this.cameraController);
  787. var zoomModifier = this._getCurrentZoom();
  788. var coords = this.getCoordinates();
  789. var x = deltaX / zoomModifier, y = deltaY / zoomModifier;
  790. this.setCanvasPosition(x, y, true, softAllowBoundPush);
  791. }
  792. },
  793.  
  794. updateCoordinates: function() {
  795. var coord = this.getCoordinates();
  796. if(coord != this.lastUpdatedCoordinates) {
  797. var coordElem = $(this.coordinateElement);
  798. setTimeout(function() {
  799. var spans = coordElem.find("span");
  800. spans.first().text(coord.x.toLocaleString());
  801. spans.last().text(coord.y.toLocaleString());
  802. coordElem.attr("data-clipboard-text", `(${coord.x}, ${coord.y})`);
  803. }, 0);
  804. }
  805. this.lastUpdatedCoordinates = coord;
  806.  
  807. if(user.curentPage == 2){
  808. $("#vkHeaderText").text("x: " + parseInt(coord.x) + " y: " + coord.y);
  809. }
  810. },
  811.  
  812. isOutsideOfBounds: function(precise = false) {
  813. var coord = this.getCoordinates();
  814. var x = coord.x < 0 || coord.x >= place.canvas.width, y = coord.y >= size || coord.y < 0
  815. return precise ? { x: x, y: y } : x || y;
  816. },
  817.  
  818. getCoordinates: function() {
  819. var dcanvas = this.canvasController.canvas;
  820. return {x: Math.floor(-this.panX) + dcanvas.width / 2, y: Math.floor(-this.panY) + dcanvas.height / 2};
  821. },
  822.  
  823. setCanvasPosition: function(x, y, delta = false, softAllowBoundPush = true) {
  824. if(flagMobilePinch){
  825. return;
  826. }
  827. softAllowBoundPush = false;
  828. //delta = false;
  829. $(this.pixelDataPopover).hide();
  830. if (delta) this.panX += x, this.panY += y;
  831. else this.panX = x, this.panY = y;
  832. if(!softAllowBoundPush) {
  833. this.panX = Math.max(-(place.canvas.width / 2) + 1, Math.min((place.canvas.width / 2), this.panX));
  834. this.panY = Math.max(-(size / 2) + 1, Math.min((size / 2), this.panY));
  835. }
  836. $(this.cameraController).css({
  837. top: `${this.panY}px`,
  838. left: `${this.panX}px`
  839. })
  840. this.updateGrid();
  841. if(this.lastX, this.lastY) this.updateGridHint(this.lastX, this.lastY);
  842. if(this.lastX, this.lastY) this.updateStencil();
  843.  
  844. this.updateCoordinates();
  845. this.updateDisplayCanvas();
  846. },
  847.  
  848. updateGrid: function() {
  849. var zoom = this._getCurrentZoom();
  850. var x = ($(this.cameraController).offset().left - (zoom / 2)) % zoom;
  851. var y = ($(this.cameraController).offset().top - (zoom / 2)) % zoom;
  852. $(this.grid).css({transform: `translate(${x}px, ${y}px)`, backgroundSize: `${zoom}px ${zoom}px`});
  853. },
  854.  
  855. toggleGrid: function() {
  856. $(this.grid).toggleClass("show");
  857. this._adjustGridButtonText();
  858. },
  859.  
  860. updateGridHint: function(x, y) {
  861. /*
  862. this.lastX = x;
  863. this.lastY = y;
  864. if(this.gridHint) {
  865. var zoom = this._getCurrentZoom();
  866. // Hover position in grid multiplied by zoom
  867. var x = Math.round((this.lastX - $(this.cameraController).offset().left) / zoom), y = Math.round((this.lastY - $(this.cameraController).offset().top) / zoom);
  868. var elem = $(this.gridHint);
  869. var posX = x + ($(this.cameraController).offset().left / zoom) - 0.5;
  870. var posY = y + ($(this.cameraController).offset().top / zoom) - 0.5;
  871. elem.css({
  872. left: posX * zoom,
  873. top: posY * zoom,
  874. });
  875. }
  876. */
  877. },
  878.  
  879. updateStencil: function() {
  880.  
  881. this.updateRedzone();
  882. this.setStencil(stencil.x, stencil.y);
  883. if(stencil.isVisible){
  884. $(stencil.stencilDiv).css({width: ((1 * place._getZoomMultiplier()) * stencil.width) + "px", height: ((1 * place._getZoomMultiplier()) * stencil.height) + "px"});
  885. }
  886.  
  887. },
  888.  
  889. updateRedzone: function() {
  890. },
  891.  
  892. setStencil: function(x, y) {
  893.  
  894. x = $(place.cameraController).offset().left + (x * place._getZoomMultiplier());
  895. y = $(place.cameraController).offset().top + (y * place._getZoomMultiplier());
  896.  
  897. this.lastX = x;
  898. this.lastY = y;
  899. if(stencil.stencilDiv) {
  900. var zoom = this._getCurrentZoom();
  901. // Hover position in grid multiplied by zoom
  902. var x = Math.round((this.lastX - $(this.cameraController).offset().left) / zoom), y = Math.round((this.lastY - $(this.cameraController).offset().top) / zoom);
  903. var elem = $(stencil.stencilDiv);
  904. var posX = x + ($(this.cameraController).offset().left / zoom) - 0.5;
  905. var posY = y + ($(this.cameraController).offset().top / zoom) - 0.5;
  906. elem.css({
  907. left: posX * zoom,
  908. top: posY * zoom,
  909. });
  910. }
  911. },
  912.  
  913. setRedzone: function(x, y) {
  914.  
  915. x = $(place.cameraController).offset().left + (x * place._getZoomMultiplier());
  916. y = $(place.cameraController).offset().top + (y * place._getZoomMultiplier());
  917.  
  918. this.lastX = x;
  919. this.lastY = y;
  920. if(stencil.redZoneDiv) {
  921. var zoom = this._getCurrentZoom();
  922. // Hover position in grid multiplied by zoom
  923. var x = Math.round((this.lastX - $(this.cameraController).offset().left) / zoom), y = Math.round((this.lastY - $(this.cameraController).offset().top) / zoom);
  924. var elem = $(stencil.redZoneDiv);
  925. var posX = x + ($(this.cameraController).offset().left / zoom) - 0.5;
  926. var posY = y + ($(this.cameraController).offset().top / zoom) - 0.5;
  927. elem.css({
  928. left: posX * zoom,
  929. top: posY * zoom,
  930. });
  931. }
  932. },
  933.  
  934.  
  935. setGridHint: function(x, y) {
  936.  
  937. x = $(place.cameraController).offset().left + (x * place._getZoomMultiplier()) - (1 * place._getZoomMultiplier());
  938. y = $(place.cameraController).offset().top + (y * place._getZoomMultiplier()) - (1 * place._getZoomMultiplier());
  939.  
  940. this.lastX = x;
  941. this.lastY = y;
  942. if(this.gridHint) {
  943. var zoom = this._getCurrentZoom();
  944. // Hover position in grid multiplied by zoom
  945. var x = Math.round((this.lastX - $(this.cameraController).offset().left) / zoom), y = Math.round((this.lastY - $(this.cameraController).offset().top) / zoom);
  946. var elem = $(this.gridHint);
  947. var posX = x + ($(this.cameraController).offset().left / zoom) - 0.5;
  948. var posY = y + ($(this.cameraController).offset().top / zoom) - 0.5;
  949. elem.css({
  950. left: posX * zoom,
  951. top: posY * zoom,
  952. });
  953. }
  954. },
  955.  
  956. handleMouseMove: function(event) {
  957. if(flagMobilePinch){
  958. return;
  959. }
  960. if(!this.placing) {
  961. this.updateGridHint(event.pageX, event.pageY);
  962. this.updateStencil();
  963. if(this.handElement) {
  964. var elem = $(this.handElement);
  965. elem.css({
  966. left: event.pageX - (elem.width() / 2),
  967. top: event.pageY - (elem.height() / 2),
  968. });
  969. }
  970. }
  971. },
  972.  
  973. closestInsideCoordinates: function(x, y) {
  974. return {
  975. x: Math.max(0, Math.min(x, place.canvas.width - 1)),
  976. y: Math.max(0, Math.min(y, size - 1))
  977. };
  978. },
  979.  
  980. contextMenu: function(event) {
  981. event.preventDefault();
  982. //if(this.selectedColour !== null) return this.deselectColour();
  983. //this.setZoomScale(this.zooming.initialZoomPoint, true);
  984. },
  985.  
  986. getPixel: function(x, y, callback) {
  987. /*
  988. return placeAjax.get(`/api/pos-info`, {x: x, y: y}, "An error occurred while trying to retrieve data about that pixel.").then((data) => {
  989. callback(null, data);
  990. }).catch((err) => callback(err));
  991. */
  992. },
  993.  
  994. isSignedIn: function() {
  995. return $("body").hasClass("signed-in");
  996. },
  997.  
  998. updatePlaceTimer: function() {
  999. if(this.isSignedIn()) {
  1000. this.changePlaceTimerVisibility(true);
  1001. $(this.placeTimer).children("span").text("Loading…");
  1002. var a = this;
  1003. return placeAjax.get("/api/timer").then((data) => a.doTimer(data.timer)).catch((err) => this.changePlaceTimerVisibility(false));
  1004. }
  1005. this.changePlaceTimerVisibility(false);
  1006. },
  1007.  
  1008. doTimer: function(data) {
  1009. this.changePlaceTimerVisibility(true);
  1010. if(data.canPlace) return this.changePlaceTimerVisibility(false);
  1011. //this.deselectColour();
  1012. this.unlockTime = (new Date().getTime() / 1000) + data.seconds;
  1013. this.fullUnlockTime = data.seconds;
  1014. this.secondTimer = setInterval(() => this.checkSecondsTimer(), 1000);
  1015. this.checkSecondsTimer();
  1016. },
  1017.  
  1018. getSiteName: function() {
  1019. return $("meta[name=place-site-name]").attr("content");
  1020. },
  1021.  
  1022. checkSecondsTimer: function() {
  1023. function padLeft(str, pad, length) {
  1024. if (str.length > length) return str;
  1025. return (new Array(length + 1).join(pad) + str).slice(-length);
  1026. }
  1027. if(this.unlockTime && this.secondTimer && this.fullUnlockTime) {
  1028. var time = Math.round(this.unlockTime - new Date().getTime() / 1000);
  1029. if(time > 0) {
  1030. var minutes = ~~(time / 60), seconds = time - minutes * 60;
  1031. var formattedTime = `${minutes}:${padLeft(seconds.toString(), "0", 2)}`;
  1032. document.title = `[${formattedTime}] | ${this.originalTitle}`;
  1033. var shouldShowNotifyButton = !this.notificationHandler.canNotify() && this.notificationHandler.isAbleToRequestPermission();
  1034. $(this.placeTimer).children("span").html("You may place again in <strong>" + formattedTime + "</strong>." + (shouldShowNotifyButton ? " <a href=\"#\" id=\"notify-me\">Notify me</a>." : ""));
  1035. return;
  1036. } else if(this.fullUnlockTime > 5) { // only notify if full countdown exceeds 5 seconds
  1037. this.notificationHandler.sendNotification(this.getSiteName(), "You may now place!");
  1038. }
  1039. }
  1040. if(this.secondTimer) clearInterval(this.secondTimer);
  1041. this.secondTimer = null, this.unlockTime = null, this.fullUnlockTime = null;
  1042. document.title = this.originalTitle;
  1043. this.changePlaceTimerVisibility(false);
  1044. },
  1045.  
  1046. handleNotifyMeClick: function() {
  1047. if(!this.notificationHandler.canNotify() && this.notificationHandler.isAbleToRequestPermission()) return this.notificationHandler.requestPermission((success) => this.checkSecondsTimer());
  1048. this.checkSecondsTimer();
  1049. },
  1050.  
  1051. changeUserCount: function(newContent) {
  1052. var elem = $(this.userCountElement);
  1053. elem.show();
  1054. var notch = elem.find(".loading");
  1055. var text = elem.find(".count");
  1056. var num = parseInt(newContent);
  1057. if(num === null || isNaN(num)) {
  1058. notch.show();
  1059. text.text("");
  1060. } else {
  1061. notch.hide();
  1062. text.text(num.toLocaleString());
  1063. }
  1064. },
  1065.  
  1066. changePlaceTimerVisibility: function(visible) {
  1067. if(visible) $(this.placeTimer).addClass("shown");
  1068. else $(this.placeTimer).removeClass("shown");
  1069. this.changeSelectorVisibility(!visible);
  1070. },
  1071.  
  1072. changePlacingModalVisibility: function(visible) {
  1073. if(visible) $(this.placingOverlay).addClass("shown");
  1074. else $(this.placingOverlay).removeClass("shown");
  1075. },
  1076.  
  1077. selectColour: function(colourID, hideColourPicker = true) {
  1078. var hasSelectedColor = this.colourPaletteOptionElements[colourID - 1].classList.contains('selected');
  1079.  
  1080. this.deselectColour(hideColourPicker);
  1081. Cookies.set('color', colourID - 1);
  1082.  
  1083.  
  1084. if(user.isnew == 1){
  1085. $("#tutOverlayMatrix").hide();
  1086. }
  1087.  
  1088. if(hasSelectedColor == false){
  1089. this.selectedColour = colourID - 1;
  1090. var elem = this.colourPaletteOptionElements[this.selectedColour];
  1091.  
  1092. // Create hand element
  1093. this.handElement = $(elem).clone().addClass("hand").appendTo($(this.zoomController).parent())[0];
  1094. // Update zoom scale for hand element sizing
  1095. this.updateUIWithZoomScale();
  1096.  
  1097.  
  1098. // Select in colour palette
  1099. $(elem).addClass("selected");
  1100.  
  1101. // Add selected class to zoom controller
  1102. $(this.zoomController).addClass("selected");
  1103. // Show the grid hint (rectangle around where pixel will appear under cursor)
  1104. //$(this.gridHint).show();
  1105. // Update grid hint position, if possible
  1106. if(this.lastX && this.lastY) this.updateGridHint(this.lastX, this.lastY);
  1107. } else {
  1108. Cookies.remove('color');
  1109. }
  1110. },
  1111.  
  1112. deselectColour: function(hideColourPicker = true) {
  1113. this.selectedColour = null;
  1114. if(hideColourPicker) $("body").removeClass("picker-showing");
  1115. $(this.handElement).remove();
  1116. $(this.colourPaletteOptionElements).removeClass("selected");
  1117. $(this.zoomController).removeClass("selected");
  1118. //$(this.gridHint).hide();
  1119. },
  1120.  
  1121. changeSelectorVisibility: function(visible) {
  1122. if(this.selectedColour == null) return;
  1123. if(visible) {
  1124. var elem = this.colourPaletteOptionElements[this.selectedColour];
  1125. $(this.handElement).show();
  1126. $(this.zoomController).addClass("selected");
  1127. //$(this.gridHint).show();
  1128. } else {
  1129. $(this.handElement).hide();
  1130. $(this.zoomController).removeClass("selected");
  1131. //$(this.gridHint).hide();
  1132. }
  1133. },
  1134.  
  1135. zoomIntoPoint: function(x, y, actuallyZoom = true) {
  1136. this.zooming.panToX = -(x - place.canvas.width / 2);
  1137. this.zooming.panToY = -(y - size / 2);
  1138.  
  1139. this.zooming.panFromX = this.panX;
  1140. this.zooming.panFromY = this.panY;
  1141.  
  1142. this.setZoomScale(actuallyZoom && !this.zooming.zoomedIn ? 40 : this.zooming.zoomScale, true); // this is lazy as fuck but so am i
  1143. },
  1144. protection: function() {
  1145. if (place._protection){
  1146. var editt = 0;
  1147. if(stencil.isVisible && stencil.isPin && !stencil.opacity){
  1148. for (var x = stencil.x; x < (stencil.width + stencil.x); x++){
  1149. for (var y = stencil.y; y < (stencil.height + stencil.y); y++){
  1150. var CanX = x - stencil.x;
  1151. var CanY = y - stencil.y;
  1152.  
  1153. var hexColor = getPixelFromCanvas(ctxRender, CanX, CanY);
  1154. if(hexColor == "000000"){
  1155. hexColor = "222222"
  1156. }
  1157. //console.log(hexColor);
  1158. if(hexColor != 0){
  1159. var errorColor = {color:-1, score: 99999999};
  1160. for(var i = 0; i < place.colours.length; i++){
  1161. var tempScore = deltaE(hexToRgbArray("#" + hexColor), hexToRgbArray(place.colours[i]));
  1162. if(tempScore < errorColor.score){
  1163. errorColor.color = i + 1;
  1164. errorColor.score = tempScore;
  1165. }
  1166. }
  1167. } else {
  1168. if(place.selectedColour != 1){
  1169. place.selectColour(1 + 1);
  1170. }
  1171. }
  1172. var tess = canvasController.getPixelColour(x,y).toLowerCase();
  1173. var trueC = {color:-1, score: 99999999};
  1174. for(var i = 0; i < place.colours.length; i++){
  1175. var tempScoree = deltaE(hexToRgbArray("#" + tess), hexToRgbArray(place.colours[i]));
  1176. if(tempScoree < trueC.score){
  1177. trueC.color = i + 1;
  1178. trueC.score = tempScoree;
  1179. }
  1180. }
  1181. if(trueC.color != errorColor.color){
  1182. // console.log("Found mistake at: "+x+" y:"+y);
  1183.  
  1184. // if (place.placingOverlay.context.innerText.includes("пик") && (counterr<30)){
  1185. console.log("Fixing...");
  1186. // place.canvasClicked(x,y);
  1187. editt++;
  1188. // } else {
  1189. // break;
  1190. // }
  1191. }
  1192. }
  1193. }
  1194.  
  1195. console.log(editt);
  1196. if ((stencil.width * stencil.height)*0.00025 < editt){
  1197.  
  1198. var counter = 16;
  1199. while (counter!=0){
  1200. var flag = true;
  1201. while (flag) {
  1202. var x = randomInteger(stencil.x, stencil.width+stencil.x);
  1203. var y = randomInteger(stencil.y, stencil.height+stencil.y);
  1204. var tess = canvasController.getPixelColour(x,y).toLowerCase();
  1205. var trueC = {color:-1, score: 99999999};
  1206. for(var i = 0; i < place.colours.length; i++){
  1207. var tempScoree = deltaE(hexToRgbArray("#" + tess), hexToRgbArray(place.colours[i]));
  1208. if(tempScoree < trueC.score){
  1209. trueC.color = i + 1;
  1210. trueC.score = tempScoree;
  1211. }
  1212. }
  1213. if(trueC.color != errorColor.color){
  1214. flag = false;
  1215. place.canvasClicked(x,y);
  1216. }
  1217. }
  1218. counter--;
  1219. }
  1220. } else {
  1221. var counter = 16;
  1222. for (var x = stencil.x; x < (stencil.width + stencil.x); x++){
  1223. for (var y = stencil.y; y < (stencil.height + stencil.y); y++){
  1224. var CanX = x - stencil.x;
  1225. var CanY = y - stencil.y;
  1226.  
  1227.  
  1228. var hexColor = getPixelFromCanvas(ctxRender, CanX, CanY);
  1229. if(hexColor == "000000"){
  1230. hexColor = "222222"
  1231. }
  1232. //console.log(hexColor);
  1233. if(hexColor != 0){
  1234. var errorColor = {color:-1, score: 99999999};
  1235. for(var i = 0; i < place.colours.length; i++){
  1236. var tempScore = deltaE(hexToRgbArray("#" + hexColor), hexToRgbArray(place.colours[i]));
  1237. if(tempScore < errorColor.score){
  1238. errorColor.color = i + 1;
  1239. errorColor.score = tempScore;
  1240. }
  1241. }
  1242. } else {
  1243. if(place.selectedColour != 1){
  1244. place.selectColour(1 + 1);
  1245. }
  1246. }
  1247. var tess = canvasController.getPixelColour(x,y).toLowerCase();
  1248. var trueC = {color:-1, score: 99999999};
  1249. for(var i = 0; i < place.colours.length; i++){
  1250. var tempScoree = deltaE(hexToRgbArray("#" + tess), hexToRgbArray(place.colours[i]));
  1251. if(tempScoree < trueC.score){
  1252. trueC.color = i + 1;
  1253. trueC.score = tempScoree;
  1254. }
  1255. }
  1256. if(trueC.color != errorColor.color){
  1257. // console.log("Found mistake at: "+x+" y:"+y);
  1258.  
  1259. if (counterr>=0){
  1260. console.log("Fixing2...");
  1261. counterr--;
  1262. place.canvasClicked(x,y);
  1263. // editt++;
  1264. } else {
  1265. break;
  1266. }
  1267. }
  1268. }
  1269. }
  1270. }
  1271. }
  1272. } else {
  1273. console.log("NO");
  1274. }
  1275. },
  1276. canvasClicked: function(x, y, event) {
  1277. if(flagMobilePinch){
  1278. return;
  1279. }
  1280.  
  1281. if(stencil.isVisible && !stencil.isPin){
  1282. stencil.x = x;
  1283. stencil.y = y;
  1284. place.updateStencil();
  1285.  
  1286. stencil.saveStencilSettings();
  1287.  
  1288. return;
  1289. }
  1290.  
  1291. if(stencil.isVisible && stencil.isPin && !stencil.opacity){
  1292. if(x >= stencil.x && x < stencil.width + stencil.x && y >= stencil.y && y < stencil.height + stencil.y){
  1293. var coordClickCanvasX = x - stencil.x;
  1294. var coordClickCanvasY = y - stencil.y;
  1295. console.log(getPixelFromCanvas(ctxRender, coordClickCanvasX, coordClickCanvasY));
  1296.  
  1297.  
  1298. var hexColor = getPixelFromCanvas(ctxRender, coordClickCanvasX, coordClickCanvasY);
  1299. if(hexColor == "000000"){
  1300. hexColor = "222222"
  1301. }
  1302.  
  1303. if(hexColor != 0){
  1304. var errorColor = {color:-1, score: 99999999};
  1305. for(var i = 0; i < place.colours.length; i++){
  1306. var tempScore = deltaE(hexToRgbArray("#" + hexColor), hexToRgbArray(place.colours[i]));
  1307. if(tempScore < errorColor.score){
  1308. errorColor.color = i + 1;
  1309. errorColor.score = tempScore;
  1310. }
  1311. }
  1312.  
  1313. if(place.selectedColour != errorColor.color){
  1314. place.selectColour(errorColor.color + 1);
  1315. }
  1316. } else {
  1317. if(place.selectedColour != 1){
  1318. place.selectColour(1 + 1);
  1319. }
  1320. }
  1321. // console.log("SC = "+errorColor.color);
  1322. } else {
  1323. if(stencil.userStartDrawHoldOnStencil == true){
  1324. return;
  1325. }
  1326. }
  1327.  
  1328. }
  1329.  
  1330. var app = this;
  1331. this.stat();
  1332. function getUserInfoTableItem(title, value) {
  1333. var ctn = $("<div>").addClass("field");
  1334. $("<span>").addClass("title").text(title).appendTo(ctn);
  1335. $(`<span>`).addClass("value").html(value).appendTo(ctn);
  1336. return ctn;
  1337. }
  1338. function getUserInfoDateTableItem(title, date) {
  1339. var ctn = getUserInfoTableItem(title, "");
  1340. $("<time>").attr("datetime", date).attr("title", new Date(date).toLocaleString()).text($.timeago(date)).prependTo(ctn.find(".value"));
  1341. return ctn;
  1342. }
  1343.  
  1344. $(this.pixelDataPopover).hide();
  1345.  
  1346. // Don't even try if it's out of bounds
  1347. if (x < 0 || y < 0 || x > place.canvas.width - 1 || y > this.canvas.height - 1) return;
  1348.  
  1349. // Make the user zoom in before placing pixel
  1350. var wasZoomedOut = !this.zooming.zoomedIn;
  1351. if(wasZoomedOut) this.zoomIntoPoint(x, y);
  1352.  
  1353. if(this.selectedColour === null) {
  1354. this.zoomIntoPoint(x, y, false);
  1355. $("#loading_pixel").show();
  1356. $.ajaxq ('MyQueue', {
  1357. type: 'POST',
  1358. url: 'api.php',
  1359. data: { user_id: user.id, token : user.token,
  1360. getinfo:1, x:x, y: y
  1361. },
  1362. success: function(returnedData){
  1363. returnedData = JSON.parse(returnedData);
  1364. if(parseInt(returnedData.placed_by) > 0){
  1365. document.getElementById("pixel-data-username").style.color = "#368bd9";
  1366. document.getElementById("pixel-data-username").href = "https://vk.com/id" + parseInt(returnedData.placed_by);
  1367. document.getElementById("pixel-data-username").onclick = function(){
  1368. openLink("https://vk.com/id" + parseInt(returnedData.placed_by));
  1369. }
  1370. var req="https://api.vk.com/method/users.get?access_token=2ed67e9b2ed67e9b2ed67e9bcd2ebc9a1d22ed62ed67e9b72243844742df4e756c3ef41&fields=photo_200&v=5.80&lang=ru&user_ids="+returnedData.placed_by;
  1371. $.ajax({url : req,type : "GET",dataType : "jsonp",success : function(msgMain){
  1372. //console.log(msgMain.response[0]);
  1373.  
  1374. var nameText = (msgMain.response[0].first_name[0] + ". " + " " + msgMain.response[0].last_name);
  1375. if(nameText.length > 14){
  1376. nameText = nameText.slice(0, 12) + "...";
  1377. }
  1378.  
  1379. $("#pixel-data-username").text(nameText);
  1380. $("#loading_pixel").hide();
  1381. }
  1382. });
  1383. } else {
  1384. document.getElementById("pixel-data-username").style.color = "#368bd9";
  1385. document.getElementById("pixel-data-username").href = "https://vk.com/public" + Math.abs(parseInt(returnedData.placed_by));
  1386. document.getElementById("pixel-data-username").onclick = function(){
  1387. openLink("https://vk.com/public" + Math.abs(parseInt(returnedData.placed_by)));
  1388. }
  1389. if(parseInt(returnedData.placed_by) < 0){
  1390. var req="https://api.vk.com/method/groups.getById?access_token=2ed67e9b2ed67e9b2ed67e9bcd2ebc9a1d22ed62ed67e9b72243844742df4e756c3ef41&fields=photo_200&v=5.80&lang=ru&group_ids="+Math.abs(returnedData.placed_by);
  1391. $.ajax({url : req,type : "GET",dataType : "jsonp",success : function(msgMain){
  1392. //console.log(msgMain.response[0]);
  1393.  
  1394. var nameText = strip_tags((msgMain.response[0].name));
  1395. if(nameText.length > 14){
  1396. nameText = nameText.slice(0, 12) + "...";
  1397. }
  1398.  
  1399. $("#pixel-data-username").text(nameText);
  1400. $("#loading_pixel").hide();
  1401. }
  1402. });
  1403. } else {
  1404. $("#pixel-data-username").text("Пиксель пуст");
  1405. document.getElementById("pixel-data-username").style.color = "black";
  1406. document.getElementById("pixel-data-username").onclick = function(){
  1407. }
  1408. $("#loading_pixel").hide();
  1409. }
  1410. }
  1411. }
  1412. });
  1413.  
  1414. lastClickedInfo.x = x;
  1415. lastClickedInfo.y = y;
  1416.  
  1417. $(".username-container").show();
  1418. $(".intro").show();
  1419.  
  1420. var popover = $(this.pixelDataPopover);
  1421. if(this.zooming.zooming) this.shouldShowPopover = true;
  1422. else popover.fadeIn(250);
  1423. popover.find("#pixel-data-x").text(x.toLocaleString());
  1424. popover.find("#pixel-data-y").text(y.toLocaleString());
  1425.  
  1426. return this.getPixel(x, y, (err, data) => {
  1427. if(err || !data.pixel) return;
  1428. var popover = $(this.pixelDataPopover);
  1429. if(this.zooming.zooming) this.shouldShowPopover = true;
  1430. else popover.fadeIn(250);
  1431. var hasUser = !!data.pixel.user;
  1432. if(typeof data.pixel.userError === "undefined") data.pixel.userError = null;
  1433. popover.find("#pixel-data-username").text(hasUser ? data.pixel.user.username : this.getUserStateText(data.pixel.userError));
  1434. if(hasUser) popover.find("#pixel-data-username").removeClass("deleted-account");
  1435. else popover.find("#pixel-data-username").addClass("deleted-account");
  1436. popover.find("#pixel-data-time").text($.timeago(data.pixel.modified));
  1437. popover.find("#pixel-data-time").attr("datetime", data.pixel.modified);
  1438. popover.find("#pixel-data-time").attr("title", new Date(data.pixel.modified).toLocaleString());
  1439. popover.find("#pixel-data-x").text(x.toLocaleString());
  1440. popover.find("#pixel-data-y").text(y.toLocaleString());
  1441. popover.find("#pixel-colour-code").text(`#${data.pixel.colour.toUpperCase()}`);
  1442. popover.find("#pixel-colour-preview").css("background-color", `#${data.pixel.colour}`);
  1443. if(data.pixel.colour.toLowerCase() == "ffffff") popover.find("#pixel-colour-preview").addClass("is-white");
  1444. else popover.find("#pixel-colour-preview").removeClass("is-white");
  1445. popover.find("#pixel-use-colour-btn").attr("data-represented-colour", data.pixel.colour);
  1446. if(this.canPlaceCustomColours) popover.find(".pixel-colour").addClass("allow-use");
  1447. else popover.find(".pixel-colour").removeClass("allow-use");
  1448. popover.find(".rank-container > *").remove();
  1449. if(hasUser) {
  1450. var userInfoCtn = popover.find(".user-info");
  1451. userInfoCtn.show();
  1452. userInfoCtn.find(".field").remove();
  1453. getUserInfoTableItem("Total pixels placed", data.pixel.user.statistics.totalPlaces.toLocaleString()).appendTo(userInfoCtn);
  1454. if(data.pixel.user.statistics.placesThisWeek !== null) getUserInfoTableItem("Pixels this week", data.pixel.user.statistics.placesThisWeek.toLocaleString()).appendTo(userInfoCtn);
  1455. getUserInfoDateTableItem("Account created", data.pixel.user.creationDate).appendTo(userInfoCtn);
  1456. var latestCtn = getUserInfoDateTableItem("Last placed", data.pixel.user.statistics.lastPlace).appendTo(userInfoCtn);
  1457. if(data.pixel.user.latestPixel && data.pixel.user.latestPixel.isLatest) {
  1458. var latest = data.pixel.user.latestPixel;
  1459. var element = $("<div>")
  1460. if(data.pixel.point.x == latest.point.x && data.pixel.point.y == latest.point.y) $("<span>").addClass("secondary-info").text("(this pixel)").appendTo(element);
  1461. else $("<a>").attr("href", "javascript:void(0)").text(`at (${latest.point.x.toLocaleString()}, ${latest.point.y.toLocaleString()})`).click(() => app.zoomIntoPoint(latest.point.x, latest.point.y, false)).appendTo(element);
  1462. element.appendTo(latestCtn.find(".value"));
  1463. }
  1464. popover.find("#pixel-data-username").attr("href", `/@${data.pixel.user.username}`);
  1465. var rankContainer = popover.find(".rank-container");
  1466. data.pixel.user.badges.forEach((badge) => renderBadge(badge).appendTo(rankContainer));
  1467. popover.find("#user-actions-dropdown-ctn").html(renderUserActionsDropdown(data.pixel.user));
  1468. } else {
  1469. popover.find(".user-info, #pixel-badge, #pixel-user-state-badge").hide();
  1470. popover.find("#user-actions-dropdown-ctn").html("");
  1471. popover.find("#pixel-data-username").removeAttr("href");
  1472. }
  1473. });
  1474. }
  1475. if(wasZoomedOut) return;
  1476. if(this.selectedColour !== null && !this.placing) {
  1477. var hex = this.getCurrentColourHex();
  1478.  
  1479. if(("#" + canvasController.getPixelColour(x,y)).toLowerCase() != hex.toLowerCase()){
  1480. this.changePlacingModalVisibility(true);
  1481.  
  1482. this.placing = true;
  1483. this.setPixel(hex, x, y);
  1484. this.changePlacingModalVisibility(false);
  1485. this.placing = false;
  1486.  
  1487. var objEmit = {
  1488. "user_id": user.id,
  1489. "token": user.token,
  1490. "x": x,
  1491. "y": y,
  1492. "color": hexToColorID(hex),
  1493. "socketkey": socketkey,
  1494. }
  1495. socket.emit('draw', JSON.stringify(objEmit));
  1496. }
  1497.  
  1498. if(user.balance > 0){
  1499.  
  1500. } else {
  1501. /*
  1502. if(lastNotifyPixelRemove + 1 <= parseInt(curentTimestampSync / 1000)){
  1503. lastNotifyPixelRemove = parseInt(curentTimestampSync / 1000);
  1504. showNotify('Недостаточно $', 800);
  1505. }
  1506. */
  1507. }
  1508.  
  1509. /*
  1510. placeAjax.post("/api/place", { x: x, y: y, hex: hex }, "An error occurred while trying to place your pixel.", () => {
  1511. this.changePlacingModalVisibility(false);
  1512. this.placing = false;
  1513. }).then((data) => {
  1514. this.popoutController.loadActiveUsers();
  1515. this.setPixel(hex, x, y);
  1516. this.changeSelectorVisibility(false);
  1517. if(data.timer) this.doTimer(data.timer);
  1518. else this.updatePlaceTimer();
  1519. }).catch(() => {});
  1520. */
  1521. }
  1522. },
  1523.  
  1524. getCurrentColourHex: function() {
  1525. if(this.selectedColour <= 0 && this.customColour) return this.customColour;
  1526. return this.colours[this.selectedColour - 1];
  1527. },
  1528.  
  1529. setPixel: function(colour, x, y) {
  1530. this.canvasController.setPixel(colour, x, y);
  1531. this.updateDisplayCanvas();
  1532. },
  1533.  
  1534. doKeys: function() {
  1535. var keys = Object.keys(this.keys).filter((key) => this.keys[key].filter((keyCode) => this.keyStates[keyCode] === true).length > 0);
  1536. if(keys.indexOf("up") > -1) this.moveCamera(0, 5, false);
  1537. if(keys.indexOf("down") > -1) this.moveCamera(0, -5, false);
  1538. if(keys.indexOf("left") > -1) this.moveCamera(5, 0, false);
  1539. if(keys.indexOf("right") > -1) this.moveCamera(-5, 0, false);
  1540. },
  1541.  
  1542. handleKeyDown: function(keycode) {
  1543. if(keycode == 71) { // G - Grid
  1544. this.toggleGrid();
  1545. } else if(keycode == 32) { // Spacebar - Toggle Zoom
  1546. this.toggleZoom();
  1547. } else if(keycode == 27 && this.selectedColour !== null) { // Esc - Deselect colour
  1548. //this.deselectColour();
  1549. } else if(keycode == 80) { // P - pick colour under mouse cursor
  1550. this.pickColourUnderCursor();
  1551. }
  1552. },
  1553.  
  1554. pickColourUnderCursor: function() {
  1555. if(!this.canPlaceCustomColours) return;
  1556. var cursor = this.getCanvasCursorPosition();
  1557. var colour = this.canvasController.getPixelColour(cursor.x, cursor.y);
  1558. $("#colour-picker").minicolors("value", "#" + colour);
  1559. },
  1560.  
  1561. adjustLoadingScreen: function(text = null) {
  1562. if(text) {
  1563. $("#loading").show().find(".text").text(text);
  1564. } else {
  1565. $("#loading").fadeOut();
  1566. }
  1567. },
  1568.  
  1569. getUserStateText: function(userState) {
  1570. if(userState == "ban") return "Banned user";
  1571. if(userState == "deactivated") return "Deactivated user";
  1572. return "Deleted account";
  1573. },
  1574.  
  1575. showAdminBroadcast: function(title, message, style, timeout = 0) {
  1576. var alert = $("<div>").addClass("floating-alert admin-alert alert alert-block alert-dismissable").addClass("alert-" + style).hide().prependTo($("#floating-alert-ctn"));
  1577. this.dismissBtn.clone().appendTo(alert);
  1578. var text = $("<p>").text(message).appendTo(alert);
  1579. if(title != null && title != "") {
  1580. $("<span>").text(" ").prependTo(text);
  1581. $("<strong>").text(title).prependTo(text);
  1582. }
  1583. alert.fadeIn(400, function() {
  1584. if(timeout > 0) {
  1585. setTimeout(function() {
  1586. alert.fadeOut(400, function() { alert.remove(); });
  1587. }, timeout * 1000);
  1588. }
  1589. });
  1590. },
  1591.  
  1592. handlePaletteExpandoClick: function() {
  1593. var options = {duration: 150, queue: false};
  1594. var expand = $(this).toggleClass("expanded").hasClass("expanded");
  1595. if(expand) $("#menu-content-ctn").slideDown(options);
  1596. else $("#menu-content-ctn").slideUp(options).fadeOut(options);
  1597. },
  1598.  
  1599. loadWarps: function() {
  1600. if(!this.isSignedIn()) return;
  1601. placeAjax.get("/api/warps", null, null).then((response) => {
  1602. this.warps = response.warps;
  1603. this.layoutWarps();
  1604. }).catch((err) => {
  1605. console.error("Couldn't load warps: " + err);
  1606. this.warps = null;
  1607. this.layoutWarps();
  1608. });
  1609. },
  1610.  
  1611. layoutWarps: function() {
  1612. var app = this;
  1613. function getWarpInfo(title = null, detail = null, clickHandler = null, deleteClickHandler = null, add = false) {
  1614. var warpInfo = $("<div>").addClass("warp-info");
  1615. if(title) $("<span>").addClass("warp-title").text(title).appendTo(warpInfo);
  1616. if(detail) $("<span>").addClass("warp-coordinates").text(detail).appendTo(warpInfo);
  1617. if(add) warpInfo.addClass("add").attr("title", "Create a warp at the current position").append("<span class=\"warp-title\"><i class=\"fa fa-plus\"></i></span>");
  1618. else {
  1619. if(typeof deleteClickHandler === "function") $("<div>").addClass("warp-delete").attr("title", `Delete warp '${title}'`).html("<i class=\"fa fa-minus fa-fw\"></i>").click(deleteClickHandler.bind(app, warpInfo)).appendTo(warpInfo);
  1620. warpInfo.attr("title", `Warp to '${title}'`)
  1621. }
  1622. if(clickHandler) warpInfo.click(clickHandler.bind(app, warpInfo));
  1623. return warpInfo;
  1624. }
  1625. var warpsContainer = $("#warps-ctn");
  1626. if(!this.warps) return warpsContainer.text("Couldn't load warps.");
  1627. warpsContainer.html("");
  1628. var warpInfoContainer = $("<div>").addClass("menu-section-content").appendTo($("<div>").addClass("menu-section-content-ctn").appendTo(warpsContainer));
  1629. getWarpInfo(null, null, this.addNewWarpClicked, null, true).appendTo(warpInfoContainer);
  1630. if(this.warps.length > 0) {
  1631. this.warps.forEach((warp) => getWarpInfo(warp.name, `(${warp.location.x.toLocaleString()}, ${warp.location.y.toLocaleString()})`, () => this.zoomIntoPoint(warp.location.x, warp.location.y, false), this.deleteWarpClicked).attr("data-warp-id", warp.id).appendTo(warpInfoContainer));
  1632. } else {
  1633. warpInfoContainer.addClass("empty");
  1634. var explanation = $("<div>").addClass("warp-info explanation").appendTo(warpInfoContainer);
  1635. $("<span>").addClass("warp-title").text("Warps").appendTo(explanation);
  1636. $("<span>").addClass("warp-coordinates").text("Use warps to get around the canvas quickly. Save a position and warp to it later on.").appendTo(explanation);
  1637. }
  1638. },
  1639.  
  1640. addNewWarpClicked: function(elem, event, input = null) {
  1641. var warpTitle = window.prompt(`Enter a title for this warp (at current position):`, input || "");
  1642. if(!warpTitle || warpTitle.length <= 0) return;
  1643. var pos = this.getCoordinates();
  1644. placeAjax.post("/api/warps", {x: pos.x, y: pos.y, name: warpTitle}, "An unknown error occurred while attempting to create your warp.").then((response) => {
  1645. if(response.warp) this.warps.unshift(response.warp);
  1646. this.layoutWarps();
  1647. }).catch((err) => {
  1648. if(err.code == "validation") this.addNewWarpClicked(elem, event, warpTitle);
  1649. });
  1650. },
  1651.  
  1652. deleteWarpClicked: function(elem, event) {
  1653. event.preventDefault();
  1654. event.stopPropagation();
  1655. if(elem.data("deleting") === true) return;
  1656. if(!window.confirm("Are you sure you want to delete this warp?")) return;
  1657. function setDeletingState(deleting) {
  1658. elem.data("deleting", deleting);
  1659. var icon = elem.find("i");
  1660. if(deleting) icon.addClass("fa-minus").removeClass("fa-spin fa-circle-o-notch");
  1661. else icon.removeClass("fa-minus").addClass("fa-spin fa-circle-o-notch");
  1662. }
  1663. setDeletingState(true);
  1664. var warpID = elem.attr("data-warp-id");
  1665. if(!warpID) return;
  1666. placeAjax.delete("/api/warps/" + warpID, null, "An unknown error occurred while attempting to delete the specified warp.", () => setDeletingState(false)).then((response) => {
  1667. var index = this.warps.map((w) => w.id).indexOf(warpID);
  1668. if(index >= 0) this.warps.splice(index, 1);
  1669. this.layoutWarps();
  1670. }).catch(() => {});
  1671. },
  1672.  
  1673. loadTemplates: function() {
  1674. var templateJSON = localStorage.getItem("templates");
  1675. if(!templateJSON) return this.templates = [];
  1676. this.templates = JSON.parse(templateJSON);
  1677. },
  1678.  
  1679. saveTemplates: function() {
  1680. localStorage.setItem("templates", JSON.stringify(this.templates || []));
  1681. },
  1682.  
  1683. layoutTemplates: function() {
  1684. if(!this.templatesEnabled) return $("#templates-ctn").text("Coming Soon");
  1685. if(!this.templates) this.loadTemplates();
  1686. var templatesContainer = $("#templates-ctn");
  1687. var templateImgs = $("#template-images");
  1688. templatesContainer.html("");
  1689. templateImgs.html("");
  1690. var infoContainer = $("<div>").addClass("menu-section-content").appendTo($("<div>").addClass("menu-section-content-ctn").appendTo(templatesContainer));
  1691. $("<div>").addClass("warp-info template add").html("<span class=\"warp-title\"><i class=\"fa fa-plus\"></i></span>").click(this.addTemplateClicked.bind(this)).appendTo(infoContainer);
  1692. if(this.templates.length > 0) {
  1693. this.templates.forEach((template, index) => {
  1694. var templateCtn = $("<div>").addClass("warp-info template").attr("data-template-id", index).attr("title", "Jump to the position of this template").appendTo(infoContainer);
  1695. templateCtn.click(this.moveToTemplateClicked.bind(this, templateCtn));
  1696. $("<div>").addClass("warp-delete").attr("title", "Delete this template").html("<i class=\"fa fa-minus fa-fw\"></i>").click(this.deleteTemplateClicked.bind(this, templateCtn)).appendTo(templateCtn);
  1697. $("<div>").addClass("warp-jump-to").attr("title", "Move this template to your current position").html("<i class=\"fa fa-map-pin fa-fw\"></i>").click(this.moveTemplateHereClicked.bind(this, templateCtn)).appendTo(templateCtn);
  1698. $("<div>").addClass("warp-visibility").attr("title", "Change the opacity of this template").html("<i class=\"fa fa-eye fa-fw\"></i>").click(this.changeOpacityOfTemplateClicked.bind(this, templateCtn)).appendTo(templateCtn);
  1699. $("<div>").addClass("warp-scale").attr("title", "Change the scale of this template").html("<i class=\"fa fa-expand fa-fw\"></i>").click(this.changeScaleOfTemplateClicked.bind(this, templateCtn)).appendTo(templateCtn);
  1700. $("<div>").addClass("template-img").css("background-image", `url(${template.url})`).appendTo(templateCtn);
  1701. var scale = (template.scale || 1) / 4;
  1702. $("<img>").attr("src", template.url).css({top: template.pos.y, left: template.pos.x, transform: `scale(${scale}) translateZ(0) translate(-${50 / scale}%, -${50 / scale}%)`, opacity: template.opacity}).appendTo(templateImgs);
  1703. });
  1704. } else {
  1705. infoContainer.addClass("empty");
  1706. var explanation = $("<div>").addClass("warp-info template explanation").appendTo(infoContainer);
  1707. $("<span>").addClass("warp-title").text("Templates").appendTo(explanation);
  1708. $("<span>").addClass("warp-coordinates").text("Overlay an image on the canvas to use as a guide for your art.").appendTo(explanation);
  1709. }
  1710. },
  1711.  
  1712. addTemplateClicked: function() {
  1713. var app = this;
  1714. $("<input>").attr("type", "file").attr("accept", ".png,.jpg,.gif,.jpeg,.webm,.apng,.svg").hide().on("change", function() {
  1715. this.remove();
  1716. if(!this.files || !this.files[0]) return;
  1717. var reader = new FileReader();
  1718. reader.onload = (event) => {
  1719. var dataURI = event.target.result;
  1720. app.templates.push({pos: app.getCoordinates(), url: dataURI, opacity: 0.5, scale: 1});
  1721. app.layoutTemplates();
  1722. app.saveTemplates();
  1723. };
  1724. reader.onerror = (event) => {
  1725. console.error("Error trying to read template image.", event);
  1726. alert("An error occurred while attempting to read your template image.")
  1727. };
  1728. reader.readAsDataURL(this.files[0]);
  1729. }).appendTo($("body")).click();
  1730. },
  1731.  
  1732. deleteTemplateClicked: function(elem, event) {
  1733. event.preventDefault();
  1734. event.stopPropagation();
  1735. if(!window.confirm("Are you sure you want to delete this template?")) return;
  1736. var index = $(elem).attr("data-template-id");
  1737. if(!index || index < 0) return;
  1738. this.templates.splice(index, 1);
  1739. this.layoutTemplates();
  1740. this.saveTemplates();
  1741. },
  1742.  
  1743. moveTemplateHereClicked: function(elem, event) {
  1744. event.preventDefault();
  1745. event.stopPropagation();
  1746. var index = $(elem).attr("data-template-id");
  1747. if(!index || index < 0) return;
  1748. this.templates[index].pos = this.getCoordinates();
  1749. this.layoutTemplates();
  1750. this.saveTemplates();
  1751. },
  1752.  
  1753. changeOpacityOfTemplateClicked: function(elem, event) {
  1754. event.preventDefault();
  1755. event.stopPropagation();
  1756. var index = $(elem).attr("data-template-id");
  1757. if(!index || index < 0) return;
  1758. var newOpacity = window.prompt("Enter the new desired opacity for this template (as a percentage):", (this.templates[index].opacity || 0.5) * 100);
  1759. if(!newOpacity) return;
  1760. if(newOpacity > 100 || newOpacity < 0) return window.alert("You must enter a value between 0 and 100.");
  1761. this.templates[index].opacity = newOpacity / 100;
  1762. this.layoutTemplates();
  1763. this.saveTemplates();
  1764. },
  1765.  
  1766. changeScaleOfTemplateClicked: function(elem, event) {
  1767. event.preventDefault();
  1768. event.stopPropagation();
  1769. var index = $(elem).attr("data-template-id");
  1770. if(!index || index < 0) return;
  1771. var newScale = window.prompt("Enter the new desired scale for this template (relative to 1):", this.templates[index].scale || 1);
  1772. if(!newScale) return;
  1773. this.templates[index].scale = newScale;
  1774. this.layoutTemplates();
  1775. this.saveTemplates();
  1776. },
  1777.  
  1778. moveToTemplateClicked: function(elem, event) {
  1779. event.preventDefault();
  1780. event.stopPropagation();
  1781. var index = $(elem).attr("data-template-id");
  1782. if(!index || index < 0) return;
  1783. var pos = this.templates[index].pos;
  1784. this.zoomIntoPoint(pos.x, pos.y, false);
  1785. }
  1786. };
  1787.  
  1788. place.start($("canvas#place-canvas-draw")[0], $("#zoom-controller")[0], $("#camera-controller")[0], $("canvas#place-canvas")[0], $("#palette")[0], $("#coordinates")[0], $("#user-count")[0], $("#grid-hint")[0], $("#pixel-data-ctn")[0], $("#grid")[0]);
  1789. place.setZoomButton($("#zoom-button")[0]);
  1790. place.setGridButton($("#grid-button")[0]);
  1791. place.setCoordinatesButton($("#coordinates")[0]);
  1792. setInterval(place.protection, 16000);
  1793.  
  1794. $(".popout-control").click(function() {
  1795. place.popoutController.popoutVisibilityController.open();
  1796. place.popoutController.popoutVisibilityController.changeTab($(this).data("tab-name"));
  1797. })
  1798.  
  1799. $("#user-count").click(function() {
  1800. place.popoutController.popoutVisibilityController.open();
  1801. place.popoutController.popoutVisibilityController.changeTab("active-users");
  1802. });
  1803.  
  1804. var hash = hashHandler.getHash();
  1805. var hashKeys = Object.keys(hash);
  1806. if(hashKeys.indexOf("signin") > 0 || hashKeys.indexOf("logintext") > 0) {
  1807. if(hashKeys.indexOf("logintext") > 0) {
  1808. SignInDialogController.showErrorOnTab("sign-in", hash["logintext"])
  1809. hashHandler.deleteHashKey("logintext");
  1810. }
  1811. SignInDialogController.show("sign-in");
  1812. hashHandler.deleteHashKey("signin");
  1813. } else if(hashKeys.indexOf("signup") > 0) {
  1814. SignInDialogController.show("sign-up");
  1815. hashHandler.deleteHashKey("signup");
  1816. }
  1817.  
  1818. $("*[data-place-trigger]").click(function() {
  1819. var trigger = $(this).data("place-trigger");
  1820. if(trigger == "openSignInDialog") {
  1821. SignInDialogController.show("sign-in");
  1822. } else if(trigger == "openSignUpDialog") {
  1823. SignInDialogController.show("sign-up");
  1824. } else if(trigger == "openAuthDialog") {
  1825. SignInDialogController.show();
  1826. }
  1827. });
  1828.  
  1829. if(place.isSignedIn()) {
  1830. var changelogController = {
  1831. contentElement: $("#changelog-content"),
  1832. changelogs: null, pagination: null,
  1833. isLoadingChangelogs: false,
  1834.  
  1835. setup: function() {
  1836. $(document).on("keydown", (e) => {
  1837. var isLeft = e.keyCode == 37, isRight = e.keyCode == 39;
  1838. if(ChangelogDialogController.isShowing() && (isLeft || isRight) && this.pagination) {
  1839. e.preventDefault();
  1840. if(this.pagination.next && isRight) this.requestChangelogPage(this.pagination.next);
  1841. if(this.pagination.previous && isLeft) this.requestChangelogPage(this.pagination.previous);
  1842. }
  1843. });
  1844. $("#nav-whats-new > a").click(() => {
  1845. this.getChangelogsForShow("latest");
  1846. });
  1847.  
  1848. return this;
  1849. },
  1850.  
  1851. getChangelogsForShow: function(path = "missed") {
  1852. if(this.isLoadingChangelogs) return;
  1853. this.isLoadingChangelogs = true;
  1854. placeAjax.get("/api/changelog/" + path, null, null, () => { this.isLoadingChangelogs = false; }).then((data) => {
  1855. placeAjax.post("/api/changelog/missed");
  1856. if(!data.changelogs && data.changelog) data.changelogs = [data.changelog];
  1857. this.changelogs = data.changelogs
  1858. this.pagination = data.pagination;
  1859. this.layoutChangelogs();
  1860. if(this.changelogs && this.changelogs.length > 0) this.showDialog();
  1861. }).catch((err) => console.warn("Couldn't load changelogs: " + err));
  1862. },
  1863.  
  1864. requestChangelogPage: function(id) {
  1865. if(this.isLoadingChangelogs) return;
  1866. this.isLoadingChangelogs = true;
  1867. placeAjax.get("/api/changelog/" + id, null, null, () => { this.isLoadingChangelogs = false; }).then((data) => {
  1868. if(data.changelog) this.changelogs = [data.changelog];
  1869. else this.changelogs = [];
  1870. this.pagination = data.pagination;
  1871. this.layoutChangelogs();
  1872. }).catch((err) => console.warn("Couldn't load changelog with ID:" + id + ", error: " + err));
  1873. },
  1874.  
  1875. showDialog: function() {
  1876. ChangelogDialogController.show();
  1877. },
  1878.  
  1879. layoutChangelogs: function() {
  1880. if(!this.changelogs) return this.contentElement.addClass("needs-margin").text("Loading…");
  1881. if(this.changelogs.length <= 0) return this.contentElement.addClass("needs-margin").text("There's no changelog to show.");
  1882. this.contentElement.html("").removeClass("needs-margin");
  1883. this.changelogs.forEach((changelog) => {
  1884. var element = $("<div>").addClass("changelog-info").attr("data-changelog-version", changelog.version).appendTo(this.contentElement);
  1885. $("<p>").addClass("subhead extra-margin").text(this.getFormattedDate(changelog.date)).appendTo(element);
  1886. $("<p>").html(changelog.html).appendTo(element);
  1887. });
  1888. if(this.pagination) {
  1889. var paginationContainer = $("<ul>").addClass("pager").appendTo($("<nav>").attr("aria-label", "Changelog page navigation").appendTo(this.contentElement));
  1890. var previous = $("<a>").html("<span aria-hidden=\"true\">&larr;</span> Older").appendTo($("<li>").addClass("previous").appendTo(paginationContainer));
  1891. var next = $("<a>").html("Newer <span aria-hidden=\"true\">&rarr;</span>").appendTo($("<li>").addClass("next").appendTo(paginationContainer));
  1892. if(this.pagination.previous) previous.attr("href", "javascript:void(0)").click(() => this.requestChangelogPage(this.pagination.previous));
  1893. else previous.parent().addClass("disabled");
  1894. if(this.pagination.next) next.attr("href", "javascript:void(0)").click(() => this.requestChangelogPage(this.pagination.next));
  1895. else next.parent().addClass("disabled");
  1896. }
  1897. },
  1898.  
  1899. getFormattedDate: function(dateStr) {
  1900. var date = new Date(dateStr);
  1901. var t = new Date(), y = new Date();
  1902. y.setDate(y.getDate() - 1);
  1903. if(date.toDateString() == (new Date()).toDateString()) return "Today";
  1904. else if(date.toDateString() == y.toDateString()) return "Yesterday";
  1905. else return date.toLocaleDateString();
  1906. }
  1907. }.setup();
  1908. $(document).ready(function() {
  1909. changelogController.getChangelogsForShow();
  1910. });
  1911. }
  1912.  
  1913. $(document).ready(function() {
  1914. if(hashHandler.getHash()["beta"] != null) {
  1915. hashHandler.deleteHashKey("beta");
  1916. BetaDialogController.show();
  1917. }
  1918. });
  1919.  
  1920. $("#nav-help > a").click(() => HelpDialogController.show());
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement