PageSourceSearch

https://www.habitatparticipatif-france.fr/tools/bazar/presentation…/bazar-fields/conditionschecking.js?v=4.4.4

js habitatparticipatif-france.fr collected 2026-10-01 11:49:48 UTC 23,119 bytes, 673 lines download raw bytes

1const ConditionsChecking = {
2  conditionsCache: [],
3  fieldNamesCache: {},
4  triggersCache: {},
5  boolList: ['false', 'true'],
6  operationsList: ['!(', 'not(', 'not (', '(', ')'],
7  operationsListIncludInSpaceParenthesis: ['and', 'or'],
8  conditionsList: ['==', '!=', ' in', '|length ==', '|length !=', '|length <', '|length <=', '|length >=', '|length >', ' is empty', ' is not empty'],
9  pregQuote(input) {
10    return (`${input}`).replace(new RegExp('[.\\[\\]\\^(){}!=\\\\+*?$<>|:]', 'g'), '\\$&')
11  },
12  updateOperationData(data, rest, condition, element) {
13    const newIndex = (rest == undefined) ? -1 : condition.length - rest[0].length
14    if (Object.keys(data).length == 0 || data.indexOf == undefined) {
15      data.indexOf = -1
16    }
17    if (newIndex > -1 && (newIndex < data.indexOf || data.indexOf < 0)) {
18      data.indexOf = newIndex
19      data.element = element
20      data.fullRest = rest[1]
21    }
22  },
23  updateObject(data, condition, names) {
24    const result = {}
25    if (data.indexOf < 0) {
26      result[names.current] = condition
27      result[names.rest] = ''
28      result[names.element] = ''
29    } else {
30      result[names.current] = condition.substr(0, data.indexOf).trim()
31      result[names.element] = data.element
32      result[names.rest] = condition.substr(data.indexOf + data.fullRest.length).trim()
33    }
34    return result
35  },
36  getFirstOperation(parsingObject) {
37    const condition = parsingObject.restOfCondition.trim()
38    const data = {}
39    for (let index = 0; index < this.operationsList.length; index++) {
40      const element = this.operationsList[index]
41      const rest = condition.match(new RegExp(`(${this.pregQuote(element)})(.*)$`, 'i'))
42      this.updateOperationData(data, rest, condition, element)
43    }
44    for (let index = 0; index < this.operationsListIncludInSpaceParenthesis.length; index++) {
45      const element = this.operationsListIncludInSpaceParenthesis[index]
46      const rest = condition.match(new RegExp(`((?<= |\\)|^)${element}(?= |\\)))(.*)$`, 'i'))
47      this.updateOperationData(data, rest, condition, element)
48    }
49    return this.updateObject(data, condition, { current: 'currentCondition', rest: 'restOfCondition', element: 'operation' })
50  },
51  addCondition(condition) {
52    const conditionLocal = condition.trim()
53    const data = {}
54    for (let index = 0; index < this.conditionsList.length; index++) {
55      const element = this.conditionsList[index]
56      const rest = conditionLocal.match(new RegExp(`(${this.pregQuote(element)})(.*)$`, 'i'))
57      this.updateOperationData(data, rest, conditionLocal, element)
58    }
59    for (let index = 0; index < this.boolList.length; index++) {
60      const element = this.boolList[index]
61      const rest = conditionLocal.match(new RegExp(`(${this.pregQuote(element)})(.*)$`, 'i'))
62      this.updateOperationData(data, rest, conditionLocal, element)
63    }
64    return this.updateObject(data, conditionLocal, { current: 'leftPart', rest: 'rightPart', element: 'typeOfCondition' })
65  },
66  getCheckboxValues(field) {
67    const result = []
68    $(field).find('input[type=checkbox]').each(function() {
69      if ($(this).prop('checked') == true) {
70        const name = $(this).attr('name')
71        let value = name.match(/\[[A-Za-z0-9_-]+\]/)[0]
72        value = value.substr(1, value.length - 2)
73        result.push(value)
74      }
75    })
76    return result
77  },
78  getCheckboxTagValues(field) {
79    let result = []
80    const value = $(field).val()
81    if (value.trim() != '') {
82      result = value.split(',')
83    }
84    return result
85  },
86  getRadioValues(inputs) {
87    const result = []
88    $(inputs).each(function() {
89      if ($(this).prop('checked') == true) {
90        result.push($(this).attr('value'))
91      }
92    })
93    return result
94  },
95  getSelectValues(field) {
96    const result = []
97    const value = $(field).val()
98    if (value.trim() != '') {
99      result.push(value.trim())
100    }
101    return result
102  },
103  getFieldNameValues(fieldName) {
104    if (typeof this.fieldNamesCache[fieldName] === 'undefined') {
105      return []
106    }
107    const fieldData = this.fieldNamesCache[fieldName]
108    switch (fieldData.type) {
109      case 'checkbox':
110        return this.getCheckboxValues(fieldData.node)
111      case 'checkboxtag':
112        return this.getCheckboxTagValues(fieldData.node)
113      case 'radio':
114        return this.getRadioValues(fieldData.node)
115      case 'select':
116        return this.getSelectValues(fieldData.node)
117      default:
118        break
119    }
120    return []
121  },
122  extractValues(values) {
123    if (values.trim() == '') {
124      return []
125    }
126    let tempValues = values.trim()
127    if (tempValues.substr(0, 1) == '[' && tempValues.substr(-1) == ']') {
128      tempValues = tempValues.substr(1, tempValues.length - 2)
129    }
130    return tempValues.split(',')
131  },
132  commonForOperations(fieldName, values, extract) {
133    const fieldValues = this.getFieldNameValues(fieldName)
134    const extractedValues = this.extractValues(values)
135    for (let index = 0; index < extractedValues.length; index++) {
136      if (extract.uniqueValues.indexOf(extractedValues[index].trim()) == -1) {
137        extract.uniqueValues.push(extractedValues[index].trim())
138      }
139    }
140    for (let index = 0; index < fieldValues.length; index++) {
141      if (extract.uniqueFieldValues.indexOf(fieldValues[index].trim()) == -1) {
142        extract.uniqueFieldValues.push(fieldValues[index].trim())
143      }
144    }
145  },
146  isLength(fieldName, values, operation) {
147    if (isNaN(values)) {
148      return false
149    }
150    const fieldValues = this.getFieldNameValues(fieldName)
151    const uniqueFieldValues = []
152    for (let index = 0; index < fieldValues.length; index++) {
153      if (uniqueFieldValues.indexOf(fieldValues[index].trim()) == -1) {
154        uniqueFieldValues.push(fieldValues[index].trim())
155      }
156    }
157    const { length } = fieldValues
158    const number = Number(values)
159    return eval(`${length} ${operation} ${number}`)
160  },
161  isEqual(fieldName, values) {
162    const extract = {
163      uniqueValues: [],
164      uniqueFieldValues: []
165    }
166    this.commonForOperations(fieldName, values, extract)
167    if (extract.uniqueValues.length != extract.uniqueFieldValues.length) {
168      return false
169    }
170    let result = true
171    for (let index = 0; index < extract.uniqueFieldValues.length; index++) {
172      if (extract.uniqueValues.indexOf(extract.uniqueFieldValues[index]) == -1) {
173        result = false
174      }
175    }
176    return result
177  },
178  isUnEqual(fieldName, values) {
179    const extract = {
180      uniqueValues: [],
181      uniqueFieldValues: []
182    }
183    this.commonForOperations(fieldName, values, extract)
184    if (extract.uniqueValues.length != extract.uniqueFieldValues.length) {
185      return true
186    }
187    let result = false
188    for (let index = 0; index < extract.uniqueFieldValues.length; index++) {
189      if (extract.uniqueValues.indexOf(extract.uniqueFieldValues[index]) == -1) {
190        result = true
191      }
192    }
193    return result
194  },
195  isIn(fieldName, values) {
196    const extract = {
197      uniqueValues: [],
198      uniqueFieldValues: []
199    }
200    this.commonForOperations(fieldName, values, extract)
201    if (extract.uniqueFieldValues.length == 0) {
202      return false
203    }
204    let result = false
205    for (let index = 0; index < extract.uniqueFieldValues.length; index++) {
206      if (extract.uniqueValues.indexOf(extract.uniqueFieldValues[index]) > -1) {
207        result = true
208      }
209    }
210    return result
211  },
212  isEmpty(fieldName) {
213    return this.isEqual(fieldName, '')
214  },
215  isNotEmpty(fieldName) {
216    return this.isUnEqual(fieldName, '')
217  },
218  renderCondition(structuredCondition) {
219    if (typeof structuredCondition.leftPart !== 'undefined'
220            && typeof structuredCondition.rightPart !== 'undefined'
221            && typeof structuredCondition.typeOfCondition !== 'undefined') {
222      return this.renderConditionSecured(
223        structuredCondition.leftPart.trim(),
224        structuredCondition.typeOfCondition.trim(),
225        structuredCondition.rightPart.trim()
226      )
227    }
228    console.log(structuredCondition)
229
230    return ''
231  },
232  renderConditionSecured(fieldName, condition, values) {
233    switch (condition) {
234      case '==':
235        return ` this.isEqual("${fieldName}","${values}")`
236      case '!=':
237        return ` this.isUnEqual("${fieldName}","${values}")`
238      case 'in':
239        return ` this.isIn("${fieldName}","${values}")`
240      case 'is empty':
241        return ` this.isEmpty("${fieldName}")`
242      case 'is not empty':
243        return ` this.isNotEmpty("${fieldName}")`
244      case '|length ==':
245      case '|length !=':
246      case '|length <':
247      case '|length <=':
248      case '|length >':
249      case '|length >=':
250        return ` this.isLength("${fieldName}","${values}","${condition.substr('|length '.length)}")`
251      case 'false':
252        return ' false '
253      case 'true':
254        return ' true '
255      case '':
256        return ''
257      default:
258        break
259    }
260    return ' false '
261  },
262  renderBadFormatingError(structuredCondition, conditionData) {
263    if (typeof structuredCondition.leftPart !== 'undefined' && structuredCondition.leftPart.length != 0) {
264      console.warn(`Left part ('${structuredCondition.leftPart}') should be empty before '${structuredCondition.operation}' in '${conditionData.condition}'`)
265      return true
266    }
267    return false
268  },
269  emptyCheckbox(element) {
270    $(element).find('input[type=checkbox]').each(function() {
271      $(this).prop('checked', false)
272      $(this).trigger('change')
273    })
274  },
275  setDefaultCheckbox(element) {
276    $(element).find('input[type=checkbox]:not([data-default])').each(function() {
277      $(this).prop('checked', false)
278      $(this).trigger('change')
279    })
280    $(element).find('input[type=checkbox][data-default]').each(function() {
281      const defaultVal = $(this).data('default')
282      if (defaultVal == 'checked') {
283        $(this).prop('checked', true)
284        $(this).trigger('change')
285      }
286    })
287  },
288  emptySelect(element) {
289    $(element).find('select').each(function() {
290      $(this).val('')
291      $(this).trigger('change')
292    })
293  },
294  setDefaultSelect(element) {
295    $(element).find('select[data-default]').each(function() {
296      const defaultVal = $(this).data('default')
297      const val = $(this).val()
298      if (defaultVal != undefined) {
299        $(this).val(defaultVal)
300        $(this).trigger('change')
301      }
302    })
303  },
304  emptyTextarea(element) {
305    $(element).find('textarea').each(function() {
306      $(this).val('')
307      $(this).trigger('change')
308    })
309  },
310  emptyRadio(element) {
311    // warning it unselect the radio button but this will not erase previous saved value
312    // it is needed to have a new value to erase it
313    $(element).find('input[type=radio]').each(function() {
314      $(this).prop('checked', false)
315      $(this).trigger('change')
316    })
317  },
318  setDefaultRadio(element) {
319    $(element).find('input[type=radio]:not([data-default])').each(function() {
320      $(this).prop('checked', false)
321      $(this).trigger('change')
322    })
323    $(element).find('input[type=radio][data-default]').each(function() {
324      const defaultVal = $(this).data('default')
325      if (defaultVal == 'checked') {
326        $(this).prop('checked', true)
327        $(this).trigger('change')
328      }
329    })
330  },
331  emptyTextarea(element) {
332    $(element).find('textarea').each(function() {
333      $(this).val('')
334      $(this).trigger('change')
335    })
336  },
337  emptyGeocode(element) {
338    $(element).find('div[class*="geocode-input"] input[type=hidden]').each(function() {
339      $(this).val('')
340      $(this).trigger('change')
341    })
342  },
343  emptyImage(element) {
344    $(element).find('div[class*="bazar-entry-edit-image"]').each(() => {
345      // currently not activated because ImageField is not safe
346      // TODO activate and TEST (prefer usage of ajax)
347      // $(this).find('output').html("");
348      // $(this).find('input[id^=data-][type=hidden]').val("");
349      // $(this).find('input[id^=filename-][type=hidden]').val("");
350      // $(this).find('input[id^=oldimage-][type=hidden]').val("");
351      // $(this).find('input[type=file]').val(" ");
351 // works only if after this.emptyOthersInputs
352    })
353  },
354  hasTagsInput(element) {
355    let result = false
356    Object.values($(element)[0]).forEach((param) => {
357      if (Object.keys(param).includes('tagsinput')) {
358        result = true
359      }
360    })
361    return result
362  },
363  emptyByTags(element) {
364    $(element).find('input.yeswiki-input-entries').each(function() {
365      if (ConditionsChecking.hasTagsInput(this)) {
366        $(this).tagsinput('removeAll')
367      }
368    })
369  },
370  setDefaultByTags(element) {
371    $(element).find('input.yeswiki-input-entries').each(function() {
372      const propertyName = $(this).prop('name')
373      const val = $(this).val()
374      if (val.length == 0 && propertyName.length > 0
375                && typeof bazarlistTagsInputsData !== 'undefined'
376                && bazarlistTagsInputsData[propertyName] != undefined
377                && ConditionsChecking.hasTagsInput(this)
378      ) {
379        const selectedOptions = bazarlistTagsInputsData[propertyName].selectedOptions || []
380        const existingTags = bazarlistTagsInputsData[propertyName].existingTags || []
381        $(this).tagsinput('removeAll')
382        selectedOptions.forEach((tag) => {
383          if (existingTags[tag] != undefined) {
384            $(this).tagsinput('add', existingTags[tag])
385          }
386        })
387      }
388    })
389  },
390  emptyOthersInputs(element) {
391    $(element).find('input:not([type=checkbox]):not([type=radio]):not([type=hidden]):not(.yeswiki-input-entries)').each(function() {
392      $(this).val('')
393      $(this).trigger('change')
394    })
395  },
396  emptyChildren(element) {
397    this.emptyCheckbox(element)
398    this.emptySelect(element)
399    this.emptyTextarea(element)
400    this.emptyRadio(element)
401    this.emptyGeocode(element)
402    this.emptyByTags(element)
403    this.emptyOthersInputs(element)
404    // this.emptyImage(element);
405    // do not work for FileField also
406  },
407  setDefaultChildren(element) {
408    this.setDefaultSelect(element)
409    this.setDefaultRadio(element)
410    this.setDefaultCheckbox(element)
411    this.setDefaultByTags(element)
412  },
413  resolveCondition(id, cleanSubelements = true, elemsToClean = {}) {
414    if (typeof this.conditionsCache[id] !== 'undefined') {
415      const conditionData = this.conditionsCache[id]
416      const stack = []
417      for (const key in conditionData.structuredConditions) {
418        stack.push(conditionData.structuredConditions[key])
419      }
420      let stringToEval = ''
421      let errorFound = false
422      while (stack.length > 0 && !errorFound) {
423        const structuredCondition = stack[0]
424        stack.splice(0, 1)
425        switch (structuredCondition.operation) {
426          case '(':
427          case '!(':
428            if (this.renderBadFormatingError(structuredCondition, conditionData)) {
429              errorFound = true
430            } else {
431              stringToEval += structuredCondition.operation
432            }
433            break
434          case 'not(':
435          case 'not (':
436            if (this.renderBadFormatingError(structuredCondition, conditionData)) {
437              errorFound = true
438            } else {
439              stringToEval = `${stringToEval}!(`
440            }
441            break
442          case ')':
443            stringToEval = stringToEval + this.renderCondition(structuredCondition) + structuredCondition.operation
444            break
445          case 'and':
446            stringToEval = `${stringToEval + this.renderCondition(structuredCondition)}&&`
447            break
448          case 'or':
449            stringToEval = `${stringToEval + this.renderCondition(structuredCondition)}||`
450            break
451          default:
452            if (stack.length > 0) {
453              errorFound = true
454              console.warn(`Unknown operation '${structuredCondition.operation}' in '${conditionData.condition}'`)
455            }
456            stringToEval += this.renderCondition(structuredCondition)
457            break
458        }
459      }
460      let display = false
461      try {
462        display = errorFound ? false : eval(stringToEval)
463      } catch (error) {
464        console.warn(error)
465        display = false
466      }
467      // for debug console.log(stringToEval+" => "+display)
468      // extract no clean param
469      const clean = $(conditionData.node).data('noclean') != true
470      if (display) {
471        const previousStateVisible = ($(conditionData.node).filter(':visible').length > 0)
472        $(conditionData.node).show()
473        window.dispatchEvent(new Event('resize')) // needed to refresh map for geolocalization
474        if (clean && !previousStateVisible) {
475          if (cleanSubelements) {
476            this.setDefaultChildren(conditionData.node)
477          } else {
478            elemsToClean[id] = false
479          }
480        }
481      } else {
482        $(conditionData.node).hide()
483        if (clean) {
484          if (cleanSubelements) {
485            this.emptyChildren(conditionData.node)
486          } else {
487            elemsToClean[id] = true
488          }
489        }
490      }
491    }
492  },
493  resolveTrigger(inputId) {
494    if (typeof this.triggersCache[inputId] !== 'undefined') {
495      const fieldsNames = this.triggersCache[inputId]
496      const conditionsIds = []
497      for (let index = 0; index < fieldsNames.length; index++) {
498        const fieldName = fieldsNames[index]
499        if (typeof this.fieldNamesCache[fieldName] !== 'undefined') {
500          const fieldData = this.fieldNamesCache[fieldName]
501          for (let indexCondition = 0; indexCondition < fieldData.conditionIds.length; indexCondition++) {
502            const id = fieldData.conditionIds[indexCondition]
503            if (conditionsIds.indexOf(id) < 0) {
504              conditionsIds.push(id)
505            }
506          }
507        }
508      }
509      for (let index = 0; index < conditionsIds.length; index++) {
510        const id = conditionsIds[index]
511        this.resolveCondition(id)
512      }
513    }
514  },
515  registerTrigger(input, fieldName) {
516    const inputId = $(input).attr('id')
517    if (typeof this.triggersCache[inputId] === 'undefined') {
518      this.triggersCache[inputId] = [fieldName]
519      $(input).on('change', () => {
520        ConditionsChecking.resolveTrigger(inputId)
521      })
522    } else if (this.triggersCache[inputId].indexOf(fieldName) < 0) {
523      this.triggersCache[inputId].push(fieldName)
524    }
525  },
526  findCheckbox(fieldName, result) {
527    if (result.type != '') {
528      return result
529    }
530    const node = $(`div[class*="group-checkbox-"][class*="${fieldName}"],ul[class*="group-checkbox-"][class*="${fieldName}"]`).filter(
531      function(index) {
532        const classes = $(this).attr('class').split(' ')
533        return classes.filter((className) => className.slice(-fieldName.length) == fieldName).length > 0
534      }
535    )
536    if (node.length > 0) {
537      const inputs = $(node).find('input[type=checkbox]')
538      if (inputs.length > 0) {
539        result.type = 'checkbox'
540        result.node = node
541        // register triggers
542        $(inputs).each(function() {
543          ConditionsChecking.registerTrigger(this, fieldName)
544        })
545      }
546    }
547    return result
548  },
549  findCheckboxTag(fieldName, result) {
550    if (result.type != '') {
551      return result
552    }
553    const node = $(`input[class$=${fieldName}].yeswiki-input-entries`)
554    if (node.length > 0) {
555      result.type = 'checkboxtag'
556      result.node = node
557      // register triggers
558      ConditionsChecking.registerTrigger(node, fieldName)
559    }
560    return result
561  },
562  findList(fieldName, result) {
563    if (result.type != '') {
564      return result
565    }
566    const node = $(`select[name$=${fieldName}]`)
567    if (node.length > 0) {
568      result.type = 'select'
569      result.node = node
570      // register triggers
571      ConditionsChecking.registerTrigger(node, fieldName)
572    }
573    return result
574  },
575  findRadio(fieldName, result) {
576    if (result.type != '') {
577      return result
578    }
579    const inputs = $(`input[name$=${fieldName}][type=radio]`)
580    if (inputs.length > 0) {
581      result.type = 'radio'
582      result.node = inputs
583      // register triggers
584      $(inputs).each(function() {
585        ConditionsChecking.registerTrigger(this, fieldName)
586      })
587    }
588    return result
589  },
590  extractFieldNode(fieldName) {
591    let result = {
592      type: '',
593      node: {},
594      conditionIds: []
595    }
596    result = this.findCheckbox(fieldName, result)
597    result = this.findCheckboxTag(fieldName, result)
598    result = this.findRadio(fieldName, result)
599    result = this.findList(fieldName, result)
600
601    return result
602  },
603  registerFieldName(fieldName, id) {
604    if (typeof this.fieldNamesCache[fieldName] === 'undefined') {
605      this.fieldNamesCache[fieldName] = this.extractFieldNode(fieldName)
606      this.fieldNamesCache[fieldName].conditionIds.push(id)
607    } else if (this.fieldNamesCache[fieldName].conditionIds.indexOf(id) < 0) {
608      this.fieldNamesCache[fieldName].conditionIds.push(id)
609    }
610  },
611  parseCondition(element) {
612    const condition = $(element).data('conditionschecking')
613    // index = internal id
614    const id = this.conditionsCache.length
615    // save cache
616    this.conditionsCache.push({
617      condition,
618      node: element,
619      structuredConditions: {}
620    })
621
622    let parsingObject = {
623      restOfCondition: condition,
624      currentCondition: '',
625      operation: ''
626    }
627    while (parsingObject.restOfCondition.length > 0) {
628      parsingObject = this.getFirstOperation(parsingObject)
629      // check condition
630      const indexForStructuredCondition = Object.keys(this.conditionsCache[id].structuredConditions).length
631      // save in cache
632      this.conditionsCache[id].structuredConditions[indexForStructuredCondition] = { operation: parsingObject.operation }
633      let structuredCondition = this.conditionsCache[id].structuredConditions[indexForStructuredCondition]
634      if (parsingObject.currentCondition.length > 0) {
635        structuredCondition = this.addCondition(
636          parsingObject.currentCondition
637        )
638      } else {
639        structuredCondition.leftPart = ''
640        structuredCondition.rightPart = ''
641        structuredCondition.typeOfCondition = ''
642      }
643      // activate trigger
644      if (typeof structuredCondition.leftPart !== 'undefined' && structuredCondition.leftPart.length > 0) {
645        const fieldName = structuredCondition.leftPart.trim()
646        this.registerFieldName(fieldName, id)
647      }
648      for (const key in structuredCondition) {
649        this.conditionsCache[id].structuredConditions[indexForStructuredCondition][key] = structuredCondition[key]
650      }
651    }
652  },
653  init() {
654    const conditionschecking = this
655    $('div[data-conditionschecking]').each(function() {
656      const element = $(this)
657      conditionschecking.parseCondition(element)
658    })
659    // init conditions
660    const elemsToClean = {}
661    for (let index = 0; index < this.conditionsCache.length; index++) {
662      this.resolveCondition(index, false, elemsToClean)
663    }
664    for (const id in elemsToClean) {
665      if (elemsToClean[id]) {
666        const conditionData = this.conditionsCache[id]
667        this.emptyChildren(conditionData.node)
668      }
669    }
670  }
671}
672
673ConditionsChecking.init()

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.