Completed
Push — master ( 395a8a...5b5be3 )
by greg
02:00
created

EditorBlock.js ➔ ???   B

Complexity

Conditions 5
Paths 16

Size

Total Lines 31

Duplication

Lines 0
Ratio 0 %

Importance

Changes 14
Bugs 7 Features 0
Metric Value
cc 5
c 14
b 7
f 0
nc 16
nop 0
dl 0
loc 31
rs 8.439
1
/*global document, abe, $, jQuery */
2
3
import {IframeNode, IframeCommentNode} from '../utils/iframe'
4
import {nextSibling} from '../utils/dom'
5
import Color from '../utils/color-picker'
6
import Link from '../utils/link-picker'
7
import image from '../utils/img-picker'
8
import smiley from '../utils/smiley-picker'
9
import RichText from '../utils/rich-texarea'
10
import Json from './EditorJson'
11
import EditorUtils from './EditorUtils'
12
import on from 'on'
13
14
export default class EditorBlock {
15
  constructor() {
16
    this._json = Json.instance
17
    var colorWysiwyg = document.querySelector('.wysiwyg-popup.color')
18
    if (colorWysiwyg != null) {
19
      this.color = new Color(colorWysiwyg)
20
    }
21
    var linkWysiwyg = document.querySelector('.wysiwyg-popup.link')
22
    if (linkWysiwyg != null) {
23
      this.link = new Link(linkWysiwyg)
24
    }
25
    var imgWysiwyg = document.querySelector('.wysiwyg-popup.image')
26
    if (imgWysiwyg != null) {
27
      this.image = new image(imgWysiwyg)
0 ignored issues
show
Coding Style Best Practice introduced by
By convention, constructors like image should be capitalized.
Loading history...
28
    }
29
30
    var imgWysiwyg = document.querySelector('.wysiwyg-popup.smiley')
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable imgWysiwyg already seems to be declared on line 25. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
31
    if (imgWysiwyg != null) {
32
      this.smiley = new smiley(imgWysiwyg)
0 ignored issues
show
Coding Style Best Practice introduced by
By convention, constructors like smiley should be capitalized.
Loading history...
33
    }
34
35
    this._removeblock = [].slice.call(document.querySelectorAll('.list-group[data-block]'))
36
    this._handleClickRemoveBlock = this._clickRemoveBlock.bind(this)
37
    
38
    this._addblock = [].slice.call(document.querySelectorAll('.add-block'))
39
    this._handleClickAddBlock = this._clickAddBlock.bind(this)
40
41
    this.onNewBlock = on(this)
42
    this.onRemoveBlock = on(this)
43
44
    this._bindEvents()
45
  }
46
47
  /**
48
   * bind events
49
   * @return {[type]} [description]
50
   */
51
  _bindEvents() {
52
    this._removeblock.forEach((block) => {
53
      block.addEventListener('click', this._handleClickRemoveBlock)
54
    })
55
    this._addblock.forEach((block) => {
56
      block.addEventListener('click', this._handleClickAddBlock)
57
    })
58
  }
59
60
  /**
61
   * event remove block
62
   * @param  {[type]} e [description]
63
   * @return {[type]}   [description]
64
   */
65
  _clickRemoveBlock(e) {
66
    var target = e.target
67
      , elem = target
68
      , parent = null
69
      , listGroup = null
70
      , blockAttr = ''
71
      , wasFound = false
72
      , startNumber = 0
73
      , endNumber = 0
74
    
75
    if(elem.classList.contains('glyphicon-trash') || elem.classList.contains('remove-block')){
76
      for(; elem && elem !== document; elem = elem.parentNode){
77
        if (elem.hasAttribute('data-block')) {
78
          parent = elem
79
          listGroup = parent.parentNode
80
          blockAttr = listGroup.getAttribute('data-block')
81
          break
82
        }
83
      }
84
    }
85
86
    if(parent && listGroup){
87
      if(listGroup.querySelectorAll('[data-block]').length === 1){
88
        var items = IframeNode('#page-template', `[data-abe-block="${blockAttr}0"]`)
89
        Array.prototype.forEach.call(items, (item) => {
90
          item.parentNode.removeChild(item)
91
        })
92
        var child = document.querySelector(`[data-block=${blockAttr}0]`)
93
        child.style.display = 'none'
94
        Array.prototype.forEach.call(child.querySelectorAll('.form-abe'), (item) => {
95
          item.value = ''
96
        })
97
        delete abe.json._data[blockAttr][0]
98
      }
99
      else{
100
        var toRemove = null
101
        Array.prototype.forEach.call(listGroup.querySelectorAll('[data-block]'), (block) => {
102
          var currentBlockAttr = block.getAttribute('data-block')
103
          var nb = parseInt(currentBlockAttr.replace(blockAttr, ''))
104
          if(wasFound){
105
            Array.prototype.forEach.call(listGroup.querySelectorAll('.form-abe'), (el) => {
106
              el.setAttribute('value', el.value)
107
            })
108
            var blockId = blockAttr + (nb-1)
109
            var html = block.innerHTML
110
111
            html = html.replace(/data-block=(\'|\")(.*)(\'|\")/g, `data-block="${blockId}"`)
112
            html = html.replace(/data-target=(\'|\")(.*)(\'|\")/g, `data-target="#${blockId}"`)
113
            html = html.replace(new RegExp('id=(' + '\\\'' + '|\\"' + ')' + blockAttr + '(\\d+)(' + '\\\'' + '|\\"' + ')', 'g'), `id="${blockId}"`)
114
            html = html.replace(/\[(\d+)\]/g, `[${nb-1}]`)
115
            block.innerHTML = html
116
            block.setAttribute('data-block', blockAttr + (nb - 1))
117
            var labelCount = block.querySelector('.label-count')
118
            labelCount.textContent = parseInt(labelCount.textContent) - 1
119
            Array.prototype.forEach.call(block.querySelectorAll('label'), (label) => {
120
              label.textContent = label.textContent.replace(new RegExp(nb, 'g'), nb - 1)
121
            })
122
123
            Array.prototype.forEach.call(IframeNode('#page-template', `[data-abe-block="${blockAttr + nb}"]`), (el) => {
124
              el.parentNode.removeChild(el)
125
            })
126
            
127
            endNumber = nb
128
          }
129
          else if(currentBlockAttr === parent.getAttribute('data-block')){
130
            Array.prototype.forEach.call(IframeNode('#page-template', `[data-abe-block="${blockAttr + nb}"]`), (el) => {
131
              el.parentNode.removeChild(el)
132
            })
133
134
            toRemove = block
135
            wasFound = true
136
            startNumber = nb
137
          }
138
        })
139
140
        toRemove.remove()
141
142
        var json = this._json.data
143
        for(var i = startNumber; i < endNumber; i++){
144
          this._insertNewBlock(blockAttr, i)
145
          Array.prototype.forEach.call(document.querySelectorAll('[data-block="' + blockAttr + i + '"] .form-abe'), (el) => {
146
            var key = el.getAttribute('data-id').split('-')
147
            if(key){
148
              key = key[1]
149
              json[blockAttr][i][key] = el.value
0 ignored issues
show
Bug introduced by
The variable i is changed as part of the for loop for example by i++ on line 143. Only the value of the last iteration will be visible in this function if it is called after the loop.
Loading history...
150
              var nodes = EditorUtils.getNode(EditorUtils.getAttr(el))
151
              Array.prototype.forEach.call(nodes, (node) => {
152
                EditorUtils.formToHtml(node, el)
153
              })
154
            }
155
          })
156
        }
157
        json[blockAttr].pop()
158
        this._json.data = json
159
      }
160
    }
161
162
    this.onRemoveBlock._fire()
163
  }
164
165
  /**
166
   * event add new block
167
   * @param  {[type]} e [description]
168
   * @return {[type]}   [description]
169
   */
170
  _clickAddBlock(e) {
171
    let target = e.currentTarget
172
    var dataLink = target.getAttribute('data-id-link')
173
    var prevListItem = target.parentNode.parentNode.querySelectorAll('.list-block')
174
    var listGroupItem = prevListItem.length
175
    prevListItem = prevListItem[prevListItem.length - 1]
176
177
    var attrId = (typeof dataLink !== 'undefined' && dataLink !== null) ? 'data-id-link' : 'data-id'
178
      , itemNumber = 0
179
      , newNumber = 0
180
      , rex = new RegExp(itemNumber, 'g')
181
182
    if(listGroupItem > 1 || (listGroupItem === 1 && prevListItem.style.display !== 'none')) {
183
      newNumber = this._createNewBlock(prevListItem, itemNumber, newNumber)
184
      rex = new RegExp(newNumber - 1, 'g')
185
    }else {
186
      prevListItem.style.display = 'block'
187
    }
188
189
    prevListItem = target.parentNode.parentNode.querySelectorAll('.list-block')
190
    prevListItem = prevListItem[prevListItem.length - 1]
191
    var prevButton = prevListItem.querySelector('button')
192
    var dataTarget = prevButton.getAttribute('data-target')
193
    var newTarget = dataTarget.replace(rex, newNumber)
194
195
    var contentListItem = prevListItem.querySelector(dataTarget)
196
    contentListItem.setAttribute('id', newTarget.slice(1))
197
    contentListItem.setAttribute(attrId, newTarget.slice(1))
198
199
    prevButton.setAttribute('data-target', newTarget)
200
    this._insertNewBlock(prevListItem.parentNode.getAttribute('data-block'), newNumber)
201
    var labels = prevListItem.querySelectorAll('label')
202
    Array.prototype.forEach.call(labels, (label) => {
203
      label.innerHTML = label.innerHTML.replace(rex, newNumber)
204
    })
205
206
    if($(target).parents('.list-group').find('.list-block').size() > 1){
207
      prevListItem.querySelector('.label-count').textContent = parseInt(prevListItem.querySelector('.label-count').textContent) + 1
208
    }
209
210
    this.onNewBlock._fire()
211
212
    if(typeof jQuery !== 'undefined' && jQuery !== null){ // Bootstrap collapse
213
      var blocks = $(target).parents('.list-group').find('.list-block > [data-id]')
214
      $(target).parents('.list-group').find('.list-block .collapse').collapse('hide')
215
      setTimeout(function () {
216
        $('#' + blocks[blocks.length - 1].id).collapse('show')
217
      }, 200)
218
    }
219
220
  }
221
222
  /**
223
   * insert node page side
224
   * @param  {[type]} dataBlock [description]
225
   * @param  {[type]} newNumber [description]
226
   * @return {[type]}           [description]
227
   */
228
  _insertNewBlock(dataBlock, newNumber) {
229
    var blockContent = IframeCommentNode('#page-template', dataBlock)
230
    if(typeof blockContent !== 'undefined' && blockContent !== null && blockContent.length > 0) {
231
      blockContent = blockContent[0]
232
      var blockHtml = unescape(blockContent.textContent.replace(/\[\[([\S\s]*?)\]\]/, ''))
233
                        .replace(new RegExp(`-${dataBlock}0`, 'g'), `-${dataBlock}${newNumber}`)
234
                        .replace(/\[0\]-/g, '' + newNumber + '-')
235
      var newBlock = document.createElement('abe')
236
      newBlock.innerHTML = blockHtml
237
238
      var childs = [].slice.call(newBlock.childNodes)
239
      Array.prototype.forEach.call(childs, (child) => {
240
        if(typeof child.setAttribute !== 'undefined' && child.setAttribute !== null) {
241
          child.setAttribute('data-abe-block', dataBlock + newNumber)
242
        }
243
        blockContent.parentNode.insertBefore(child, blockContent)
244
      })
245
      
246
    }
247
  }
248
249
  /**
250
   * remove default value into a form
251
   * @param  {[type]} block [description]
252
   * @return {[type]}       [description]
253
   */
254
  _unValueForm(block) {
255
256
    var inputs = [].slice.call(block.querySelectorAll('input'))
257
    Array.prototype.forEach.call(inputs, (input) => {
258
      input.value = ''
259
    })
260
261
    var textareas = [].slice.call(block.querySelectorAll('textarea'))
262
    Array.prototype.forEach.call(textareas, (textarea) => {
263
      textarea.value = ''
264
    })
265
266
    // var contenteditables = [].slice.call(block.querySelectorAll('[contenteditable]'))
267
    // Array.prototype.forEach.call(contenteditables, (contenteditable) => {
268
    //   contenteditable.innerHTML = ''
269
    // })
270
271
    var selects = [].slice.call(block.querySelectorAll('select'))
272
    Array.prototype.forEach.call(selects, (select) => {
273
      select.value = ''
274
275
      var options = [].slice.call(select.querySelectorAll('option'))
276
      Array.prototype.forEach.call(options, (option) => {
277
        option.removeAttribute('selected')
278
      })
279
    })
280
  }
281
282
  /**
283
   * Create admin side block
284
   * @param  {[type]} prevListItem [description]
285
   * @param  {[type]} itemNumber   [description]
286
   * @param  {[type]} newNumber    [description]
287
   * @return {[type]}              [description]
288
   */
289
  _createNewBlock(prevListItem, itemNumber, newNumber) {
290
    var htmlBlockItem = prevListItem.innerHTML
291
292
    htmlBlockItem = htmlBlockItem.replace(/\[(.*?)\]/g, function(val, $_1) {
293
      itemNumber = parseInt($_1)
294
      newNumber = itemNumber + 1
295
      return '[' + newNumber + ']'
296
    })
297
    var rex = new RegExp(itemNumber, 'g')
298
    
299
    var dataBlock = prevListItem.getAttribute('data-block').replace(rex, newNumber)
300
301
    var newBlock = document.createElement('div')
302
    newBlock.classList.add('list-block')
303
    newBlock.setAttribute('data-block', dataBlock)
304
    newBlock.innerHTML = htmlBlockItem
305
    var next = nextSibling(prevListItem.parentNode, prevListItem)
306
    prevListItem.parentNode.insertBefore(newBlock, next)
307
    this._unValueForm(newBlock)
308
309
    var richs = [].slice.call(newBlock.querySelectorAll('[contenteditable]'))
310
    if(typeof richs !== 'undefined' && richs !== null && richs.length > 0) {
311
      Array.prototype.forEach.call(richs, (rich) => {
312
        rich.remove()
313
      })
314
      var newRichs = [].slice.call(newBlock.querySelectorAll('.rich'))
315
      Array.prototype.forEach.call(newRichs, (newRich) => {
316
        new RichText(newRich, this.color, this.link, this.image, this.smiley)
0 ignored issues
show
Unused Code Best Practice introduced by
The object created with new RichText(newRich, th...his.image, this.smiley) is not used but discarded. Consider invoking another function instead of a constructor if you are doing this purely for side effects.
Loading history...
317
      })
318
    }
319
320
    return newNumber
321
  }
322
}