c80_map 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +10 -0
  3. data/.travis.yml +3 -0
  4. data/CODE_OF_CONDUCT.md +13 -0
  5. data/Gemfile +4 -0
  6. data/LICENSE.txt +21 -0
  7. data/README.md +51 -0
  8. data/Rakefile +1 -0
  9. data/app/admin/c80_map/settings.rb +32 -0
  10. data/app/assets/javascripts/buttons/admin_buttons/button_back_to_map.js +48 -0
  11. data/app/assets/javascripts/buttons/admin_buttons/button_cancel_create.js +21 -0
  12. data/app/assets/javascripts/buttons/admin_buttons/button_complete_create.js +22 -0
  13. data/app/assets/javascripts/buttons/admin_buttons/button_edit.js +80 -0
  14. data/app/assets/javascripts/buttons/admin_buttons/button_new.js +46 -0
  15. data/app/assets/javascripts/buttons/admin_buttons/button_save.js +83 -0
  16. data/app/assets/javascripts/buttons/zoom_buttons.js +61 -0
  17. data/app/assets/javascripts/c80_map.js.coffee +11 -0
  18. data/app/assets/javascripts/events/app_event.js +15 -0
  19. data/app/assets/javascripts/map_objects/area.js +221 -0
  20. data/app/assets/javascripts/map_objects/building.js +309 -0
  21. data/app/assets/javascripts/map_objects/dot.js +14 -0
  22. data/app/assets/javascripts/src/main.js +992 -0
  23. data/app/assets/javascripts/src/state_controller.js +220 -0
  24. data/app/assets/javascripts/src/utils.js +127 -0
  25. data/app/assets/javascripts/svg_elements/helper.js +36 -0
  26. data/app/assets/javascripts/svg_elements/polygon.js +192 -0
  27. data/app/assets/javascripts/view/save_preloader.js +30 -0
  28. data/app/assets/stylesheets/c80_map.scss +2 -0
  29. data/app/assets/stylesheets/map.scss +1435 -0
  30. data/app/assets/stylesheets/view/save_preloader.scss +206 -0
  31. data/app/controllers/c80_map/application_controller.rb +6 -0
  32. data/app/controllers/c80_map/map_ajax_controller.rb +54 -0
  33. data/app/helpers/c80_map/application_helper.rb +33 -0
  34. data/app/models/c80_map/area.rb +5 -0
  35. data/app/models/c80_map/building.rb +73 -0
  36. data/app/models/c80_map/setting.rb +30 -0
  37. data/app/uploaders/c80_map/map_image_uploader.rb +31 -0
  38. data/app/views/c80_map/_map_row.html.erb +15 -0
  39. data/app/views/c80_map/_map_row_index.html.erb +39 -0
  40. data/app/views/c80_map/shared/_save_preloader.html.erb +3 -0
  41. data/bin/console +14 -0
  42. data/bin/setup +7 -0
  43. data/c80_map.gemspec +25 -0
  44. data/config/routes.rb +3 -0
  45. data/db/migrate/20160617130000_create_c80_map_settings.rb +8 -0
  46. data/db/migrate/20160620040202_create_c80_map_areas.rb +9 -0
  47. data/db/migrate/20160620040204_create_c80_map_buildings.rb +8 -0
  48. data/db/seeds/801_fill_map_settings.rb +6 -0
  49. data/lib/c80_map/engine.rb +23 -0
  50. data/lib/c80_map/version.rb +3 -0
  51. data/lib/c80_map.rb +8 -0
  52. metadata +136 -0
