c80_map_floors 0.1.0.9 → 0.1.0.10

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 (34) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/lib/awesomplete.js +450 -0
  3. data/app/assets/javascripts/map_objects/area.js +12 -8
  4. data/app/assets/javascripts/map_objects/building.js +23 -6
  5. data/app/assets/javascripts/src/main.js +12 -2
  6. data/app/assets/javascripts/src/state_controller.js +50 -8
  7. data/app/assets/javascripts/svg_elements/admin_building_label.js +6 -6
  8. data/app/assets/javascripts/svg_elements/building_label.js +88 -60
  9. data/app/assets/javascripts/ui/tabs/tabs.js +55 -2
  10. data/app/assets/javascripts/view/building_info/building_info.js +85 -8
  11. data/app/assets/javascripts/view/building_info/mobj_info_parser.js +316 -12
  12. data/app/assets/javascripts/view/search_gui.js +295 -0
  13. data/app/assets/stylesheets/c80_map_floors.scss +3 -1
  14. data/app/assets/stylesheets/lib/awesomplete.scss +104 -0
  15. data/app/assets/stylesheets/lib_custom/awesomplete.scss +8 -0
  16. data/app/assets/stylesheets/map.scss +61 -16
  17. data/app/assets/stylesheets/ui/search_gui.scss +51 -0
  18. data/app/assets/stylesheets/ui/tabs_js.scss +25 -0
  19. data/app/assets/stylesheets/view/buttons/back_to_map_button.scss +1 -1
  20. data/app/assets/stylesheets/view/elems/building_info.scss +1 -1
  21. data/app/assets/stylesheets/view/elems/free_areas_label.scss +6 -1
  22. data/app/controllers/c80_map_floors/ajax_controller.rb +82 -0
  23. data/app/helpers/c80_map_floors/search_gui_helper.rb +19 -0
  24. data/app/models/c80_map_floors/area.rb +1 -1
  25. data/app/models/c80_map_floors/area_representator.rb +13 -22
  26. data/app/models/c80_map_floors/building_representator.rb +8 -1
  27. data/app/models/c80_map_floors/floor.rb +1 -1
  28. data/app/models/c80_map_floors/map_building.rb +2 -2
  29. data/app/models/c80_map_floors/map_json.rb +2 -1
  30. data/app/views/c80_map_floors/_map_row_index.html.erb +5 -0
  31. data/app/views/c80_map_floors/shared/map_row/_search_gui.html.erb +11 -0
  32. data/config/routes.rb +2 -0
  33. data/lib/c80_map_floors/version.rb +1 -1
  34. metadata +9 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 31966802b697ea9309a9b2f6200a0a5bdda7faee
4
- data.tar.gz: 72e5e8432d971bd72cbd178eca37e7dd93c3b04f
3
+ metadata.gz: 2b17f8e15a4a08414b6675fbe965a5cbef72d10b
4
+ data.tar.gz: f21b31c759a5e8661e6a186315f2e9c2a3cdcc55
5
5
  SHA512:
