src/server/controllers/editor.js   D
last analyzed

Complexity

Total Complexity 71
Complexity/F 3.94

Size

Lines of Code 300
Function Count 18

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 1 Features 0
Metric Value
cc 0
c 3
b 1
f 0
nc 1
dl 0
loc 300
rs 4.0769
wmc 71
mnd 5
bc 67
fnc 18
bpm 3.7222
cpm 3.9444
noi 13

10 Functions

Rating   Name   Duplication   Size   Complexity  
A editor.js ➔ orderByTabindex 0 9 3
C editor.js ➔ add 0 36 7
A editor.js ➔ insertAbeEach 0 14 4
A editor.js ➔ getDataIdWithNoSlash 0 8 2
A editor.js ➔ matchAttrAbe 0 8 2
A editor.js ➔ addSource 0 19 3
D editor.js ➔ each 0 44 10
C editor.js ➔ orderBlock 0 58 11
D editor.js ➔ addToForm 0 32 9
A editor.js ➔ editor 0 52 1

How to fix   Complexity   

Complexity

Complex classes like src/server/controllers/editor.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
import {Promise} from 'bluebird'
2
import path from 'path'
3
4
import {
5
  cmsData,
6
  cmsEditor,
7
  abeEngine,
8
  cmsTemplates,
9
  abeExtend
10
} from '../../cli'
11
12
function add(obj, json, text, util) {
13
  var value = obj.value
14
15
  if(obj.key.indexOf('[') > -1) {
16
    var key = obj.key.split('[')[0]
17
    var index = obj.key.match(/[^\[]+?(?=\])/)[0]
0 ignored issues
show
Unused Code introduced by
The variable index seems to be never used. Consider removing it.
Loading history...
18
    var prop = obj.key.replace(/[^\.]+?\./, '')
19
    key = getDataIdWithNoSlash(key)
20
21
    try {
22
      obj.value = eval('json[key][index].' + prop)
0 ignored issues
show
Security Performance introduced by
Calls to eval are slow and potentially dangerous, especially on untrusted code. Please consider whether there is another way to achieve your goal.
Loading history...
23
    } catch(e) {
24
25
      try {
26
        eval(`json[key][index].${prop} = ` + JSON.stringify(value))
0 ignored issues
show
Security Performance introduced by
Calls to eval are slow and potentially dangerous, especially on untrusted code. Please consider whether there is another way to achieve your goal.
Loading history...
27
      }catch(e) {
28
        // no value found inside json OKEY
29
      }
30
    }
31
  }else {
32
    try {
33
      obj.value = eval(`json.${getDataIdWithNoSlash(obj.key)}`)
0 ignored issues
show
Security Performance introduced by
Calls to eval are slow and potentially dangerous, especially on untrusted code. Please consider whether there is another way to achieve your goal.
Loading history...
34
    } catch(e) {
35
      // no value found inside json OKEY
36
    }
37
  }
38
39
  obj.key = getDataIdWithNoSlash(obj.key)
40
  if (json != null && json.abe_meta != null) {
41
    obj.status = json.abe_meta.status
42
  }
43
44
  util.add(obj)
45
46
  return value
47
}
48
49
function getDataIdWithNoSlash(key) {
50
  var trueKey = key
51
  if (trueKey.indexOf('/') > -1) {
52
    trueKey = trueKey.split('/')
53
    trueKey = trueKey[trueKey.length - 1]
54
  }
55
  return trueKey
56
}
57
58
function addToForm(match, text, json, util, arrayBlock, keyArray = null, i = 0) {
59
  var v = `{{${match}}}`,
60
    obj = cmsData.attributes.getAll(v, json)
61
62
  var realKey
63
64
  if(typeof keyArray !== 'undefined' && keyArray !== null) {
65
    realKey = obj.key.replace(/[^\.]+?\./, '')
66
67
    if(obj.key.indexOf(keyArray + '.') >= 0 && realKey.length > 0){
68
      obj.keyArray = keyArray
69
      obj.realKey = realKey
70
      obj.key = keyArray + '[' + i + '].' + realKey
71
      obj.desc = obj.desc + ' ' + i,
0 ignored issues
show
Comprehensibility introduced by
Usage of the sequence operator is discouraged, since it may lead to obfuscated code.

The sequence or comma operator allows the inclusion of multiple expressions where only is permitted. The result of the sequence is the value of the last expression.

This operator is most often used in for statements.

Used in another places it can make code hard to read, especially when people do not realize it even exists as a seperate operator.

This check looks for usage of the sequence operator in locations where it is not necessary and could be replaced by a series of expressions or statements.

var a,b,c;

a = 1, b = 1,  c= 3;

could just as well be written as:

var a,b,c;

a = 1;
b = 1;
c = 3;

To learn more about the sequence operator, please refer to the MDN.

Loading history...
72
      insertAbeEach(obj, text, json, util, arrayBlock)
73
74
    }else if(util.dontHaveKey(obj.key)) {
75
      obj.value = json[getDataIdWithNoSlash(obj.key)]
76
      json[getDataIdWithNoSlash(obj.key)] = add(obj, json, text, util)
77
    }
78
79
  }else if(util.dontHaveKey(obj.key) && cmsData.regex.isSingleAbe(v, text)) {
80
    realKey = obj.key//.replace(/\./g, '-')
81
    try {
82
      obj.value = eval(`json.${getDataIdWithNoSlash(realKey)}`)
0 ignored issues
show
Security Performance introduced by
Calls to eval are slow and potentially dangerous, especially on untrusted code. Please consider whether there is another way to achieve your goal.
Loading history...
83
    }catch(e) {
84
      obj.value = null
85
    }
86
    // json[getDataIdWithNoSlash(obj.key)] = 
87
    add(obj, json, text, util)
88
  }
89
}
90
91
function matchAttrAbe(text, json, util, arrayBlock) {
92
  var patt = /abe [^{{}}]+?(?=\}})/g,
