Test Failed
Push — master ( b74686...9a59de )
by Alexey
05:52
created

system/modules/Ui/static/js/Ui.js   F

Complexity

Total Complexity 95
Complexity/F 1.9

Size

Lines of Code 443
Function Count 50

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 0
nc 32
dl 0
loc 443
rs 3.12
c 0
b 0
f 0
wmc 95
mnd 3
bc 92
fnc 50
bpm 1.84
cpm 1.9
noi 14

15 Functions

Rating   Name   Duplication   Size   Complexity  
B Forms.submitAjax 0 44 5
A Forms.delRowFromList 0 3 1
B Ui.js ➔ Autocomplete 0 78 1
A Editors.loadAll 0 3 1
B Editors.loadIn 0 36 2
A Editors.beforeSubmit 0 12 2
B Forms.checkAditionals 0 22 5
A Ui.js ➔ Editors 0 10 1
A Editors.checkEditors 0 5 3
A Forms.popUp 0 23 3
A Forms.addRowToList 0 7 1
B Modals.show 0 38 5
A Ui.js ➔ Forms 0 4 1
A Ui.js ➔ Modals 0 3 1
A Ui.js ➔ ActiveForm 0 69 1

How to fix   Complexity   

Complexity

Complex classes like system/modules/Ui/static/js/Ui.js often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
/**
2
 * Main Ui object
3
 *
4
 * @returns {Ui}
5
 */