6
- metadata.gz: bf3b68b79cee003d5566fc9d5269fe322db7db74f274ffd8518ad72fc9feaa0c1eda21bc505d9091f84d56ffdd4421fb3af209f5465cc05e9af57f1fb99bb8c1
7
- data.tar.gz: 48137d966c943d594adf406a92c6a67b0bf275745490c036c1c7cc0f5ba1e8e200d67fcd0726cd6c9cf80eb3a000323047b932535bfa45046e2adb0d879419cd
6
+ metadata.gz: cf959e253e274628ac7b7610263b15960247437ec8d556f7b6c2981ef947ce3687997b1dee8fb8a467bf85a4e11f622c14e7a10903de2697b4069116de418cbe
7
+ data.tar.gz: 931f343f204d4393d3c67b62c3136cfd9d577e0a47a02f610da205613f3d6d65f7856741b9c13e8c0fbc610e26f86dc545ca44cde0b8aa04fa7445901018e5b7
@@ -0,0 +1,450 @@
1
+ /**
2
+ * Simple, lightweight, usable local autocomplete library for modern browsers
3
+ * Because there weren’t enough autocomplete scripts in the world? Because I’m completely insane and have NIH syndrome? Probably both. :P
4
+ * @author Lea Verou http://leaverou.github.io/awesomplete
5
+ * MIT license
6
+ */
7
+
8
+ (function () {
9
+
10
+ var _ = function (input, o) {
11
+ var me = this;
12
+
13
+ // Setup
14
+
15
+ this.isOpened = false;
16
+
17
+ this.input = $(input);
18
+ this.input.setAttribute("autocomplete", "off");
19
+ this.input.setAttribute("aria-autocomplete", "list");
20
+
21
+ o = o || {};
22
+
23
+ configure(this, {
24
+ minChars: 2,
25
+ maxItems: 10,
26
+ autoFirst: false,
27
+ data: _.DATA,
28
+ filter: _.FILTER_CONTAINS,
29
+ sort: _.SORT_BYLENGTH,
30
+ item: _.ITEM,
31
+ replace: _.REPLACE
32
+ }, o);
33
+
34
+ this.index = -1;
35
+
36
+ // Create necessary elements
37
+
38
+ this.container = $.create("div", {
39
+ className: "awesomplete",
40
+ around: input
41
+ });
42
+
43
+ this.ul = $.create("ul", {
44
+ hidden: "hidden",
45
+ inside: this.container
46
+ });
47
+
48
+ this.status = $.create("span", {
49
+ className: "visually-hidden",
50
+ role: "status",
51
+ "aria-live": "assertive",
52
+ "aria-relevant": "additions",
53
+ inside: this.container
54
+ });
55
+
56
+ // Bind events
57
+
58
+ $.bind(this.input, {
59
+ "input": this.evaluate.bind(this),
60
+ "blur": this.close.bind(this, { reason: "blur" }),
61
+ "keydown": function(evt) {
62
+ var c = evt.keyCode;
63
+
64
+ // If the dropdown `ul` is in view, then act on keydown for the following keys:
65
+ // Enter / Esc / Up / Down
66
+ if(me.opened) {
67
+ if (c === 13 && me.selected) { // Enter
68
+ evt.preventDefault();
69
+ me.select();
70
+ }
71
+ else if (c === 27) { // Esc
72
+ me.close({ reason: "esc" });
73
+ }
74
+ else if (c === 38 || c === 40) { // Down/Up arrow
75
+ evt.preventDefault();
76
+ me[c === 38? "previous" : "next"]();
77
+ }
78
+ }
79
+ }
80
+ });
81
+
82
+ $.bind(this.input.form, {"submit": this.close.bind(this, { reason: "submit" })});
83
+
84
+ $.bind(this.ul, {"mousedown": function(evt) {
85
+ var li = evt.target;
86
+
87
+ if (li !== this) {
88
+
89
+ while (li && !/li/i.test(li.nodeName)) {
90
+ li = li.parentNode;
91
+ }
92
+
93
+ if (li && evt.button === 0) { // Only select on left click
94
+ evt.preventDefault();
95
+ me.select(li, evt.target);
96
+ }
97
+ }
98
+ }});
99
+
100
+ if (this.input.hasAttribute("list")) {
101
+ this.list = "#" + this.input.getAttribute("list");
102
+ this.input.removeAttribute("list");
103
+ }
104
+ else {
105
+ this.list = this.input.getAttribute("data-list") || o.list || [];
106
+ }
107
+
108
+ _.all.push(this);
109
+ };
110
+
111
+ _.prototype = {
112
+ set list(list) {
113
+ if (Array.isArray(list)) {
114
+ this._list = list;
115
+ }
116
+ else if (typeof list === "string" && list.indexOf(",") > -1) {
117
+ this._list = list.split(/\s*,\s*/);
118
+ }
119
+ else { // Element or CSS selector
120
+ list = $(list);
121
+
122
+ if (list && list.children) {
123
+ var items = [];
124
+ slice.apply(list.children).forEach(function (el) {
125
+ if (!el.disabled) {
126
+ var text = el.textContent.trim();
127
+ var value = el.value || text;
128
+ var label = el.label || text;
129
+ if (value !== "") {
130
+ items.push({ label: label, value: value });
131
+ }
132
+ }
133
+ });
134
+ this._list = items;
135
+ }
136
+ }
137
+
138
+ if (document.activeElement === this.input) {
139
+ this.evaluate();
140
+ }
141
+ },
142
+
143
+ get selected() {
144
+ return this.index > -1;
145
+ },
146
+
147
+ get opened() {
148
+ return this.isOpened;
149
+ },
150
+
151
+ close: function (o) {
152
+ if (!this.opened) {
153
+ return;
154
+ }
155
+
156
+ this.ul.setAttribute("hidden", "");
157
+ this.isOpened = false;
158
+ this.index = -1;
159
+
160
+ $.fire(this.input, "awesomplete-close", o || {});
161
+ },
162
+
163
+ open: function () {
164
+ this.ul.removeAttribute("hidden");
165
+ this.isOpened = true;
166
+
167
+ if (this.autoFirst && this.index === -1) {
168
+ this.goto(0);
169
+ }
170
+
171
+ $.fire(this.input, "awesomplete-open");
172
+ },
173
+
174
+ next: function () {
175
+ var count = this.ul.children.length;
176
+ this.goto(this.index < count - 1 ? this.index + 1 : (count ? 0 : -1) );
177
+ },
178
+
179
+ previous: function () {
180
+ var count = this.ul.children.length;
181
+ var pos = this.index - 1;
182
+
183
+ this.goto(this.selected && pos !== -1 ? pos : count - 1);
184
+ },
185
+
186
+ // Should not be used, highlights specific item without any checks!
187
+ goto: function (i) {
188
+ var lis = this.ul.children;
189
+
190
+ if (this.selected) {
191
+ lis[this.index].setAttribute("aria-selected", "false");
192
+ }
193
+
194
+ this.index = i;
195
+
196
+ if (i > -1 && lis.length > 0) {
197
+ lis[i].setAttribute("aria-selected", "true");
198
+ this.status.textContent = lis[i].textContent;
199
+
200
+ // scroll to highlighted element in case parent's height is fixed
201
+ this.ul.scrollTop = lis[i].offsetTop - this.ul.clientHeight + lis[i].clientHeight;
202
+
203
+ $.fire(this.input, "awesomplete-highlight", {
204
+ text: this.suggestions[this.index]
205
+ });
206
+ }
207
+ },
208
+
209
+ select: function (selected, origin) {
210
+ if (selected) {
211
+ this.index = $.siblingIndex(selected);
212
+ } else {
213
+ selected = this.ul.children[this.index];
214
+ }
215
+
216
+ if (selected) {
217
+ var suggestion = this.suggestions[this.index];
218
+
219
+ var allowed = $.fire(this.input, "awesomplete-select", {
220
+ text: suggestion,
221
+ origin: origin || selected
222
+ });
223
+
224
+ if (allowed) {
225
+ this.replace(suggestion);
226
+ this.close({ reason: "select" });
227
+ $.fire(this.input, "awesomplete-selectcomplete", {
228
+ text: suggestion
229
+ });
230
+ }
231
+ }
232
+ },
233
+
234
+ evaluate: function() {
235
+ var me = this;
236
+ var value = this.input.value;
237
+
238
+ if (value.length >= this.minChars && this._list.length > 0) {
239
+ this.index = -1;
240
+ // Populate list with options that match
241
+ this.ul.innerHTML = "";
242
+
243
+ this.suggestions = this._list
244
+ .map(function(item) {
245
+ return new Suggestion(me.data(item, value));
246
+ })
247
+ .filter(function(item) {
248
+ return me.filter(item, value);
249
+ })
250
+ .sort(this.sort)
251
+ .slice(0, this.maxItems);
252
+
253
+ this.suggestions.forEach(function(text) {
254
+ me.ul.appendChild(me.item(text, value));
255
+ });
256
+
257
+ if (this.ul.children.length === 0) {
258
+ this.close({ reason: "nomatches" });
259
+ } else {
260
+ this.open();
261
+ }
262
+ }
263
+ else {
264
+ this.close({ reason: "nomatches" });
265
+ }
266
+ }
267
+ };
268
+
269
+ // Static methods/properties
270
+
271
+ _.all = [];
272
+
273
+ _.FILTER_CONTAINS = function (text, input) {
274
+ return RegExp($.regExpEscape(input.trim()), "i").test(text);
275
+ };
276
+
277
+ _.FILTER_STARTSWITH = function (text, input) {
278
+ return RegExp("^" + $.regExpEscape(input.trim()), "i").test(text);
279
+ };
280
+
281
+ _.SORT_BYLENGTH = function (a, b) {
282
+ if (a.length !== b.length) {
283
+ return a.length - b.length;
284
+ }
285
+
286
+ return a < b? -1 : 1;
287
+ };
288
+
289
+ _.ITEM = function (text, input) {
290
+ var html = input.trim() === '' ? text : text.replace(RegExp($.regExpEscape(input.trim()), "gi"), "<mark>$&</mark>");
291
+ return $.create("li", {
292
+ innerHTML: html,
293
+ "aria-selected": "false"
294
+ });
295
+ };
296
+
297
+ _.REPLACE = function (text) {
298
+ this.input.value = text.value;
299
+ };
300
+
301
+ _.DATA = function (item/*, input*/) { return item; };
302
+
303
+ // Private functions
304
+
305
+ function Suggestion(data) {
306
+ var o = Array.isArray(data)
307
+ ? { label: data[0], value: data[1] }
308
+ : typeof data === "object" && "label" in data && "value" in data ? data : { label: data, value: data };
309
+
310
+ this.label = o.label || o.value;
311
+ this.value = o.value;
312
+ }
313
+ Object.defineProperty(Suggestion.prototype = Object.create(String.prototype), "length", {
314
+ get: function() { return this.label.length; }
315
+ });
316
+ Suggestion.prototype.toString = Suggestion.prototype.valueOf = function () {
317
+ return "" + this.label;
318
+ };
319
+
320
+ function configure(instance, properties, o) {
321
+ for (var i in properties) {
322
+ var initial = properties[i],
323
+ attrValue = instance.input.getAttribute("data-" + i.toLowerCase());
324
+
325
+ if (typeof initial === "number") {
326
+ instance[i] = parseInt(attrValue);
327
+ }
328
+ else if (initial === false) { // Boolean options must be false by default anyway
329
+ instance[i] = attrValue !== null;
330
+ }
331
+ else if (initial instanceof Function) {
332
+ instance[i] = null;
333
+ }
334
+ else {
335
+ instance[i] = attrValue;
336
+ }
337
+
338
+ if (!instance[i] && instance[i] !== 0) {
339
+ instance[i] = (i in o)? o[i] : initial;
340
+ }
341
+ }
342
+ }
343
+
344
+ // Helpers
345
+
346
+ var slice = Array.prototype.slice;
347
+
348
+ function $(expr, con) {
349
+ return typeof expr === "string"? (con || document).querySelector(expr) : expr || null;
350
+ }
351
+
352
+ function $$(expr, con) {
353
+ return slice.call((con || document).querySelectorAll(expr));
354
+ }
355
+
356
+ $.create = function(tag, o) {
357
+ var element = document.createElement(tag);
358
+
359
+ for (var i in o) {
360
+ var val = o[i];
361
+
362
+ if (i === "inside") {
363
+ $(val).appendChild(element);
364
+ }
365
+ else if (i === "around") {
366
+ var ref = $(val);
367
+ ref.parentNode.insertBefore(element, ref);
368
+ element.appendChild(ref);
369
+ }
370
+ else if (i in element) {
371
+ element[i] = val;
372
+ }
373
+ else {
374
+ element.setAttribute(i, val);
375
+ }
376
+ }
377
+
378
+ return element;
379
+ };
380
+
381
+ $.bind = function(element, o) {
382
+ if (element) {
383
+ for (var event in o) {
384
+ var callback = o[event];
385
+
386
+ event.split(/\s+/).forEach(function (event) {
387
+ element.addEventListener(event, callback);
388
+ });
389
+ }
390
+ }
391
+ };
392
+
393
+ $.fire = function(target, type, properties) {
394
+ var evt = document.createEvent("HTMLEvents");
395
+
396
+ evt.initEvent(type, true, true );
397
+
398
+ for (var j in properties) {
399
+ evt[j] = properties[j];
400
+ }
401
+
402
+ return target.dispatchEvent(evt);
403
+ };
404
+
405
+ $.regExpEscape = function (s) {
406
+ return s.replace(/[-\\^$*+?.()|[\]{}]/g, "\\$&");
407
+ };
408
+
409
+ $.siblingIndex = function (el) {
410
+ /* eslint-disable no-cond-assign */
411
+ for (var i = 0; el = el.previousElementSibling; i++);
412
+ return i;
413
+ };
414
+
415
+ // Initialization
416
+
417
+ function init() {
418
+ $$("input.awesomplete").forEach(function (input) {
419
+ new _(input);
420
+ });
421
+ }
422
+
423
+ // Are we in a browser? Check for Document constructor
424
+ if (typeof Document !== "undefined") {
425
+ // DOM already loaded?
426
+ if (document.readyState !== "loading") {
427
+ init();
428
+ }
429
+ else {
430
+ // Wait for it
431
+ document.addEventListener("DOMContentLoaded", init);
432
+ }
433
+ }
434
+
435
+ _.$ = $;
436
+ _.$$ = $$;
437
+
438
+ // Make sure to export Awesomplete on self when in a browser
439
+ if (typeof self !== "undefined") {
440
+ self.Awesomplete = _;
441
+ }
442
+
443
+ // Expose Awesomplete as a CJS module
444
+ if (typeof module === "object" && module.exports) {
445
+ module.exports = _;
446
+ }
447
+
448
+ return _;
449
+
450
+ }());
@@ -110,17 +110,19 @@ function Area() {
110
110
  _this._polygon_overlay.hover(_this._mouse_in, _this._mouse_out);
111
111
  _this._calcBBox();
112
112
 
113
- // TODO:: старый код не работает: вместо area_hash должно быть data (и так далее)
113
+ //<editor-fold desc="// если у полигона имеется Площадь - добавим css класс (для разукрашивания таких полигонов)">
114
114
  var k = 'unassigned';
115
- if (options.area_hash != undefined) {
116
- if (typeof options.area_hash.id !== 'undefined') {
117
- k = 'free';
118
- if (!options.area_hash.is_free) {
119
- k = 'busy';
115
+ if (options['data'] != undefined) {
116
+ if (typeof options['data']['id'] !== 'undefined') {
117
+ console.log('<Area.init> [breakpoint]');
118
+ k = 'busy';
119
+ if (options['data']['is_free']) {
120
+ k = 'free';
120
121
  }
121
122
  }
122
123
  }
123
124
  _this._polygon.parent().attr("class", k);
125
+ //</editor-fold>
124
126
 
125
127
  };
126
128
 
@@ -239,12 +241,14 @@ function Area() {
239
241
  _this._mouse_in = function () {
240
242
  //console.log('<Area._mouse_in>');
241
243
  //console.log(_this._polygon);
242
- _this._polygon.attr('class', 'hover');
244
+ //_this._polygon.attr('class', 'hover');
245
+ $(_this._polygon).addClass('hover');
243
246
  };
244
247
 
245
248
  _this._mouse_out = function () {
246
249
  //console.log('<Area._mouse_out>');
247
- _this._polygon.attr('class', '');
250
+ //_this._polygon.attr('class', '');
251
+ $(_this._polygon).removeClass('hover');
248
252
  };
249
253
 
250
254
  _this._calc_polygon_attr = function () {
@@ -27,8 +27,9 @@ function Building() {
27
27
  // если вошли в какой-то этаж - эта переменная будет хранить ссылку на объект с данными полигона Этажа из locations.json
28
28
  var _json_current_floor = null;
29
29
 
30
- _this._label = null;
31
- _this._admin_label = null;
30
+ _this.id = null;
31
+ _this._label = null; // подпись над зданием - сколько магазинов (удовлетворяющих поиску) находятся в Здании
32
+ _this._admin_label = null; // админский лейбл - айдишник и название Здания (привязанного к полигону), видимый в режиме редактирования
32
33
 
33
34
  var _zoomToMe = function () {
34
35
 
@@ -163,7 +164,7 @@ function Building() {
163
164
 
164
165
  /**
165
166
  *
166
- * @param options - Это C80MapFloors::MapBuilding.my_as_json
167
+ * @param options - Это C80MapFloors::MapBuilding.my_as_json5
167
168
  * @param link_to_map
168
169
  */
169
170
  _this.init = function (options, link_to_map) {
@@ -211,9 +212,6 @@ function Building() {
211
212
 
212
213
  _this._calcBBox();
213
214
 
214
- // TODO:: подпись над зданием - сколько свободных площадей
215
- _this._label = new BuildingLabel(options, _map);
216
-
217
215
  // подпись над полигоном показываем только админам
218
216
  // UPD: не прокатит, т.к. здания инициализируются ДО того, как приходит ajax/map_edit_buttons, где IS_ADMIN=true
219
217
  //if (IS_ADMIN) {
@@ -407,4 +405,23 @@ function Building() {
407
405
  _this._admin_label = null;
408
406
  }
409
407
  };
408
+
409
+ // показать/скрыть зеленые кружки с цифрами
410
+ this.greenCircleShow = function (count) { // count - кол-во (удовлетворяющих поиску) магазинов в здании
411
+ console.log('<Building.greenCircleShow> Покажем зеленую метку над зданием.');
412
+ if (_this._label == null) {
413
+ _this._label = new BuildingLabel({
414
+ x: _options['coords'][0],
415
+ y: _options['coords'][1],
416
+ count: count
417
+ }, _map);
418
+ }
419
+ };
420
+ this.greenCircleHide = function () {
421
+ console.log('<Building.greenCircleShow> Скроем зеленую метку над зданием.');
422
+ if (_this._label != null) {
423
+ _this._label.destroy();
424
+ _this._label = null;
425
+ }
426
+ };
410
427
  }
@@ -89,6 +89,7 @@ var InitMap = function (params) {
89
89
  self.last_clicked_g = null; // начали просматривать area\building (запустили сессию), и здесь храним ссылку на последний кликнутый полигон из svg_overlay в течение сессии
90
90
  //self.o.dnd_enable = null; // если да, то можно карту dnd мышкой
91
91
  self.building_info_klass = null; // класс, занимающися отображением данных об этаже\здании\площади
92
+ self.search_gui_klass = null; // класс, занимающийся обслуживанием поисковых запросов пользователя карты
92
93
 
93
94
  // во время анимации каждый шаг рассчитывается мгновенный scale
94
95
  self.scale_during_animation = null;
@@ -291,6 +292,9 @@ var InitMap = function (params) {
291
292
  }
292
293
  });
293
294
 
295
+ // инициализируем класс, обслуживающий поиск
296
+ self.search_gui_klass = new SearchGUI(self);
297
+
294
298
  // начнём слушать окно браузера
295
299
  $(window).resize(function () {
296
300
 
@@ -848,6 +852,12 @@ var InitMap = function (params) {
848
852
  //ip = Polygon.createFromSaved(iobj);
849
853
  //utils.id('svg').appendChild(ip.g);
850
854
  }
855
+
856
+ // Только после того, как нарисуем всех детей на карте, подсветим результаты поиска
857
+ if (self.search_gui_klass != null) {
858
+ self.search_gui_klass.handleSearchResults();
859
+ }
860
+
851
861
  };
852
862
 
853
863
  /**
@@ -1359,7 +1369,7 @@ var InitMap = function (params) {
1359
1369
  }
1360
1370
  };
1361
1371
 
1362
- // показать инфо о просматриваемой площади
1372
+ // TODO:: показать инфо о просматриваемой площади (это код, который остался без изменений от c80_map)
1363
1373
  self.showAreaInfo = function (area_json, parent_floor_json) {
1364
1374
  //console.log(area_hash);
1365
1375
 
@@ -1377,7 +1387,7 @@ var InitMap = function (params) {
1377
1387
  // "price": "от 155 руб/кв.м в месяц"
1378
1388
  // }
1379
1389
 
1380
- // так было в c80_map
1390
+ // так стало в c80_map_floors
1381
1391
  //"rent_building_hash": {
1382
1392
  // "id": 2,
1383
1393
  // "title": "Здание 2",