c80_map_floors 0.1.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (92) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +11 -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 +67 -0
  8. data/Rakefile +1 -0
  9. data/app/admin/c80_map_floors/floors.rb +57 -0
  10. data/app/admin/c80_map_floors/map_buildings.rb +49 -0
  11. data/app/admin/c80_map_floors/settings.rb +32 -0
  12. data/app/assets/javascripts/buttons/admin_buttons/button_area_link.js +91 -0
  13. data/app/assets/javascripts/buttons/admin_buttons/button_building_link.js +91 -0
  14. data/app/assets/javascripts/buttons/admin_buttons/button_cancel_create.js +21 -0
  15. data/app/assets/javascripts/buttons/admin_buttons/button_cancel_remove.js +23 -0
  16. data/app/assets/javascripts/buttons/admin_buttons/button_complete_create.js +22 -0
  17. data/app/assets/javascripts/buttons/admin_buttons/button_edit.js +96 -0
  18. data/app/assets/javascripts/buttons/admin_buttons/button_new.js +46 -0
  19. data/app/assets/javascripts/buttons/admin_buttons/button_remove.js +23 -0
  20. data/app/assets/javascripts/buttons/admin_buttons/button_save.js +111 -0
  21. data/app/assets/javascripts/buttons/button_back_to_map.js +84 -0
  22. data/app/assets/javascripts/buttons/zoom_buttons.js +78 -0
  23. data/app/assets/javascripts/c80_map_floors.js.coffee +23 -0
  24. data/app/assets/javascripts/events/app_event.js +15 -0
  25. data/app/assets/javascripts/map_objects/area.js +251 -0
  26. data/app/assets/javascripts/map_objects/building.js +294 -0
  27. data/app/assets/javascripts/map_objects/dot.js +14 -0
  28. data/app/assets/javascripts/map_objects/floor.js +10 -0
  29. data/app/assets/javascripts/src/main.js +1421 -0
  30. data/app/assets/javascripts/src/state_controller.js +322 -0
  31. data/app/assets/javascripts/src/utils/map_utils.js +23 -0
  32. data/app/assets/javascripts/src/utils/opacity_buttons_utils.js +15 -0
  33. data/app/assets/javascripts/src/utils/utils.js +140 -0
  34. data/app/assets/javascripts/svg_elements/area_label.js +25 -0
  35. data/app/assets/javascripts/svg_elements/building_label.js +65 -0
  36. data/app/assets/javascripts/svg_elements/helper.js +36 -0
  37. data/app/assets/javascripts/svg_elements/polygon.js +194 -0
  38. data/app/assets/javascripts/view/save_preloader.js +30 -0
  39. data/app/assets/stylesheets/c80_map_floors.scss +4 -0
  40. data/app/assets/stylesheets/map.scss +1464 -0
  41. data/app/assets/stylesheets/view/buttons/area_order_button.scss +16 -0
  42. data/app/assets/stylesheets/view/buttons/back_to_map_button.scss +28 -0
  43. data/app/assets/stylesheets/view/elems/building_info.scss +54 -0
  44. data/app/assets/stylesheets/view/elems/free_areas_label.scss +116 -0
  45. data/app/assets/stylesheets/view/elems/map_objects/map_object_image_bg.scss +10 -0
  46. data/app/assets/stylesheets/view/modal_window.scss +13 -0
  47. data/app/assets/stylesheets/view/save_preloader.scss +206 -0
  48. data/app/controllers/c80_map_floors/ajax_controller.rb +65 -0
  49. data/app/controllers/c80_map_floors/application_controller.rb +6 -0
  50. data/app/controllers/c80_map_floors/map_ajax_controller.rb +70 -0
  51. data/app/helpers/c80_map_floors/application_helper.rb +28 -0
  52. data/app/models/c80_map_floors/area.rb +20 -0
  53. data/app/models/c80_map_floors/area_representator.rb +75 -0
  54. data/app/models/c80_map_floors/base_map_object.rb +41 -0
  55. data/app/models/c80_map_floors/building_representator.rb +72 -0
  56. data/app/models/c80_map_floors/floor.rb +37 -0
  57. data/app/models/c80_map_floors/map_building.rb +45 -0
  58. data/app/models/c80_map_floors/map_json.rb +35 -0
  59. data/app/models/c80_map_floors/setting.rb +32 -0
  60. data/app/uploaders/c80_map_floors/building_image_uploader.rb +31 -0
  61. data/app/uploaders/c80_map_floors/floor_image_uploader.rb +33 -0
  62. data/app/uploaders/c80_map_floors/map_image_uploader.rb +31 -0
  63. data/app/views/c80_map_floors/_map_row_index.html.erb +40 -0
  64. data/app/views/c80_map_floors/ajax/fetch_unlinked_areas.js.erb +7 -0
  65. data/app/views/c80_map_floors/ajax/fetch_unlinked_buildings.js.erb +7 -0
  66. data/app/views/c80_map_floors/ajax/map_edit_buttons.js.erb +36 -0
  67. data/app/views/c80_map_floors/ajax/shared/_map_creating.html.erb +6 -0
  68. data/app/views/c80_map_floors/ajax/shared/_map_editing.html.erb +4 -0
  69. data/app/views/c80_map_floors/ajax/shared/_map_removing.html.erb +5 -0
  70. data/app/views/c80_map_floors/ajax/shared/_select_list_unlinked_areas.html.erb +6 -0
  71. data/app/views/c80_map_floors/ajax/shared/_select_list_unlinked_buildings.html.erb +10 -0
  72. data/app/views/c80_map_floors/shared/_modal_window.html.erb +28 -0
  73. data/app/views/c80_map_floors/shared/_save_preloader.html.erb +3 -0
  74. data/app/views/c80_map_floors/shared/map_row/_area_order_button.html.erb +7 -0
  75. data/app/views/c80_map_floors/shared/map_row/_building_info.html.erb +17 -0
  76. data/bin/console +14 -0
  77. data/bin/setup +7 -0
  78. data/c80_map_floors.gemspec +28 -0
  79. data/config/routes.rb +4 -0
  80. data/db/migrate/20161015190000_create_c80_map_floors_settings.rb +8 -0
  81. data/db/migrate/20161015190001_create_c80_map_floors_map_buildings.rb +10 -0
  82. data/db/migrate/20161015190002_create_c80_map_floors_areas.rb +12 -0
  83. data/db/migrate/20161015190003_add_building_representator_to_c80_map_floors_buildings.rb +6 -0
  84. data/db/migrate/20161015190004_create_c80_map_floors_floors.rb +16 -0
  85. data/db/migrate/20161019111010_add_title_to_c80_map_floors_buildings.rb +5 -0
  86. data/db/migrate/20161020184040_add_coords_img_to_c80_map_floors_buildings.rb +5 -0
  87. data/db/seeds/c80_map_floors_01_fill_map_settings.rb +6 -0
  88. data/db/seeds/c80_map_floors_02_create_test_area.rb +7 -0
  89. data/lib/c80_map_floors.rb +8 -0
  90. data/lib/c80_map_floors/engine.rb +23 -0
  91. data/lib/c80_map_floors/version.rb +3 -0
  92. metadata +218 -0