6
inji.Ui = new function () {
7
  inji.onLoad(function () {
8
    inji.Ui.bindMenu($('.nav-list-categorys'));
9
    inji.Ui.modals = new Modals();
10
    inji.Ui.forms = new Forms();
11
    inji.Ui.editors = new Editors();
12
    inji.Ui.autocomplete = new Autocomplete();
13
  });
14
15
  this.bindMenu = function (container) {
16
    container.find('.nav-left-ml').toggle();
17
    container.find('label.nav-toggle span').click(function () {
18
      $(this).parent().parent().children('ul.nav-left-ml').toggle(300);
19
      var cs = $(this).attr("class");
20
      if (cs == 'nav-toggle-icon glyphicon glyphicon-chevron-right') {
21
        $(this).removeClass('glyphicon-chevron-right').addClass('glyphicon-chevron-down');
22
      }
23
      if (cs == 'nav-toggle-icon glyphicon glyphicon-chevron-down') {
24
        $(this).removeClass('glyphicon-chevron-down').addClass('glyphicon-chevron-right');
25
      }
26
    });
27
  };
28
  this.requestInfo = function (options, callback) {
29
    var id = 'resultForm' + inji.randomString();
30
    var body = '<form id ="' + id + '">';
31
    body += '<h2>' + options.header + '</h2>';
32
    for (var key in options.inputs) {
0 ignored issues
show
Complexity introduced by
A for in loop automatically includes the property of any prototype object, consider checking the key using hasOwnProperty.

When iterating over the keys of an object, this includes not only the keys of the object, but also keys contained in the prototype of that object. It is generally a best practice to check for these keys specifically:

var someObject;
for (var key in someObject) {
    if ( ! someObject.hasOwnProperty(key)) {
        continue; // Skip keys from the prototype.
    }

    doSomethingWith(key);
}
Loading history...
33
      body += '<div class = "form-group">';
34
      body += '<label>' + options.inputs[key].label + '</label>';
35
      body += '<input type = "' + options.inputs[key].type + '" name = "' + key + '" class ="form-control" />';
36
      body += '</div>';
37
    }
38
    body += '<button class = "btn btn-primary">' + options.btn + '</button>';
39
    body += '</form>';
40
    var modal = inji.Ui.modals.show('', body);
41
    $('#' + id).on('submit', function () {
42
      callback($('#' + id).serializeArray());
43
      modal.modal('hide');
44
      return false;
45
    });
46
  }
47
};
48
49
function Autocomplete() {
50
  this.autocompletes = [];
51
  this.bind = function (element, options, params) {
52
    var autocomplete = new this.fn(element, options, params);
53
    element.element.__inji_autocomplete = autocomplete;
54
    this.autocompletes.push(autocomplete);
55
  };
56
  this.fn = function (element, snippet, snippetParams) {
57
    this.element = element;
58
    this.snippet = snippet;
59
    this.snippetParams = snippetParams;
60
    this.reqestProcess = null;
61
    this.inputContainer = element.element.parentNode;
62
    this.selectedDiv = this.inputContainer.querySelector('.form-search-cur');
63
    this.resultsDiv = this.inputContainer.querySelector('.form-search-results');
64
    this.changer = this.inputContainer.querySelector('.custominput-clear');
65
    this.hidden = this.inputContainer.querySelector('[type="hidden"]');
66
67
    var self = this;
68
    this.element.element.onkeyup = function () {
69
      self.loadResult(this.value);
70
    };
71
72
    this.clear = function () {
73
      this.setValue('', '')
74
    };
75
    this.setValue = function (value, text) {
76
      this.hidden.value = value;
77
      if (this.hidden.fireEvent !== undefined)
78
        this.hidden.fireEvent("onchange");
79
      else {
80
        var evt = document.createEvent("HTMLEvents");
81
        evt.initEvent("change", false, true);
82
        this.hidden.dispatchEvent(evt);
83
      }
84
      this.inputContainer.querySelector('[type="text"]').value = text;
85
      this.selectedDiv.innerHTML = 'Выбрано: ' + text;
86
      this.resultsDiv.style.display = 'none';
87
      this.changer.style.display = value ? 'block' : 'none';
88
    };
89
    this.loadResult = function (search) {
90
      if (this.reqestProcess) {
91
        this.reqestProcess.abort()
92
      }
93
      this.resultsDiv.innerHTML = '<div class = "text-center"><img src = "' + inji.options.appRoot + 'static/moduleAsset/Ui/images/ajax-loader.gif" /></div>';
94
      this.resultsDiv.style.display = 'block';
95
      this.reqestProcess = inji.Server.request({
96
        url: 'ui/autocomplete',
97
        data: {
98
          snippet: this.snippet,
99
          snippetParams: this.snippetParams,
100
          search: search
101
        },
102
        success: function (results) {
103
          self.resultsDiv.innerHTML = '';
104
          for (var key in results) {
0 ignored issues
show
Complexity introduced by
A for in loop automatically includes the property of any prototype object, consider checking the key using hasOwnProperty.

When iterating over the keys of an object, this includes not only the keys of the object, but also keys contained in the prototype of that object. It is generally a best practice to check for these keys specifically:

var someObject;
for (var key in someObject) {
    if ( ! someObject.hasOwnProperty(key)) {
        continue; // Skip keys from the prototype.
    }

    doSomethingWith(key);
}
Loading history...
105
            var result = results[key];
106
            var resultElement = document.createElement("div");
107
            resultElement.setAttribute('objectid', key);
108
            resultElement.appendChild(document.createTextNode(result));
109
            resultElement.onclick = function () {
110
              var value = 0;
111
              for (var key2 in this.attributes) {
0 ignored issues
show
Complexity introduced by
A for in loop automatically includes the property of any prototype object, consider checking the key using hasOwnProperty.

When iterating over the keys of an object, this includes not only the keys of the object, but also keys contained in the prototype of that object. It is generally a best practice to check for these keys specifically:

var someObject;
for (var key in someObject) {
    if ( ! someObject.hasOwnProperty(key)) {
        continue; // Skip keys from the prototype.
    }

    doSomethingWith(key);
}
Loading history...
112
                if (this.attributes[key2].name === 'objectid') {
113
                  value = this.attributes[key2].value;
114
                }
115
              }
116
              self.setValue(value, this.innerText);
117
            };
118
            self.resultsDiv.appendChild(resultElement);
119
          }
120
          self.resultsDiv.style.display = 'block';
121
        }
122
      });
123
    };
124
125
  };
126
}
127
128
/**
129
 * Editors
130
 *
131
 */