@@ -0,0 +1,992 @@
1
+ "use strict";
2
+
3
+ var InitMap = function () {
4
+
5
+ // - to delete start -----------------------------------------------------------------------------------------------------------------------
6
+ var scale = 0.599999;
7
+
8
+ var window_height = $(window).height() - 200;
9
+ if (window_height < 400) window_height = 400;
10
+
11
+ var window_width = $(window).width();
12
+ var image_width = MAP_WIDTH * scale;
13
+ var image_height = MAP_HEIGHT * scale;
14
+
15
+ var x = (window_width - image_width)/2;
16
+ var y = (window_height - image_height)/2;
17
+ // - to delete end -----------------------------------------------------------------------------------------------------------------------
18
+
19
+ $('#map_wrapper').beMap(
20
+ {
21
+ source:LOCS_HASH,
22
+ scale: scale,
23
+ x: x,
24
+ y: y,
25
+ mapwidth: MAP_WIDTH,
26
+ mapheight: MAP_HEIGHT,
27
+ height: window_height
28
+ }
29
+ );
30
+
31
+ };
32
+
33
+ var clog = function () {
34
+ console.log(arguments[0]);
35
+ };
36
+
37
+ (function () {
38
+
39
+ var Map = function () {
40
+ var self = this;
41
+
42
+ self.debug = true;
43
+ self.o = {
44
+ source: 'locations.json', // data
45
+ height: 400, // viewbox height, pixels
46
+ mapwidth: 100, // actual image size, in pixels
47
+ mapheight: 100,
48
+ mapfill: true,
49
+ zoom: true,
50
+ zoombuttons: true,
51
+ maxscale: 1,
52
+ fitscale: 0.51,
53
+ skin: '', // css class name
54
+ scale: 1,
55
+ x: 0,
56
+ y: 0
57
+ };
58
+ self.svg = null;
59
+ self.svg_overlay = null;
60
+ self.container = null;
61
+ self.mode = 'viewing';
62
+ self.prev_mode = null;
63
+ self.setMode = null;
64
+ self.selected_area = null;
65
+ self.drawing_poligon = null;
66
+ self.events = [];
67
+ self.edit_type = null;
68
+ self.new_button_klass = null;
69
+ self.edit_button_klass = null;
70
+ self.complete_creating_button_klass = null;
71
+ self.back_to_map_button_klass = null;
72
+ self.current_building = null;
73
+ self.current_area = null;
74
+ self.is_draw = false;
75
+ self.save_button_klass = null;
76
+ self.drawn_areas = []; // если имеются нарисованные но несохранённые Площади - они хранятся тут
77
+ self.drawn_buildings = []; // если имеются нарисованные но несохранённые Здания - они хранятся тут
78
+ self.save_preloader_klass = null;
79
+
80
+ // true, если:
81
+ //- юзер не кликал по кнопкам zoom
82
+ //- юзер не делал drag-n-drop
83
+ //- юзер не в здании и не в площади
84
+ self.mark_virgin = true;
85
+
86
+ self.init = function (el, params) {
87
+ // extend options
88
+ self.o = $.extend(self.o, params);
89
+
90
+ self.x = self.o.x;
91
+ self.y = self.o.y;
92
+ self.scale = self.o.scale;
93
+
94
+ self.el = el.addClass('melem mloading').addClass(self.o.skin).height(self.o.height);
95
+
96
+ // Disable modules when landmark mode is active
97
+ /*if (self.o.landmark) {
98
+ self.o.sidebar = false;
99
+ self.o.zoombuttons = false;
100
+ self.o.deeplinking = false;
101
+ }*/
102
+
103
+ if (typeof self.o.source === 'string') {
104
+ // Loading .json file with AJAX
105
+ $.getJSON(self.o.source, function (data) { // Success
106
+ initProcessData(data);
107
+ self.el.removeClass('mloading');
108
+ //setTimeout(invalidateZoom,1000);
109
+
110
+ }).fail(function () { // Failure: couldn't load JSON file, or it is invalid.
111
+ console.error('Couldn\'t load map data. (Make sure you are running the script through a server and not just opening the html file with your browser)');
112
+ self.el.removeClass('mloading').addClass('merror');
113
+ alert('Data file missing or invalid!');
114
+ });
115
+ }
116
+ else {
117
+ // Inline json object
118
+ initProcessData(self.o.source);
119
+ self.el.removeClass('mloading');
120
+ }
121
+
122
+
123
+ return self;
124
+
125
+
126
+ };
127
+
128
+ var initProcessData = function (data) {
129
+
130
+ self.data = data;
131
+
132
+ //var nrlevels = 0;
133
+ //var shownLevel;
134
+
135
+ self.container = $('.mcontainer'); //$('<div></div>').addClass('mcontainer').appendTo(self.el);
136
+ self.map = self.container.find('.mmap'); //$('<div></div>').addClass('mmap').appendTo(self.container);
137
+ if (self.o.zoom) self.map.addClass('mapplic-zoomable');
138
+ self.map_layers = self.map.find('.layers');
139
+ self.map_overlay_layers = self.map.find('.overlay_layers');
140
+
141
+ self.svg = $("#svg");
142
+ self.svg_overlay = $('#svg_overlay');
143
+ //$('<svg></svg>')
144
+ //.attr('xmlns','http://www.w3.org/2000/svg')
145
+ //.attr('version','1.2')
146
+ //.attr('baseProfile','tiny')
147
+ //.attr('id','svg')
148
+ //.appendTo(self.map);
149
+
150
+ self.levelselect = $('<select></select>').addClass('mlevels_select');
151
+
152
+ self.container.css('width', '100%'); // if (!self.o.sidebar)
153
+
154
+ self.contentWidth = parseInt(data.mapwidth);
155
+ self.contentHeight = parseInt(data.mapheight);
156
+
157
+ self.hw_ratio = data.mapheight / data.mapwidth;
158
+
159
+ self.map.css({
160
+ 'width': data.mapwidth,
161
+ 'height': data.mapheight
162
+ });
163
+
164
+ // Create new map layer
165
+ var layer = $('<div></div>').addClass('mlayer').addClass(data["object_type"]).appendTo(self.map_layers); // .hide()
166
+ $('<img>').attr('src', data["img"]).addClass('mmap-image').appendTo(layer);
167
+
168
+ // Zoom buttons
169
+ if (self.o.zoombuttons) {
170
+ self.zoombuttons = new ZoomButtons();
171
+ self.zoombuttons.init({height: self.o.height}, self);
172
+ //if (!self.o.clearbutton) self.zoombuttons.el.css('bottom', '0');
173
+ }
174
+
175
+ var sc = new StateController();
176
+ sc.init(self);
177
+ self.setMode = sc.setMode;
178
+
179
+ // Admin buttons
180
+ $.ajax({
181
+ url: '/ajax/map_edit_buttons',
182
+ type: 'POST',
183
+ dataType: 'script',
184
+ data: {
185
+ div_css_selector: '#container_buttons .mzoom_buttons'
186
+ }
187
+ }).done(function () {
188
+ clog('<ajax.done>');
189
+
190
+ self.edit_button_klass = new EditButton();
191
+ self.edit_button_klass.init('.mapplic-edit-button', self);
192
+
193
+ var e = new NewButton();
194
+ e.init('.mapplic-new-button', self);
195
+ self.new_button_klass = e;
196
+
197
+ e = new CancelCreatingButton();
198
+ e.init("#cancelCreating", self);
199
+
200
+ e = new CompleteCreatingButton();
201
+ e.init("#completeCreating", self);
202
+
203
+ self.save_button_klass = new SaveChangesButton();
204
+ self.save_button_klass.init('.mapplic-save-button', self);
205
+
206
+ });
207
+
208
+ // Controls
209
+ initAddControls();
210
+
211
+ self.draw_childs(data["childs"]);
212
+
213
+ self.ivalidateViewArea();
214
+
215
+ // Browser resize
216
+ $(window).resize(function () {
217
+
218
+ // Mobile
219
+ //if ($(window).width() < 668) {
220
+ // self.container.height($(window).height() - 66);
221
+ //}
222
+ //else self.container.height('100%');
223
+
224
+ // Контейнер с картой должен слушать изменения габаритов окна и подгоняться по высоте
225
+ var window_height = $(window).height() - 200;
226
+ if (window_height < 400) window_height = 400;
227
+ self.el.height(window_height + "px");
228
+
229
+ // ------------------------------------------------------------------------------------------------------------------------
230
+
231
+ // если пользователь еще не взаимодействовал с картой или вне здания\площади
232
+ // вписываем картинку карты в главный прямоугольник карты
233
+ // т.е. меняем масштаб
234
+ if (self.mark_virgin) {
235
+ // рассчитаем масштаб, при котором можно вписать главный прямоугольник карты в прямоугольник рабочей области
236
+ var scaleX = self.calcScale(self.o.mapwidth*0.05, self.o.mapwidth *.95, self.X10, self.X20);
237
+ var scaleY = self.calcScale(self.o.mapheight*0.05, self.o.mapheight *.95, self.Y10, self.Y20);
238
+ var scale = (scaleX < scaleY) ? scaleX : scaleY;
239
+ self.scale = scale;
240
+ }
241
+
242
+ // совмещаем точку на экране, в которую надо центрировать карту,
243
+ // с центром карты (или с центром здания\площади, в котором находится юзер),
244
+ // с учётом рассчитанного масштаба
245
+
246
+ // если пользователь еще не взаимодействовал с картой или вне здания\площади,
247
+ // то фокусируемся на центр карты,
248
+ // иначе - фокусируемся на центр здания\площади, в котором находится пользователь и
249
+ // фокус сдвигаем, с учётом того, что сбоку открыта панель с информацией о здании
250
+
251
+ // NOTE-25::хардкод
252
+ // логические координаты - геометрический центр картинки
253
+ var cx = self.o.mapwidth/2;
254
+ var cy = self.o.mapheight/2;
255
+ var mark_do_moving = false;
256
+
257
+ if (self.current_building) {
258
+ cx = self.current_building.cx();
259
+ cy = self.current_building.cy();
260
+ mark_do_moving = true;
261
+ } else if (self.current_area) {
262
+ cx = self.current_area.cx();
263
+ cy = self.current_area.cy();
264
+ mark_do_moving = true;
265
+ } else if (self.mark_virgin) {
266
+ mark_do_moving = true;
267
+ }
268
+
269
+ if (mark_do_moving) {
270
+ self.x = self.normalizeX(self.CX - self.scale * cx - self.container.offset().left);
271
+ self.y = self.normalizeY(self.CY - self.scale * cy - self.container.offset().top);
272
+ clog("<Map.initProcessData> call moveTo");
273
+ self.moveTo(self.x, self.y, self.scale, 100);
274
+ }
275
+
276
+ // ------------------------------------------------------------------------------------------------------------------------
277
+
278
+
279
+ self.ivalidateViewArea();
280
+
281
+ }).resize();
282
+
283
+ };
284
+
285
+ var initAddControls = function () {
286
+ var map = self.map,
287
+ mapbody = $('.mmap-image', self.map);
288
+
289
+ document.ondragstart = function () {
290
+ return false;
291
+ }; // IE drag fix
292
+
293
+ function onSvgMousedown(e) {
294
+ clog("<onSvgMousedown> self.mode = " + self.mode);
295
+
296
+ if (self.mode === 'editing' || self.mode === "edit_building" || self.mode === 'edit_area') {
297
+ if (e.target.parentNode.tagName === 'g') {
298
+ clog("<onSvgMousedown> e = ");
299
+ //clog(e.pageX);
300
+ //clog("<mouseDown> e.target.parentNode.tagName = " + e.target.parentNode.tagName);
301
+ //clog(e.target);
302
+ //info.unload();
303
+
304
+ // запомним ссылку на "выбранную" область
305
+ self.selected_area = e.target.parentNode.obj;
306
+
307
+ //app.deselectAll();
308
+
309
+ // поменяем внешний вид - добавим класс .selected
310
+ self.selected_area.select();
311
+
312
+ // запомним начальные координаты кликаы
313
+ self.selected_area.delta = {
314
+ 'x': e.pageX,
315
+ 'y': e.pageY
316
+ };
317
+
318
+ // если взаимодействуем с вершиной
319
+ if (utils.hasClass(e.target, 'helper')) {
320
+ var helper = e.target;
321
+ //clog("<mouseDown> helper.action = ");
322
+ //clog(helper.action);
323
+ self.edit_type = helper.action; // pointMove
324
+
325
+ if (helper.n >= 0) { // if typeof selected_area == polygon
326
+ self.selected_area.selected_point = helper.n;
327
+ }
328
+
329
+ self.addEvent(self.el[0], 'mousemove', self.onEdit)
330
+ //self.addEvent(self.el[0], 'mousemove', self.selected_area.onEdit)
331
+ .addEvent(self.el[0], 'mouseup', self.onEditStop);
332
+ }
333
+
334
+ else if (e.target.tagName === 'rect' || e.target.tagName === 'circle' || e.target.tagName === 'polygon') {
335
+ self.edit_type = 'move';
336
+ self.addEvent(self.el[0], 'mousemove', self.onEdit)
337
+ .addEvent(self.el[0], 'mouseup', self.onEditStop);
338
+ }
339
+ } else {
340
+ //app.deselectAll();
341
+ //info.unload();
342
+ }
343
+ }
344
+ }
345
+
346
+ self.svg.on('mousedown', onSvgMousedown);
347
+ //self.el[0].addEventListener('mousedown', onSvgMousedown, false);
348
+
349
+
350
+ // Drag & drop
351
+ function onDragNdrop(event) {
352
+ //clog("<mousedown> edit_type = " + self.edit_type);
353
+ clog("<mousedown> mode = " + self.mode);
354
+ //clog(event);
355
+
356
+ // если в данный момент не редактируем фигуру (т.е. не двигаем вершину фигуры)
357
+ if (self.edit_type == null) {
358
+ self.dragging = false;
359
+ map.stop();
360
+
361
+ map.data('mouseX', event.pageX);
362
+ map.data('mouseY', event.pageY);
363
+ map.data('lastX', self.x);
364
+ map.data('lastY', self.y);
365
+ map.data('startX', self.x);
366
+ map.data('startY', self.y);
367
+
368
+ map.addClass('mdragging');
369
+
370
+ self.map.on('mousemove', function (event) {
371
+ self.dragging = true;
372
+
373
+ var x = event.pageX - map.data('mouseX') + self.x;
374
+ var y = event.pageY - map.data('mouseY') + self.y;
375
+
376
+ x = self.normalizeX(x);
377
+ y = self.normalizeY(y);
378
+
379
+ clog("<Map.mousemove> x = " + x + "; y = " + y);
380
+ clog("<Map.mousemove> Call moveTo.");
381
+ self.moveTo(x, y);
382
+ map.data('lastX', x);
383
+ map.data('lastY', y);
384
+ });
385
+
386
+ $(document).on('mouseup', function (event) {
387
+ //clog("<mouseup> dragging = " + self.dragging + ", mode = " + self.mode + "; is_draw = " + self.is_draw + "; scale = " + self.scale);
388
+ //clog("<mouseup> event = ");
389
+ //clog(event);
390
+ //clog("<mouseup> event.target = ");
391
+ //clog($(event.target).parent()[0].obj);
392
+
393
+ //clog("<mouseup> [qq] screen: " + event.pageX + ", " + event.pageY +
394
+ //"; logic: " + self.rightX(event.pageX) + ", " + self.rightY(event.pageY));
395
+
396
+ clog("<mouseup> self.mode = " + self.mode);
397
+
398
+ // исключаем случайный dnd дрожащей рукой
399
+ var dx = map.data('startX') - map.data('lastX');
400
+ var dy = map.data('startY') - map.data('lastY');
401
+ var delta = Math.sqrt(dx*dx + dy*dy);
402
+ var is_real_dragging = delta > 10;
403
+
404
+ // если это в самом деле был drag\n\drop
405
+ if (self.dragging && is_real_dragging) {
406
+
407
+ self.x = map.data('lastX');
408
+ self.y = map.data('lastY');
409
+ }
410
+
411
+ // иначе - пытаемся выяснить, в каком режиме находимся
412
+ else {
413
+
414
+ var p;
415
+
416
+ /* если находимся в режиме просмотра всей карты - входим в здание */
417
+ if (self.mode == 'viewing') {
418
+ //clog($(event.target).parent()[0].obj.building);
419
+
420
+ // добираемся до объекта класса Здание, который обслуживает полигон
421
+ p = $(event.target).parent()[0];
422
+ if (p.obj && p.obj.building) {
423
+ var building = p.obj.building;
424
+ clog("<mouseup> Входим в здание.");
425
+ building.enter();
426
+ }
427
+
428
+ }
429
+
430
+ /* если находимся в режиме рисования - рисуем */
431
+ else if (self.mode == 'creating') {
432
+
433
+ // и если ещё пока не начали рисовать (т.е. если это первый клик)
434
+ if (!self.is_draw) {
435
+
436
+ var xx = self.rightX(event.pageX);
437
+ var yy = self.rightY(event.pageY);
438
+ //clog("<mouseup> " + xx + "; " + yy);
439
+
440
+ self.drawing_poligon = new Polygon(xx, yy, false, self);
441
+
442
+ //self.addEvent(self.el[0], 'mousemove', self.drawing_poligon.onDraw)
443
+ self.addEvent(self.el[0], 'mousemove', function (e) {
444
+ var _n_f = self.drawing_poligon;
445
+ var right_angle = !!e.shiftKey; //e.shiftKey ? true : false;
446
+
447
+ _n_f.dynamicDraw(self.rightX(e.pageX), self.rightY(e.pageY), right_angle);
448
+ })
449
+ //.addEvent(self.drawing_poligon.helpers[0].helper, 'click', self.drawing_poligon.onDrawStop)
450
+ //.addEvent(self.el[0], 'click', self.drawing_poligon.onDrawAddPoint);
451
+ .addEvent(self.el[0], 'click', function (e) {
452
+
453
+ // если кликнули в первую точку фигуры - заканчиваем рисование
454
+ var $et = $(e.target);
455
+ var $h = $(self.drawing_poligon.helpers[0].helper);
456
+ if ($et.attr('x') == $h.attr('x') && $et.attr('y') == $h.attr('y')) {
457
+ //self.drawing_poligon.onDrawStop();
458
+ self.onDrawStop();
459
+ return;
460
+ }
461
+
462
+ var x = self.rightX(e.pageX),
463
+ y = self.rightY(e.pageY),
464
+ _n_f = self.drawing_poligon;
465
+
466
+ if (e.shiftKey) {
467
+ var right_coords = _n_f.right_angle(x, y);
468
+ x = right_coords.x;
469
+ y = right_coords.y;
470
+ }
471
+ _n_f.addPoint(x, y);
472
+ })
473
+ .addEvent(document, 'keydown', self.onDrawStop);
474
+ }
475
+ }
476
+
477
+ /* если находимся в режиме просмотра здания - входим в площадь*/
478
+ else if (self.mode == 'view_building') {
479
+
480
+ // добираемся до объекта класса Area, который обслуживает полигон
481
+ p = $(event.target).parent()[0];
482
+ //clog($(event.target).parent()[0].obj.area_hash);
483
+
484
+ if (p.obj && p.obj.area) {
485
+ var area = p.obj.area;
486
+ clog("<mouseup> Входим в площадь.");
487
+ area.enter();
488
+ }
489
+
490
+ }
491
+ }
492
+
493
+ self.map.off('mousemove');
494
+ $(document).off('mouseup');
495
+
496
+ map.removeClass('mdragging');
497
+ });
498
+ }
499
+ }
500
+
501
+ self.svg.on('mousedown', onDragNdrop);
502
+ self.svg_overlay.on('mousedown', onDragNdrop);
503
+
504
+ self.el[0].addEventListener('mousemove', function (e) {
505
+ //coords_info.innerHTML = 'x: ' + rightX(e.pageX) + ', ' + 'y: ' + rightY(e.pageY);
506
+ }, false);
507
+
508
+ self.el[0].addEventListener('mouseleave', function () {
509
+ //coords_info.innerHTML = '';
510
+ }, false);
511
+
512
+ /* Disable selection */
513
+ //self.el[0].addEventListener('mousedown', function(e) { e.preventDefault(); }, false);
514
+
515
+ /* Disable image dragging */
516
+ self.el[0].addEventListener('dragstart', function (e) {
517
+ e.preventDefault();
518
+ }, false);
519
+
520
+ self.back_to_map_button_klass = new BackToMapButton();
521
+ self.back_to_map_button_klass.init("#container_buttons", self);
522
+
523
+ self.save_preloader_klass = new SavePreloader();
524
+ self.save_preloader_klass.init();
525
+
526
+ };
527
+
528
+ // какой должен быть минимальный масштаб, чтобы вписать отрезок [min,max] в отрезок [p1,p2]
529
+ self.calcScale = function (min, max, p1, p2) {
530
+ clog("<calcScale> [" + min + "," + max + '] to [' + p1 + "," + p2 + "]");
531
+ return (p2 - p1) / (max - min);
532
+ };
533
+
534
+ self.calcCoord = function (scale, pageC, logicC) {
535
+ return pageC - scale * logicC;
536
+ };
537
+
538
+ /* --- ivalidateViewArea BEGIN --------------------------------------------------------------------------------- */
539
+
540
+ var _$m = $("#map_wrapper");
541
+ var _$b = $('footer .container');
542
+ var $building_info = $('.building_info');
543
+ var $area_order_button = $('.area_order_button');
544
+ var $container_buttons = $('#container_buttons');
545
+ var _is_debug_drawn = false;
546
+
547
+ self.ivalidateViewArea = function () {
548
+ //clog('<init> _$b.offset().left = ' + _$b.offset().left);
549
+
550
+ // рассчитаем "константы" - прямоугольник, в который надо вписывать картинки зданий при входе в них
551
+ self.X1 = _$b.offset().left + 100;
552
+ self.X1S = _$b.offset().left + 200;
553
+ self.Y1 = 73;
554
+ self.Y1S = 140;
555
+ self.X2 = self.X1 + _$b.width() * .5;
556
+ self.X2S = self.X1 + _$b.width() * .4;
557
+ self.X3 = self.X1 + _$b.width() - 100;
558
+ self.Y2 = _$m.height() - 20;
559
+ self.Y2S = _$m.height() - 80;
560
+ self.CX = (self.X2 - self.X1) / 2 - 2 + self.X1;
561
+ self.CY = (self.Y2 - self.Y1) / 2 - 2 + self.Y1;
562
+
563
+ self.X10 = _$b.offset().left + 15;
564
+ self.X20 = self.X10 + _$b.width();
565
+ self.Y10 = 73;
566
+ self.Y20 = _$m.height();
567
+
568
+ // позиционируем элементы
569
+ $building_info.css("left", self.X2 + "px");
570
+ $area_order_button.css("left", self.X2 + "px");
571
+ if (self.container) $container_buttons.css("margin-top", (self.container.height() -10) + "px");
572
+
573
+ // DEBUG
574
+ if (self.debug) {
575
+
576
+ if (!_is_debug_drawn) {
577
+ _is_debug_drawn = true;
578
+
579
+ var style = "display:block;position:absolute;background-color:#00ff00;opacity:0.4;";
580
+ var style_x = style + "width:1px;height:800px;top:0;left:{X}px;";
581
+ var style_y = style + "width:3000px;height:1px;left:0;top:{Y}px;";
582
+ var style_dot = style + 'width:4px;height:4px;left:{X}px;top:{Y}px;';
583
+
584
+ var to_draw = [
585
+ {x: self.X10},
586
+ {x: self.X20},
587
+ {y: self.Y10},
588
+ {y: self.Y20},
589
+ {x: self.CX},
590
+ {y: self.CY},
591
+ ];
592
+
593
+
594
+ var i, istyle, xx, yy, ip;
595
+ for (i = 0; i < to_draw.length; i++) {
596
+ ip = to_draw[i];
597
+
598
+ if (ip.x != undefined) {
599
+ istyle = style_x.split("{X}").join(ip.x);
600
+ } else if (ip.y != undefined) {
601
+ istyle = style_y.split("{Y}").join(ip.y);
602
+ }
603
+
604
+ _$m.append($("<div style=" + istyle + "></div>"));
605
+ }
606
+
607
+ }
608
+
609
+ }
610
+
611
+ };
612
+
613
+ /* --- ivalidateViewArea END ----------------------------------------------------------------------------------- */
614
+
615
+ self.addEvent = function (target, eventType, func) {
616
+ self.events.push(new AppEvent(target, eventType, func));
617
+ return self;
618
+ };
619
+
620
+ self.removeAllEvents = function () {
621
+ utils.foreach(self.events, function (x) {
622
+ x.remove();
623
+ });
624
+ self.events.length = 0;
625
+ self.edit_type = null;
626
+ return this;
627
+ };
628
+
629
+ self.addNodeToSvg = function (node, is_overlay) {
630
+ if (is_overlay) {
631
+ self.svg_overlay[0].appendChild(node);
632
+ } else {
633
+ self.svg[0].appendChild(node);
634
+ }
635
+ return self;
636
+ };
637
+
638
+ self.removeNodeFromSvg = function(node, is_overlay) {
639
+ if (is_overlay) {
640
+ self.svg_overlay[0].removeChild(node);
641
+ } else {
642
+ self.svg[0].removeChild(node);
643
+ }
644
+ return this;
645
+ };
646
+
647
+ self.svgRemoveAllNodes = function () {
648
+ self.svg.empty();
649
+ };
650
+
651
+ self.draw_childs = function (childs, parent_hash) {
652
+ //clog("<Map.draw_childs>");
653
+
654
+ //var ip;
655
+ var iobj;
656
+ var ib, id, ia;
657
+ for (var i = 0; i < childs.length; i++) {
658
+ iobj = childs[i];
659
+
660
+ switch (iobj["object_type"]) {
661
+ case 'building':
662
+ ib = new Building();
663
+ ib.init(iobj,self);
664
+ break;
665
+ case 'dot':
666
+ id = new Dot();
667
+ id.init(iobj,self);
668
+ break;
669
+ case 'area':
670
+ ia = new Area();
671
+ ia.init(iobj, parent_hash, self);
672
+ break;
673
+ }
674
+ //ip = Polygon.createFromSaved(iobj);
675
+ //utils.id('svg').appendChild(ip.g);
676
+ }
677
+ };
678
+
679
+ //
680
+ self.draw_child_bg_image = function (img_src, obj_type, is_overlay) {
681
+ var t;
682
+ if (is_overlay == true) {
683
+ t = self.map_overlay_layers;
684
+ } else {
685
+ t = self.map_layers;
686
+ }
687
+ // Create new map layer
688
+ var layer = $('<div></div>').addClass('mlayer').addClass(obj_type).appendTo(t); // .hide()
689
+ $('<img>').attr('src', img_src).addClass('mmap-image').appendTo(layer);
690
+
691
+ return layer;
692
+ };
693
+
694
+ self.onEdit = function (e) {
695
+
696
+ //clog("<Polygon.prototype.onEdit> _s_f = " + _s_f);
697
+ //clog("<Polygon.prototype.onEdit> e = ");
698
+ //clog(_s_f);
699
+ //clog(e.pageX);
700
+
701
+ var selected_area = self.selected_area;
702
+ var edit_type = self.edit_type;
703
+ //clog("<Polygon.prototype.onEdit> edit_type = " + edit_type);
704
+
705
+ selected_area.dynamicEdit(selected_area[edit_type](e.pageX - selected_area.delta.x, e.pageY - selected_area.delta.y));
706
+ selected_area.delta.x = e.pageX;
707
+ selected_area.delta.y = e.pageY;
708
+ };
709
+
710
+ self.onDrawStop = function (e) {
711
+ clog("<Map.onDrawStop>");
712
+
713
+ if (e != undefined) {
714
+ if (e.type == 'keydown' && e.keyCode == 13) {
715
+ // its ok, continue execution..
716
+ } else {
717
+ return
718
+ }
719
+ }
720
+
721
+ var _n_f = self.drawing_poligon;
722
+ if (_n_f.params.length >= 6) { //>= 3 points for polygon
723
+ _n_f.polyline = _n_f.polygon;
724
+ _n_f.polygon = document.createElementNS('http://www.w3.org/2000/svg', 'polygon');
725
+ _n_f.g.replaceChild(_n_f.polygon, _n_f.polyline);
726
+ _n_f.setCoords(_n_f.params).deselect();
727
+ delete(_n_f.polyline);
728
+
729
+ // в зависимости от предыдущего состояния, создадим либо Здание, либо Площадь
730
+ if (self.prev_mode == "edit_building") {
731
+ var bo = self.current_building.options;
732
+ var a = new Area();
733
+ a.init({ coords:_n_f.params }, bo, self);
734
+ //a.is_new = true;
735
+ _n_f.remove(); // удаляем нарисованный полигон, т.к. его уже заменили полигоном Area
736
+ self.registerJustDrownArea(a);
737
+ }
738
+ else if (self.prev_mode == 'editing') {
739
+ var b = new Building();
740
+ b.init({ coords:_n_f.params }, self);
741
+ //b.is_new = true;
742
+ _n_f.remove(); // удаляем нарисованный полигон, т.к. его уже заменили полигоном Building
743
+ self.registerJustDrownBuilding(b);
744
+ }
745
+
746
+ self.removeAllEvents();
747
+ self.drawing_poligon = null;
748
+ self.is_draw = false;
749
+ }
750
+
751
+ self.setMode('editing');
752
+ };
753
+
754
+ self.onEditStop = function (e) {
755
+ //clog("<Polygon.prototype.onEditStop>");
756
+ var _s_f = self.selected_area,
757
+ edit_type = self.edit_type;
758
+
759
+ _s_f.setParams(_s_f.dynamicEdit(_s_f[edit_type](e.pageX - _s_f.delta.x, e.pageY - _s_f.delta.y)));
760
+
761
+ self.removeAllEvents();
762
+ };
763
+
764
+ self.registerJustDrownArea = function (area) {
765
+ self.drawn_areas.push(area);
766
+ };
767
+
768
+ self.registerJustDrownBuilding = function (building) {
769
+ self.drawn_buildings.push(building);
770
+ };
771
+
772
+ self.normalizeX = function (x) {
773
+ var minX = self.container.width() - self.contentWidth * self.scale;
774
+
775
+ if (minX < 0) {
776
+ if (x > 0) x = 0;
777
+ else if (x < minX) x = minX;
778
+ }
779
+ else x = minX / 2;
780
+
781
+ return x;
782
+ };
783
+
784
+ self.normalizeY = function (y) {
785
+ var minY = self.container.height() - self.contentHeight * self.scale;
786
+
787
+ if (minY < 0) {
788
+ if (y >= 0) y = 0;
789
+ else if (y < minY) y = minY;
790
+ }
791
+ else y = minY / 2;
792
+
793
+ return y;
794
+ };
795
+
796
+ self.normalizeScale = function (scale) {
797
+ clog('<self.normalizeScale>' + self.o.fitscale);
798
+ if (scale < self.o.fitscale) scale = self.o.fitscale;
799
+ else if (scale > self.o.maxscale) scale = self.o.maxscale;
800
+
801
+ if (self.zoombuttons) self.zoombuttons.update(scale);
802
+
803
+ return scale;
804
+ };
805
+
806
+ self.zoomTo = function (x, y, s, duration, easing, ry) {
807
+ duration = typeof duration !== 'undefined' ? duration : 400;
808
+ ry = typeof ry !== 'undefined' ? ry : 0.5;
809
+
810
+ self.scale = self.normalizeScale(self.o.fitscale * s);
811
+
812
+ self.x = self.normalizeX(self.container.width() * 0.5 - self.scale * self.contentWidth * x);
813
+ self.y = self.normalizeY(self.container.height() * ry - self.scale * self.contentHeight * y);
814
+
815
+ clog("<Map.zoomTo> Call moveTo.");
816
+ self.moveTo(self.x, self.y, self.scale, duration, easing);
817
+ };
818
+
819
+ // optimisation
820
+ var __moveToStep = function () {
821
+ //clog(self.map.attr('style'));
822
+ // left: -69.9985px; top: -299.999px;
823
+ // left: [-]{0,1}(\d+\.\d+px);
824
+
825
+ var str = self.map.attr('style');
826
+ var rx_left = /left: [-]{0,1}(\d+\.\d+)px;/;
827
+ var rx_top = /top: ([-]{0,1}\d+\.\d+)px;/;
828
+ var match_left = str.match(rx_left);
829
+ var match_right = str.match(rx_top);
830
+
831
+ if (match_left != null && match_right != null) {
832
+ var x = -1 * Number(match_left[1]); // ["left: -69.9985px;", "69.9985"]
833
+ var y = -1 * Number(match_right[1]); // ["left: -69.9985px;", "69.9985"]
834
+ var att = x + " " + y + " " + self.contentWidth + " " + self.contentHeight;
835
+ //clog(x + "; y = " + y);
836
+ self.svg.attr('viewBox', att);
837
+ self.svg_overlay.attr('viewBox', att);
838
+ }
839
+
840
+ };
841
+ var __moveToTimeout = function () {
842
+ $("#masked").removeClass('hiddn');
843
+ };
844
+ var __moveToAnimate = function () {
845
+ if (self.tooltip) self.tooltip.position();
846
+ };
847
+
848
+ // x,y - экранные координаты
849
+ self.moveTo = function (x, y, scale, d, easing) {
850
+ clog("<self.moveTo> x = " + x + "; y = " + y + "; scale = " + scale);
851
+
852
+ // если подан аргумент scale(масштаб)
853
+ // перемещаемся анимированно
854
+ if (scale !== undefined) {
855
+
856
+ // на время движения скрываем слой с полосатой анимацией
857
+ if (self.current_area != null) {
858
+ $("#masked").addClass('hiddn');
859
+ setTimeout(__moveToTimeout, d);
860
+ setTimeout(__moveToStep, d);
861
+ }
862
+
863
+ self.map.stop().animate(
864
+ {
865
+ 'left': x,
866
+ 'top': y,
867
+ 'width': self.contentWidth * scale,
868
+ 'height': self.contentHeight * scale
869
+ },
870
+ //{ 'step': __moveToStep },
871
+ d,
872
+ easing,
873
+ __moveToAnimate
874
+ );
875
+
876
+ }
877
+
878
+ // если не подан аргумент scale(масштаб)
879
+ // перемещаемся без анимации
880
+ else {
881
+
882
+ self.map.css({
883
+ 'left': x,
884
+ 'top': y
885
+ });
886
+
887
+ var t = (-x) + " " + (-y) + " " + self.contentWidth * self.scale + " " + self.contentHeight * self.scale;
888
+ self.svg.attr('viewBox',t);
889
+ self.svg_overlay.attr('viewBox', t);
890
+ }
891
+
892
+ if (self.current_area != null) {
893
+ self.current_area.invalidateAnimationMask();
894
+ }
895
+
896
+ //if (self.tooltip) self.tooltip.position();
897
+ //if (self.minimap) self.minimap.update(x, y);
898
+ };
899
+
900
+ // показать инфо о здании
901
+ self.showBuildingInfo = function (building_hash) {
902
+ //"building_hash": {
903
+ // "id": 2,
904
+ // "title": "Здание 2",
905
+ // "props": {
906
+ // "square": "1234 кв.м.",
907
+ // "square_free": "124 кв. м",
908
+ // "floor_height": "124 кв. м",
909
+ // "column_step": "2 м",
910
+ // "gate_type": "распашные",
911
+ // "communications": "Интернет, электричество, водоснабжение",
912
+ // "price": "от 155 руб/кв.м в месяц"
913
+ // }
914
+
915
+ $building_info.find("h2").text(building_hash["title"]);
916
+
917
+ var v;
918
+ for (var p in building_hash["props"]) {
919
+ v = building_hash["props"][p];
920
+ $building_info.find("#" + p).find('span').text(v);
921
+ }
922
+
923
+ $building_info.find("#square_free").css('height', 'auto');
924
+
925
+ };
926
+
927
+ // показать инфо о просматриваемой площади
928
+ self.showAreaInfo = function (area_hash, parent_building_hash) {
929
+ //clog(area_hash);
930
+
931
+ //"area_hash": {
932
+ // "id": 2,
933
+ // "title": "Площадь 2.12",
934
+ // "is_free": false,
935
+ // "props": {
936
+ // "square": "100 кв.м.",
937
+ // "floor_height": "6 кв. м",
938
+ // "column_step": "2 м",
939
+ // "gate_type": "распашные",
940
+ // "communications": "Интернет, электричество, водоснабжение",
941
+ // "price": "от 155 руб/кв.м в месяц"
942
+ // }
943
+
944
+ //"building_hash": {
945
+ // "id": 2,
946
+ // "title": "Здание 2",
947
+ // "props": {
948
+ // "square": "1234 кв.м.",
949
+ // "square_free": "124 кв. м",
950
+ // "floor_height": "6 кв. м",
951
+ // "column_step": "2 м",
952
+ // "gate_type": "распашные",
953
+ // "communications": "Интернет, электричество, водоснабжение",
954
+ // "price": "от 155 руб/кв.м в месяц"
955
+ // }
956
+
957
+ $building_info.find("h2").html("</span>" + area_hash["title"] + "<span style='color:#D0B2B2;'> / " + parent_building_hash["title"]);
958
+
959
+ var v;
960
+ for (var p in area_hash["props"]) {
961
+ v = area_hash["props"][p];
962
+ $building_info.find("#" + p).find('span').text(v);
963
+ }
964
+
965
+ $building_info.find("#square_free").css('height', '0');
966
+
967
+ };
968
+
969
+ // перевод экранных координат в логические
970
+ self.rightX = function(x) {
971
+ return (x - self.x - self.container.offset().left) / self.scale;
972
+ };
973
+
974
+ self.rightY = function(y) {
975
+ return (y - self.y - self.container.offset().top) / self.scale
976
+ }
977
+
978
+ };
979
+
980
+ // Create a jQuery plugin
981
+ $.fn.beMap = function (params) {
982
+ var len = this.length;
983
+
984
+ return this.each(function (index) {
985
+ var me = $(this),
986
+ key = 'beMap' + (len > 1 ? '-' + ++index : ''),
987
+ instance = (new Map).init(me, params);
988
+ });
989
+ };
990
+
991
+ })
992
+ (jQuery);