93
    match
94
  // While regexp match HandlebarsJS template item => keepgoing
95
  while (match = patt.exec(text)) {
96
    addToForm(match[0], text, json, util, arrayBlock, null, null)
97
  }
98
}
99
100
function insertAbeEach (obj, text, json, util, arrayBlock) {
101
  if(typeof arrayBlock[obj.keyArray][obj.realKey] === 'undefined' || arrayBlock[obj.keyArray][obj.realKey] === null) {
102
    arrayBlock[obj.keyArray][obj.realKey] = []
103
  }
104
  var exist = false
105
  Array.prototype.forEach.call(arrayBlock[obj.keyArray][obj.realKey], (block) => {
106
    if(block.key === obj.key) {
107
      exist = true
108
    }
109
  })
110
  if(!exist) {
111
    arrayBlock[obj.keyArray][obj.realKey].push(obj)
112
  }
113
}
114
115
function each(text, json, util, arrayBlock) {
116
  let pattEach = /(\{\{#each (\r|\t|\n|.)*?\/each\}\})/g
117
  let patt = /{{(abe .*?["']) ?}}/g
118
  var textEach, match
119
120
  while (textEach = pattEach.exec(text)) {
121
    var i
122
    var keyArray = textEach[0].match(/#each (\n|.)*?\}/)
123
    keyArray = keyArray[0].slice(6, keyArray[0].length - 1)
124
125
    if(keyArray.split(' ').length > 1){
126
      keyArray = keyArray.split(' ')[0]
127
    }
128
    arrayBlock[keyArray] = []
129
    // ce while boucle sur les block de contenu {{abe}}
130
    while (match = patt.exec(textEach[0])) {
131
      var v = match[0]
132
133
      if(v.indexOf('abe') > -1){
134
        if(json[keyArray]){
135
          for (i = 0; i < json[keyArray].length; i++) {
136
            addToForm(v, text, json, util, arrayBlock, keyArray, i)
137
          }
138
        }else{
139
          addToForm(v, text, json, util, arrayBlock, keyArray, 0)
140
        }
141
      }
142
    }
143
144
    // ici on boucle a nouveau sur les champs pour les placer a la suite dans le formulaire
145
    var attrArray = [],
146
      length = 0
147
    for(var index in arrayBlock[keyArray]) {
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...
148
      attrArray.push(index)
149
      length = arrayBlock[keyArray][index].length
150
    }
151
152
    for (i = 0; i < length; i++) {
153
      for (var j = 0; j < attrArray.length; j++) {
154
        add(arrayBlock[keyArray][attrArray[j]][i], json, text, util)
155
      }
156
    }
157
  }
158
}
159
160
function addSource(text, json, util) {
161
  // removing each blocks potentially containing abe data type
162
  let pattEach = /(\{\{#each (\r|\t|\n|.)*?\/each\}\})/g
163
  text = text.replace(pattEach, '')
164
165
  var listReg = /({{abe.*type=[\'|\"]data.*}})/g
166
  var match
167
168
  while (match = listReg.exec(text)) {
169
    var obj = cmsData.attributes.getAll(match[0], json)
170
171
    if(obj.editable) {
172
      obj.value = json[getDataIdWithNoSlash(obj.key)]
173
      add(obj, json, text, util)
174
    }else {
175
      json[getDataIdWithNoSlash(obj.key)] = obj.source
176
    }
177
  }
178
}
179
180
function orderByTabindex(a, b) {
181
  if(a.order < b.order) {
182
    return -1
183
  }else if(a.order > b.order) {
184
    return 1
185
  }
186
187
  return 0
188
}
189
190
function orderBlock(util) {
191
    
192
  var formBlock = {}
193
194
  for(var tab in util.form) {
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...
195
196
    var formBlockTab = {}
197
    for (var i = 0; i < util.form[tab].item.length; i++) {
198
      var blockName = (util.form[tab].item[i].block === '') ? 'default_' + i : util.form[tab].item[i].block
199
      if(util.form[tab].item[i].key.indexOf('[') > -1){
200
        blockName = util.form[tab].item[i].key.split('[')[0]
201
      }
202
      if(typeof formBlockTab[blockName] === 'undefined' || formBlockTab[blockName] === null) {
203
        formBlockTab[blockName] = []
204
      }
205
      formBlockTab[blockName].push(util.form[tab].item[i])
206
    }
207
    if(typeof blockName !== 'undefined' && blockName !== null) {
0 ignored issues
show
Bug introduced by
The variable blockName seems to not be initialized for all possible execution paths.
Loading history...
208
      formBlockTab[blockName].sort(orderByTabindex)
209
    }
210
    if(typeof formBlock[tab] === 'undefined' || formBlock[tab] === null) {
211
      formBlock[tab] = {}
212
    }
213
214
    var formBlockOrdered = {}
215
    var arKeys = Object.keys(formBlockTab).sort((a,b) => {
216
      if(parseFloat(formBlockTab[a][0].order) < parseFloat(formBlockTab[b][0].order)) {
0 ignored issues
show
Bug introduced by
The variable formBlockTab is changed as part of the for-each loop for example by {} on line 196. Only the value of the last iteration will be visible in this function if it is called after the loop.
Loading history...
217
        return -1
218
      }else if(parseFloat(formBlockTab[a][0].order) > parseFloat(formBlockTab[b][0].order)) {
219
        return 1
220
      }
221
      return 0
222
    })
223
224
    Array.prototype.forEach.call(arKeys, (arKey) => {
225
      formBlockOrdered[arKey] = formBlockTab[arKey]
0 ignored issues
show
Bug introduced by
The variable formBlockTab is changed as part of the for-each loop for example by {} on line 196. Only the value of the last iteration will be visible in this function if it is called after the loop.
Loading history...
Bug introduced by
The variable formBlockOrdered is changed as part of the for-each loop for example by {} on line 214. Only the value of the last iteration will be visible in this function if it is called after the loop.
Loading history...
226
    })
227
    formBlock[tab] = formBlockOrdered
228
  }
229
230
  var formTabsOrdered = {}
231
  var arKeysTabs = Object.keys(formBlock).sort((a,b) => {
232
    if(parseFloat(formBlock[a][Object.keys(formBlock[a])[0]][0].order) < parseFloat(formBlock[b][Object.keys(formBlock[b])[0]][0].order)) {
233
      return -1
234
    }else if(parseFloat(formBlock[a][Object.keys(formBlock[a])[0]][0].order) > parseFloat(formBlock[b][Object.keys(formBlock[b])[0]][0].order)) {
235
      return 1
236
    }
237
    return 0
238
  })
239
240
  Array.prototype.forEach.call(arKeysTabs, (arKeysTab) => {
241
    if(arKeysTab !== 'slug') formTabsOrdered[arKeysTab] = formBlock[arKeysTab]
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
242
  })
243
244
  formTabsOrdered['slug'] = formBlock['slug']
245
246
  return formTabsOrdered
247
}
248
249
export function editor(text, json, documentLink, precontrib = false) {
250
  let p = new Promise((resolve) => {
251
    var util = new cmsEditor.form()
252
    var arrayBlock = []
253
    cmsData.source.getDataList(path.dirname(documentLink), text, json)
254
      .then(() => {
255
        addSource(text, json, util)
256
257
        text = cmsData.source.removeNonEachDataList(text)
258
259
        if (!precontrib) {
260
          text = cmsTemplates.template.setAbeSlugDefaultValueIfDoesntExist(text)
261
          text = cmsTemplates.template.setAbePrecontribDefaultValueIfDoesntExist(text)
262
        }
263
264
        matchAttrAbe(text, json, util, arrayBlock)
265
        arrayBlock = []
266
        each(text, json, util, arrayBlock)
267
268
        text = cmsData.source.removeDataList(text)
269
        if(typeof json.abe_meta !== 'undefined' && json.abe_meta !== null) {
270
          var links = json.abe_meta.link.split('/')
271
          var link = links.pop()
272
          json.abe_meta.cleanName = link.replace(/\..+$/, '')
273
          json.abe_meta.cleanFilename = links.join('/').replace(/\..+$/, '')
274
        }
275
276
        if (!precontrib) {
277
          // HOOKS beforeEditorFormBlocks
278
          json = abeExtend.hooks.instance.trigger('beforeEditorFormBlocks', json, text)
279
        }
280
281
        var blocks = orderBlock(util)
282
283
        if (!precontrib) {
284
          // HOOKS afterEditorFormBlocks
285
          blocks = abeExtend.hooks.instance.trigger('afterEditorFormBlocks', blocks, json, text)
286
        }
287
288
        abeEngine.instance.content = json
289
        resolve({
290
          text: text,
291
          form: blocks,
292
          json: json
293
        })
294
      }).catch(function(e) {
295
        console.error(e)
296
      })
297
  })
298
299
  return p
300
}