132
var Editors = function () {
133
  this.ckeditor = false;
134
  this.checkEditors();
135
  inji.on('loadScript', function () {
136
    inji.Ui.editors.checkEditors();
137
  });
138
  inji.onLoad(function () {
139
    inji.Ui.editors.loadIn('.htmleditor');
140
  })
141
};
142
Editors.prototype.checkEditors = function () {
143
  if (!this.ckeditor && typeof CKEDITOR != 'undefined') {
144
    this.ckeditor = true;
145
  }
146
};
147
Editors.prototype.loadAll = function () {
148
149
};
150
Editors.prototype.loadIn = function (selector, search) {
151
  if (this.ckeditor) {
152
    setTimeout(function () {
153
      var instances;
154
      if (typeof search != 'undefined') {
155
        instances = $(selector).find(search);
156
      } else {
157
        instances = $(selector);
158
      }
159
      $.each(instances, function () {
160
        var editor;
161
        var _this = this;
162
        if ($(this).closest('.modal').length == 0 || $(this).closest('.modal').hasClass('in')) {
163
          editor = $(_this).ckeditor({customConfig: inji.options.appRoot + 'static/moduleAsset/libs/libs/ckeditor/program/userConfig.php'});
164
        }
165
        if ($(this).closest('.modal').length != 0) {
166
          $(this).closest('.modal').on('shown.bs.modal', function () {
167
            setTimeout(function () {
168
              editor = $(_this).ckeditor({customConfig: inji.options.appRoot + 'static/moduleAsset/libs/libs/ckeditor/program/userConfig.php'});
169
            }, 1000);
170
          });
171
          $(this).closest('.modal').on('hide.bs.modal', function () {
172
            if (editor.editor) {
0 ignored issues
show
Bug introduced by
The variable editor does not seem to be initialized in case $(this).closest(".modal"....modal").hasClass("in") on line 162 is false. Are you sure this can never be the case?
Loading history...
173
              editor.editor.updateElement();
174
              editor.editor.destroy();
175
              delete editor.editor;
176
              $(this).closest('.modal').unbind('hide.bs.modal');
177
              $(this).closest('.modal').unbind('shown.bs.modal');
178
            }
179
180
          })
181
        }
182
      })
183
    }, 1000);
184
  }
185
};
186
Editors.prototype.beforeSubmit = function (form) {
187
  if (this.ckeditor) {
188
    $.each(CKEDITOR.instances, function () {
189
      this.updateElement();
190
    });
191
    $.each($(form).find('.cke'), function () {
192
      var instance = $(this).attr('id').replace('cke_', '');
193
      $(CKEDITOR.instances[instance].element).closest('.modal').unbind();
194
      CKEDITOR.instances[instance].destroy();
195
    });
196
  }
197
};
198
/**
199
 * Modals objects
200
 *
201
 * @returns {Modals}
202
 */
203
var Modals = function () {
204
  this.modals = 0;
205
};
206
Modals.prototype.show = function (title, body, code, size) {
207
  if (code == null) {
208
    code = 'modal' + (++this.modals);
209
  }
210
  if ($('#' + code).length == 0) {
211
    if (size == null) {
212
      size = '';
213
    }
214
    if (title) {
215
      title = '<div class="modal-header">\
216
                  <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>\
217
                  <h4 class="modal-title">' + title + '</h4>\
218
                </div>';
219
    } else {
220
      title = '';
221
    }
222
    var html = '\
223
          <div class="modal fade" id = "' + code + '" >\
224
            <div class="modal-dialog ' + size + '">\
225
              <div class="modal-content">\
226
                ' + title + '\
227
                <div class="modal-body">\
228
                ' + body + '\
229
                </div>\
230
                <div class="modal-footer">\
231
                  <button type="button" class="btn btn-default" data-dismiss="modal">Закрыть</button>\
232
                </div>\
233
              </div>\
234
            </div>\
235
          </div>';
236
    $('body').append(html);
237
238
  }
239
  var modal = $('#' + code);
240
  $('body').append(modal);
241
  modal.modal('show');
242
  return modal;
243
};
244
245
/**
246
 * Forms object
247
 *
248
 * @returns {Forms}
249
 */
250
function Forms() {
251
  this.dataManagers = 0;
252
  this.formCallbacks = {};
253
}
254
255
Forms.prototype.popUp = function (item, params, callback) {
256
  var code = item;
257
258
  if (typeof params == 'undefined') {
259
    params = {};
260
  }
261
  if (typeof (params.relation) != 'undefined') {
262
    code += params.relation;
263
  }
264
  code = code.replace(/:/g, '_').replace(/\\/g, '_');
265
  var modal = inji.Ui.modals.show('', '<div class = "text-center"><img src = "' + inji.options.appRoot + 'static/moduleAsset/Ui/images/ajax-loader.gif" /></div>', code, 'modal-lg');
266
  inji.Server.request({
267
    url: 'ui/formPopUp/',
268
    data: {item: item, params: params},
269
    success: function (data) {
270
      modal.find('.modal-body').html(data);
271
      if (callback) {
272
        inji.Ui.forms.formCallbacks[modal.find('.form').attr('id')] = callback;
273
      }
274
      inji.Ui.editors.loadIn(modal.find('.modal-body'), '.htmleditor');
275
    }
276
  });
277
};
278
Forms.prototype.submitAjax = function (form, params) {
279
  inji.Ui.editors.beforeSubmit(form);
280
  var form = $(form);
281
  var container = form.parent().parent();
282
  var btn = form.find('button');
283
  btn.text('Подождите');
284
  btn[0].disabled = true;
285
  btn.data('loading-text', "Подождите");
286
287
  var url = form.attr('action');
288
  if (params) {
289
    var first = true;
290
    if (url.indexOf('?') >= 0) {
291
      first = false;
292
    }
293
    for (var key in params) {
0 ignored issues
show
Complexity introduced by
A for in loop automatically includes the property of any prototype object, consider checking the key using hasOwnProperty.

When iterating over the keys of an object, this includes not only the keys of the object, but also keys contained in the prototype of that object. It is generally a best practice to check for these keys specifically:

var someObject;
for (var key in someObject) {
    if ( ! someObject.hasOwnProperty(key)) {
        continue; // Skip keys from the prototype.
    }

    doSomethingWith(key);
}
Loading history...
294
      url += (first ? '?' : '&') + key + '=' + params[key];
295
    }
296
  }
297
  var formData = new FormData(form[0]);
298
  inji.Server.request({
299
    url: url,
300
    type: 'POST',
301
    data: formData,
302
    processData: false,
303
    success: function (data) {
304
      if (inji.Ui.forms.formCallbacks[form.attr('id')]) {
305
        inji.Ui.forms.formCallbacks[form.attr('id')]();
306
        delete inji.Ui.forms.formCallbacks[form.attr('id')];
307
      }
308
      container.html(data);
309
      inji.Ui.editors.loadIn(container, '.htmleditor');
310
      inji.Ui.dataManagers.reloadAll();
311
      if (params && !params.notSave) {
312
        var btn = container.find('form button');
313
        var text = btn.text();
314
        btn.text('Изменения сохранены!');
315
        setTimeout(function () {
316
          btn.text(text)
317
        }, 3000);
318
      }
319
    }
320
  });
321
};
322
Forms.prototype.addRowToList = function (btn) {
323
  var container = $(btn).closest('.dynamicList');
324
  var counter = parseInt(container.find('.sourceRow').data('counter')) + 1;
325
  container.find('.sourceRow').data('counter', counter);
326
  var trHtml = container.find('.sourceRow script').html().replace(/^\/\*/g, '').replace(/\*\/$/g, '').replace(/\[counterPlaceholder\]/g, '[' + counter + ']');
327
  container.find('.listBody').append(trHtml);
328
};
329
Forms.prototype.checkAditionals = function (select) {
330
  var selectedInputAd = $(select).find('option:selected').attr('data-aditionalInput');
331
  var nextSelect = $(select).next();
332
  var i = 0;
333
  if ($(select).data('aditionalEnabled') == 1) {
334
    $(select).data('aditionalEnabled', 0);
335
  }
336
  while (nextSelect.length) {
337
    if (i != selectedInputAd) {
338
      nextSelect[0].disabled = true;
339
      nextSelect.addClass('hidden');
340
    } else {
341
      if ($(select).data('aditionalEnabled') != 1) {
342
        $(select).data('aditionalEnabled', 1);
343
      }
344
      nextSelect[0].disabled = false;
345
      nextSelect.removeClass('hidden');
346
    }
347
    nextSelect = $(nextSelect).next();
348
    i++;
349
  }
350
};
351
Forms.prototype.delRowFromList = function (btn) {
352
  $(btn).closest('tr').remove();
353
};
354
355
inji.Ui.activeForms = new function () {
356
  this.activeForms = [];
357
  this.get = function (selector) {
358
    var element = inji.get(selector);
359
    if (element && element.data('activeFormIndex') !== null) {
360
      return this.activeForms[element.data('activeFormIndex')];
361
    }
362
    this.initial(element);
363
  };
364
  this.initial = function (element) {
365
    var activeForm = new ActiveForm();
366
    this.activeForms.push(activeForm);
367
368
    activeForm.index = this.activeForms.length - 1;
369
    activeForm.element = element;
370
    activeForm.modelName = element.data('modelname');
371
    activeForm.formName = element.data('formname');
372
    activeForm.inputs = element.data('inputs');
373
374
    element.element.setAttribute('activeFormIndex', activeForm.index);
375
376
    activeForm.load();
377
  }
378
};
379
380
function ActiveForm() {
381
  this.modelName;
0 ignored issues
show
introduced by
The result of the property access to this.modelName is not used.
Loading history...
382
  this.formName;
0 ignored issues
show
introduced by
The result of the property access to this.formName is not used.
Loading history...
383
  this.reqestProcess;
0 ignored issues
show
introduced by
The result of the property access to this.reqestProcess is not used.
Loading history...
384
  this.inputs = {};
385
  this.index;
0 ignored issues
show
introduced by
The result of the property access to this.index is not used.
Loading history...
386
  this.element;
0 ignored issues
show
introduced by
The result of the property access to this.element is not used.
Loading history...
387
  this.load = function () {
388
    for (var inputName in this.inputs) {
0 ignored issues
show
Complexity introduced by
A for in loop automatically includes the property of any prototype object, consider checking the key using hasOwnProperty.

When iterating over the keys of an object, this includes not only the keys of the object, but also keys contained in the prototype of that object. It is generally a best practice to check for these keys specifically:

var someObject;
for (var key in someObject) {
    if ( ! someObject.hasOwnProperty(key)) {
        continue; // Skip keys from the prototype.
    }

    doSomethingWith(key);
}
Loading history...
389
      var inputParams = this.inputs[inputName];
390
      var self = this;
391
      if (this.inputHandlers[inputParams.type]) {
392
        var query = '#' + this.element.element.id + ' [name="query-ActiveForm_' + this.formName + '[' + this.modelName.replace(/\\/g, '\\\\') + '][' + inputName + ']"]';
393
        this.inputHandlers[inputParams.type](inji.get(query), inputName, this);
394
      }
395
      if (inputParams.onChange == 'reloadForm') {
396
        var query = '#' + this.element.element.id + ' [name="ActiveForm_' + this.formName + '[' + this.modelName.replace(/\\/g, '\\\\') + '][' + inputName + ']"]';
397
        $(query).on('change', function () {
398
          inji.Ui.forms.submitAjax($('#' + self.element.element.id + ' form')[0], {notSave: true});
0 ignored issues
show
Bug introduced by
The variable self is changed as part of the for-each loop for example by this on line 390. Only the value of the last iteration will be visible in this function if it is called after the loop.
Loading history...
399
        })
400
      }
401
    }
402
  };
403
  this.inputHandlers = {
404
    search: function (element, inputName, activeForm) {
405
      element.element.onkeyup = function () {
406
        var inputContainer = element.element.parentNode;
407
        var selectedDiv = inputContainer.querySelector('.form-search-cur');
408
        var resultsDiv = inputContainer.querySelector('.form-search-results');
409
        resultsDiv.innerHTML = '<div class = "text-center"><img src = "' + inji.options.appRoot + 'static/moduleAsset/Ui/images/ajax-loader.gif" /></div>';
410
        if (this.reqestProcess) {
411
          this.reqestProcess.abort()
412
        }
413
        this.reqestProcess = inji.Server.request({
414
          url: 'ui/activeForm/search',
415
          data: {
416
            modelName: activeForm.modelName,
417
            formName: activeForm.formName,
418
            inputName: inputName,
419
            search: this.value
420
          },
421
          success: function (results) {
422
            resultsDiv.innerHTML = '';
423
            for (var key in results) {
0 ignored issues
show
Complexity introduced by
A for in loop automatically includes the property of any prototype object, consider checking the key using hasOwnProperty.

When iterating over the keys of an object, this includes not only the keys of the object, but also keys contained in the prototype of that object. It is generally a best practice to check for these keys specifically:

var someObject;
for (var key in someObject) {
    if ( ! someObject.hasOwnProperty(key)) {
        continue; // Skip keys from the prototype.
    }

    doSomethingWith(key);
}
Loading history...
424
              var result = results[key];
425
              var resultElement = document.createElement("div");
426
              resultElement.setAttribute('objectid', key);
427
              resultElement.appendChild(document.createTextNode(result));
428
              resultElement.onclick = function () {
429
                var value = 0;
430
                for (key in this.attributes) {
431
                  if (this.attributes[key].name == 'objectid') {
0 ignored issues
show
introduced by
The variable key is changed by the for-each loop on line 430. Only the value of the last iteration will be visible in this function if it is called outside of the loop.
Loading history...
432
                    value = this.attributes[key].value;
433
                  }
434
                }
435
                inputContainer.querySelector('[type="hidden"]').value = value;
436
                inputContainer.querySelector('[type="text"]').value = this.innerHTML;
437
                selectedDiv.innerHTML = 'Выбрано: ' + this.innerHTML;
438
                resultsDiv.innerHTML = '';
439
              };
440
              resultsDiv.appendChild(resultElement);
441
            }
442
            resultsDiv.style.display = 'block';
443
          }
444
        })
445
      };
446
    }
447
  };
448
}