@@ -0,0 +1,322 @@
1
+ "use strict";
2
+
3
+ function StateController() {
4
+
5
+ var _map = null;
6
+ var _this = this;
7
+
8
+ _this.left_side = $("#left_side");
9
+ _this.right_side = $("#right_side");
10
+ _this.remove_button = $('.mapplic-remove-button');
11
+ _this.new_button = $('.mapplic-new-button');
12
+ _this.mzoom_buttons = $('.mzoom_buttons');
13
+ _this.map_creating = $('#map_creating');
14
+ _this.map_editing = $('#map_editing');
15
+ _this.map_removing = $('#map_removing');
16
+ _this.main_map = $('.main_map'); /*qwwqq*/
17
+ _this.svg_overlay = $('#svg_overlay');
18
+ _this.building_info = $('.building_info');
19
+ _this.area_order_button = $('.area_order_button');
20
+ _this.edit_button = $('.mapplic-edit-button');
21
+ _this.masked = $('#masked');
22
+
23
+ _this.setMode = function (mode) {
24
+
25
+ // Должен быть учёт, из какого состояния пришли в состояние рисования, и возвращаться в него
26
+ // * При рисовании, находясь внутри здания, возвращаться в 'edit_building'
27
+ // * При рисовании, находясь внутри площади, возвращаться в 'edit_area'
28
+ // За исключением ситуации, когда начали редактировать, находясь на карте
29
+ if (mode == 'editing' && _map.prev_mode != 'viewing') {
30
+ mode = _map.prev_mode;
31
+ }
32
+
33
+ clog('<StateController.setMode> mode = ' + mode);
34
+
35
+ _map.prev_mode = _map.mode;
36
+ _map.mode = mode;
37
+
38
+ // этот код коррелирует с [x9cs7]. Возможно, нужен рефакторинг.
39
+ _map.container.removeClass("viewing");
40
+ _map.container.removeClass("editing");
41
+ _map.container.removeClass("creating");
42
+ _map.container.removeClass("removing");
43
+ _map.container.removeClass("view_building");
44
+ _map.container.removeClass("edit_building");
45
+ _map.container.removeClass("view_area");
46
+ _map.container.removeClass("edit_area");
47
+ _map.container.addClass(mode);
48
+
49
+ _this.checkMode();
50
+ };
51
+
52
+ _this.checkMode = function () {
53
+
54
+ if (_this.new_button.length == 0) _this.new_button = $('.mapplic-new-button');
55
+ if (_this.remove_button.length == 0) _this.remove_button = $('.mapplic-remove-button');
56
+ if (_this.edit_button.length == 0) _this.edit_button = $('.mapplic-edit-button');
57
+ if (_this.right_side.length == 0) _this.right_side = $('#right_side');
58
+ if (_this.left_side.length == 0) _this.left_side = $('#left_side');
59
+ if (_this.mzoom_buttons.length == 0) _this.mzoom_buttons = $('.mzoom_buttons');
60
+ if (_this.map_creating.length == 0) _this.map_creating = $('#map_creating');
61
+ if (_this.map_editing.length == 0) _this.map_editing = $('#map_editing');
62
+ if (_this.map_removing.length == 0) _this.map_removing = $('#map_removing');
63
+ if (_this.main_map.length == 0) _this.main_map = $('.main_map'); /*qwwqq*/
64
+ if (_this.svg_overlay.length == 0) _this.svg_overlay = $('#svg_overlay');
65
+ if (_this.building_info.length == 0) _this.building_info = $('.building_info');
66
+ if (_this.area_order_button.length == 0) _this.area_order_button = $('.area_order_button');
67
+ if (_this.masked.length == 0) _this.masked = $("#masked");
68
+
69
+ switch (_map.mode) {
70
+
71
+ // перешли в состояние
72
+ // редактирования карты
73
+ case "editing":
74
+
75
+ // спрячем надписи "цена за метр" и адрес с телефоном
76
+ _this.left_side.css("top", -300);
77
+ _this.right_side.css("top", -300);
78
+
79
+ // покажем кнопку "добавить фигуру"
80
+ OpacityButtonsUtils.show(_this.new_button);
81
+ _map.new_button_klass.resetState();
82
+
83
+ // покажем кнопку "удалить фигуру"
84
+ OpacityButtonsUtils.show(_this.remove_button);
85
+
86
+ // спрячем статусную область
87
+ _this.map_creating.css('display', 'none');
88
+ _this.map_editing.css('display', 'block');
89
+ _this.map_removing.css('display', 'none');
90
+
91
+ // покажем кнопки, присущие этому режиму
92
+ _this.mzoom_buttons.css('opacity', '1');
93
+
94
+ _this.main_map.css('opacity', '1');
95
+
96
+ _map.save_button_klass.show();
97
+ _map.save_button_klass.check_and_enable();
98
+
99
+ // скроем подсказки - сколько свободных площадей где есть
100
+ _map.hide_free_areas_hint();
101
+
102
+ break;
103
+
104
+ // перешли в состояние
105
+ // просмотра карты, все здания с крышами
106
+ case "viewing":
107
+ //clog("_this.left_side.data('init') = " + _this.left_side.data('init'));
108
+
109
+ // покажем надписи "цена за метр" и адрес с телефоном
110
+ if (_this.left_side.data('init') == undefined) {
111
+ _this.left_side.data('init', _this.left_side.css("top"));
112
+ }
113
+ if (_this.right_side.data('init') == undefined) {
114
+ _this.right_side.data('init', _this.right_side.css("top"));
115
+ }
116
+ _this.left_side.css("top", _this.left_side.data('init'));
117
+ _this.right_side.css("top", _this.right_side.data('init'));
118
+
119
+ // спрячем кнопки редактора
120
+ OpacityButtonsUtils.hide(_this.new_button);
121
+ OpacityButtonsUtils.hide(_this.remove_button);
122
+ _this.map_creating.css('display', 'none');
123
+ _this.map_editing.css('display', 'none');
124
+ _this.map_removing.css('display', 'none');
125
+
126
+ // покажем кнопки zoom
127
+ _this.mzoom_buttons.css('opacity', '1');
128
+
129
+ // покажем главную карту(?)
130
+ _this.main_map.css('opacity', '1');
131
+
132
+ // скроем svg_overlay слой, который нужен только когда внутри здания
133
+ _this.svg_overlay.css('display', 'none');
134
+
135
+ // скроем слой, в котором рисуем анимацию по маске, который нужен только когда внутри здания\площади
136
+ _this.masked.addClass('hiddn');
137
+
138
+ // скроем табличку с данными о здании
139
+ if (_this.building_info.data("init") == undefined) {
140
+ _this.building_info.data('init', _this.building_info.css("top"));
141
+ }
142
+ _this.building_info.css("top", -300);
143
+ _this.building_info.css("display", "block");
144
+
145
+ // скроем кнопку "забронировать площадь"
146
+ _this.area_order_button.css('display', 'none');
147
+
148
+ // скроем кнопку "обратно на карту"
149
+ _map.back_to_map_button_klass.hide();
150
+
151
+ // актуально, когда входим в это состояние
152
+ // из здания\площади, нажав кнопку "обратно на карту"
153
+ _map.edit_button_klass.setState('viewing', true); // [a1x7]
154
+
155
+ if (_map.save_button_klass) {
156
+ _map.save_button_klass.hide();
157
+ }
158
+
159
+ // покажем подсказки - сколько свободных площадей где есть
160
+ _map.show_free_areas_hint();
161
+
162
+ break;
163
+
164
+ // перешли в состояние рисования полигона
165
+ case "creating":
166
+ //_this.mzoom_buttons.css('opacity', '0');
167
+ _this.map_creating.css('display', 'block');
168
+ _this.map_editing.css('display', 'none');
169
+ _this.map_removing.css('display', 'none');
170
+
171
+ _this.main_map.css('opacity', '1');
172
+
173
+ break;
174
+
175
+ // перешли в состояние удаления полигона
176
+ case "removing":
177
+ //_this.mzoom_buttons.css('opacity', '0');
178
+ _this.map_creating.css('display', 'none');
179
+ _this.map_editing.css('display', 'none');
180
+ _this.map_removing.css('display', 'block');
181
+
182
+ _this.main_map.css('opacity', '1');
183
+
184
+ _map.save_button_klass.hide();
185
+
186
+ // прячем кнопку "создать полигон"
187
+ OpacityButtonsUtils.hide(_this.new_button);
188
+ OpacityButtonsUtils.hide(_this.remove_button);
189
+ OpacityButtonsUtils.hide(_this.edit_button);
190
+
191
+ break;
192
+
193
+ // вошли в здание
194
+ case "view_building":
195
+
196
+ // покажем кнопку "обратно на карту"
197
+ _map.back_to_map_button_klass.show();
198
+
199
+ // скроем кнопку "связать здание с полигоном"
200
+ _map.building_link_button_klass.hide();
201
+
202
+ // спрячем надписи "цена за метр" и адрес с телефоном
203
+ _this.left_side.css("top", -300);
204
+ _this.right_side.css("top", -300);
205
+
206
+ //_this.main_map.css('opacity','0.7');
207
+ _this.svg_overlay.css('display', 'block');
208
+
209
+ _this.building_info.css("top", _this.building_info.data("init"));
210
+ _this.masked.addClass('hiddn');
211
+
212
+ _this.area_order_button.css('display', 'none');
213
+ _map.edit_button_klass.setState('view_building', true); // [a1x7]
214
+ if (_map.current_building != undefined) _map.current_building.resetOverlayZindex();
215
+ _map.save_button_klass.hide();
216
+
217
+ OpacityButtonsUtils.hide(_this.new_button);
218
+ OpacityButtonsUtils.hide(_this.remove_button);
219
+
220
+ _this.mzoom_buttons.css('opacity', '1');
221
+
222
+ // скроем подсказки - сколько свободных площадей где есть
223
+ _map.hide_free_areas_hint();
224
+
225
+ break;
226
+
227
+ // редактируем, находясь в здании
228
+ case "edit_building":
229
+
230
+ // спрячем кнопку "обратно на карту"
231
+ _map.back_to_map_button_klass.hide();
232
+
233
+ // покажем кнопку "связать здание с полигоном"
234
+ _map.building_link_button_klass.show();
235
+
236
+ // т.к. этот слой используется испключительно в помощь при рисовании обводки площадей
237
+ // и перехватывает клики при dnd, то тут он нам не нужен
238
+ _this.svg_overlay.css('display', 'none');
239
+
240
+ // заодно поменяем z-index слоёв с колоннами и слоя с svg
241
+ // полигонами площадей, чтобы можно было добраться мышкой
242
+ // до этих полигонов и редактировать их
243
+ if (_map.current_building != undefined) _map.current_building.changeOverlayZindex();
244
+
245
+ // покажем кнопку "добавить фигуру"
246
+ OpacityButtonsUtils.show(_this.new_button);
247
+ _map.new_button_klass.resetState();
248
+
249
+ // покажем кнопку "удалить фигуру"
250
+ OpacityButtonsUtils.show(_this.remove_button);
251
+
252
+ // покажем кнопку "ред"
253
+ OpacityButtonsUtils.show(_this.edit_button);
254
+
255
+ // спрячем инфу о здании
256
+ _this.building_info.css("top", -300);
257
+
258
+ // спрячем статус строку "вы создаёте полигон"
259
+ _this.map_creating.css('display', 'none');
260
+ _this.map_removing.css('display', 'none');
261
+
262
+ // покажем, возможно спрятанные, zoom кнопки
263
+ _this.mzoom_buttons.css('opacity', '1');
264
+
265
+ _map.save_button_klass.show();
266
+ _map.save_button_klass.check_and_enable();
267
+
268
+ break;
269
+
270
+ // вошли в площадь
271
+ case "view_area":
272
+ _map.back_to_map_button_klass.show();
273
+ _this.masked.removeClass('hiddn');
274
+ var t = _this.building_info.height() + _this.building_info.offset().top;
275
+ var tt = _this.building_info.css("left");
276
+ var tq = (_this.building_info.width() + 40) + "px";
277
+ _this.area_order_button.css("top", t + "px");
278
+ // _this.area_order_button.css("bottom","400px");
279
+ _this.area_order_button.css("left", tt);
280
+ _this.area_order_button.css("width", tq);
281
+ _this.area_order_button.css('display', 'block');
282
+ _map.edit_button_klass.setState('view_area', true); // [a1x7]
283
+
284
+ // скроем кнопку "связать площадь с полигоном"
285
+ _map.area_link_button_klass.hide();
286
+
287
+ OpacityButtonsUtils.hide(_this.new_button);
288
+ OpacityButtonsUtils.hide(_this.remove_button);
289
+
290
+ _this.mzoom_buttons.css('opacity', '1');
291
+
292
+ break;
293
+
294
+ // начали редактировать площадь
295
+ case 'edit_area':
296
+
297
+ // спрячем кнопку "обратно на карту"
298
+ _map.back_to_map_button_klass.hide();
299
+
300
+ // покажем кнопку "связать площадь с полигоном"
301
+ _map.area_link_button_klass.show();
302
+
303
+ // покажем кнопку "сохранить изменения"
304
+ _map.save_button_klass.show();
305
+
306
+ // спрячем кнопку "создать полигон"
307
+ OpacityButtonsUtils.hide(_this.new_button);
308
+
309
+ // покажем кнопку "ред"
310
+ OpacityButtonsUtils.show(_this.edit_button);
311
+
312
+ _map.edit_button_klass.setState('edit_area', true); // [a1x7]
313
+
314
+ break;
315
+ }
316
+ };
317
+
318
+ _this.init = function (link_to_map) {
319
+ _map = link_to_map;
320
+ }
321
+
322
+ }
@@ -0,0 +1,23 @@
1
+ var MapUtils = {
2
+
3
+ svgOverlayHideAllExcept: function ($g) {
4
+ if ($g != null) {
5
+ console.log("<MapUtils.svgOverlayHideAllExcept>");
6
+
7
+ // убираем у всех g из svg_overlay класс viewing_area
8
+ $g.parent().find('g').css('display','none');
9
+
10
+ // а редактируемому полигону добавим класс viewing_area
11
+ $g.css('display','block');
12
+ }
13
+
14
+ },
15
+
16
+ svgOverlayRestore: function ($g) {
17
+ if ($g != null) {
18
+ console.log("<MapUtils.svgOverlayRestore>");
19
+ $g.parent().find('g').css('display','block');
20
+ }
21
+ }
22
+
23
+ };
@@ -0,0 +1,15 @@
1
+ var OpacityButtonsUtils = {
2
+
3
+ hide: function ($button) {
4
+ $button.css('opacity', '0');
5
+ setTimeout(function () { $button.css('display', 'none'); }, 200); // see map.scss: %ebutton
6
+ $button.addClass('mapplic-disabled');
7
+ },
8
+
9
+ show: function ($button) {
10
+ $button.css('display', 'block');
11
+ $button.css('opacity', '1');
12
+ $button.removeClass('mapplic-disabled');
13
+ }
14
+
15
+ };
@@ -0,0 +1,140 @@
1
+
2
+ var utils = {
3
+
4
+ // выдать из массива arr элемент с указанным id
5
+ getById: function (id,arr) {
6
+ var i;
7
+ var iel;
8
+ for (i=0; i<arr.length; i++) {
9
+ iel = arr[i];
10
+ if (iel.hasOwnProperty('id') && iel['id'] == id) {
11
+ return iel;
12
+ }
13
+ }
14
+ return null;
15
+ },
16
+
17
+ offsetX: function (node) {
18
+ var box = node.getBoundingClientRect(),
19
+ scroll = window.pageXOffset;
20
+
21
+ return Math.round(box.left + scroll);
22
+ },
23
+ offsetY: function (node) {
24
+ var box = node.getBoundingClientRect(),
25
+ scroll = window.pageYOffset;
26
+
27
+ return Math.round(box.top + scroll);
28
+ },
29
+
30
+ trim: function (str) {
31
+ return str.replace(/^\s+|\s+$/g, '');
32
+ },
33
+ id: function (str) {
34
+ return document.getElementById(str);
35
+ },
36
+ hide: function (node) {
37
+ node.style.display = 'none';
38
+
39
+ return this;
40
+ },
41
+ show: function (node) {
42
+ node.style.display = 'block';
43
+
44
+ return this;
45
+ },
46
+ encode: function (str) {
47
+ return str.replace(/</g, '&lt;').replace(/>/g, '&gt;');
48
+ },
49
+ decode: function (str) {
50
+ return str.replace(/&lt;/g, '<').replace(/&gt;/g, '>');
51
+ },
52
+ foreach: function (arr, func) {
53
+ for (var i = 0, count = arr.length; i < count; i++) {
54
+ func(arr[i], i);
55
+ }
56
+ },
57
+ foreachReverse: function (arr, func) {
58
+ for (var i = arr.length - 1; i >= 0; i--) {
59
+ func(arr[i], i);
60
+ }
61
+ },
62
+ debug: (function () {
63
+ var output = document.getElementById('debug');
64
+ return function () {
65
+ output.innerHTML = [].join.call(arguments, ' ');
66
+ }
67
+ })(),
68
+ stopEvent: function (e) {
69
+ e.stopPropagation();
70
+ e.preventDefault();
71
+
72
+ return this;
73
+ },
74
+ addClass: function (node, str) {
75
+ // node.className.baseVal for SVG-elements
76
+ // or
77
+ // node.className for HTML-elements
78
+ var is_svg = node.className.baseVal !== undefined ? true : false,
79
+ arr = is_svg ? node.className.baseVal.split(' ') : node.className.split(' '),
80
+ isset = false;
81
+
82
+ utils.foreach(arr, function (x) {
83
+ if (x === str) {
84
+ isset = true;
85
+ }
86
+ });
87
+
88
+ if (!isset) {
89
+ arr.push(str);
90
+ is_svg ? node.className.baseVal = arr.join(' ') : node.className = arr.join(' ');
91
+ }
92
+
93
+ return this;
94
+ },
95
+ removeClass: function (node, str) {
96
+ var is_svg = node.className.baseVal !== undefined ? true : false,
97
+ arr = is_svg ? node.className.baseVal.split(' ') : node.className.split(' '),
98
+ isset = false;
99
+
100
+ utils.foreach(arr, function (x, i) {
101
+ if (x === str) {
102
+ isset = true;
103
+ arr.splice(i--, 1);
104
+ }
105
+ });
106
+
107
+ if (isset) {
108
+ is_svg ? node.className.baseVal = arr.join(' ') : node.className = arr.join(' ');
109
+ }
110
+
111
+ return this;
112
+ },
113
+ hasClass: function (node, str) {
114
+ var is_svg = node.className.baseVal !== undefined ? true : false,
115
+ arr = is_svg ? node.className.baseVal.split(' ') : node.className.split(' '),
116
+ isset = false;
117
+
118
+ utils.foreach(arr, function (x) {
119
+ if (x === str) {
120
+ isset = true;
121
+ }
122
+ });
123
+
124
+ return isset;
125
+ },
126
+ extend: function (obj, options) {
127
+ var target = {};
128
+
129
+ for (var name in obj) {
130
+ if (obj.hasOwnProperty(name)) {
131
+ target[name] = options[name] ? options[name] : obj[name];
132
+ }
133
+ }
134
+
135
+ return target;
136
+ },
137
+ supportFileReader: (function () {
138
+ return (typeof FileReader !== 'undefined');
139
+ })()
140
+ };