PageSourceSearch

https://peterduebolig.dk/wp-content/themes/webplatform/assets/scripts/unithandler.js

js peterduebolig.dk collected 2026-09-29 04:26:32 UTC 22,547 bytes, 642 lines download raw bytes

1var MindworkingUnitHandler;
2
3(function ($) {
4    MindworkingUnitHandler = function (configuration) {
5        this.unitTypes = {};
6        this.formatting = {};
7
8        // todo should we also use a ration setting, like for currency?
9        this.renderOptions = {
10            optimize: true,
11            format: true,
12            currency: "dkk",
13            group: "metric",
14            locale: "da_DK"
15        };
16
17        this.renderIndicator = "data-render";
18
19        if (configuration !== undefined) {
20            this.loadConfigurationJson(configuration);
21        }
22    };
23
24    /**
25     * set the configuration from a json object
26     *
27     * @param {Object} configuration
28     * @return {MindworkingUnitHandler}
29     */
30    MindworkingUnitHandler.prototype.loadConfigurationJson = function (configuration) {
31        // todo sanitize data
32        this.unitTypes = configuration.unitTypes;
33        this.formatting = configuration.formatting;
34        this.renderOptions.currency = this.unitTypes.currency.base;
35        this.renderOptions.group = this.unitTypes.area.base.split("/")[0];
36
37        // switch locale in renderOptions to defaults exiting in config
38        if (!this.formatting.hasOwnProperty(this.renderOptions.locale) && this.formatting.length > 0) {
39            this.renderOptions.locale = Object.keys(this.formatting)[0];
40        }
41        return this;
42    };
43
44    /**
45     * sets the render options, can trigger a re-render
46     *
47     * @param {Object} options
48     * @param [$updateNode] the html element to update, as a jQuery Object. set this to trigger an automatic re-render of all render fields in the object.
49     * @returns {MindworkingUnitHandler}
50     */
51    MindworkingUnitHandler.prototype.setRenderOptions = function (options, $updateNode) {
52        if ($updateNode === undefined) $updateNode = $('body');
53        var changed = false;
54        for (var key in options) {
55            if (this.renderOptions.hasOwnProperty(key)) {
56                if (this.renderOptions[key] !== options[key]) {
57                    this.renderOptions[key] = options[key];
58                    changed = true;
59                }
60            }
61        }
62
63        if ($updateNode !== undefined && changed) {
64            this.renderElementsIn($updateNode);
65        }
66
67        return this;
68    };
69
70    /**
71     * convert a value
72     * @param {Number} value
73     * @param {Object} config the unit configuration
74     * @param {String} from
75     * @param {String} to
76     * @returns {number}
77     */
78    MindworkingUnitHandler.prototype.convert = function (value, config, from, to) {
79        var factorFrom = this.getUnitFactor(from, config);
80        var factorTo = this.getUnitFactor(to, config);
81
82        return value * factorFrom / factorTo;
83    };
84
85    /**
86     * optimize a value within the given unit type (and optionally also group)
87     * this will try to select a unit so the value is above 1
88     * if there is none, it will select the one where the value will be closest to one
89     *
90     * @param {Number} value
91     * @param {Object} config the unit configuration
92     * @param {String} from
93     * @param {String} [groupTo] the unit group, fx metric or imperial, if available
94     * @returns {[Number, Object]}
95     */
96    MindworkingUnitHandler.prototype.optimize = function (value, config, from, groupTo) {
97
98        var units = config.grouped ? config.units[groupTo] : config.units;
99
100        var unit = this.getUnitConfiguration(from, config);
101        var valueOptimized = false;
102        var unitOptimized = false;
103        var valueTmp, unitTmp;
104        for (var key in units) {
105            unitTmp = units[key];
106            valueTmp = value * unit.factor / unitTmp.factor;
107            if (valueOptimized === false || (valueTmp > 1 && (valueTmp < valueOptimized || valueOptimized < 0))) {
108                valueOptimized = valueTmp;
109                unitOptimized = unitTmp;
110            }
111        }
112
113        if (valueOptimized === false) {
114            valueOptimized = value;
115            unitOptimized = unit;
116        }
117
118        return [valueOptimized, unitOptimized];
119    };
120
121    /**
122     * get the factor for a unit
123     *
124     * @param {String} unit
125     * @param {Object} config
126     * @returns {Number|Boolean}
127     */
128    MindworkingUnitHandler.prototype.getUnitFactor = function (unit, config) {
129        var unitConfiguration = this.getUnitConfiguration(unit, config);
130        if (unitConfiguration !== false) {
131            return unitConfiguration.factor;
132        }
133        return false;
134    };
135
136    /**
137     * get the configuration for a unit
138     *
139     * @param {String} unit the unit key, can also contain a group key, like "metric/m2"
140     * @param {Object} config
141     * @returns {*}
142     */
143    MindworkingUnitHandler.prototype.getUnitConfiguration = function (unit, config) {
144        if (config !== false) {
145            if (config.grouped) {
146                if (unit.indexOf("/") !== -1) {
147                    var tmp = unit.split("/");
148                    if (config.units.hasOwnProperty(tmp[0])) {
149                        var group = config.units[tmp[0]];
150                        if (group.hasOwnProperty(tmp[1])) {
151                            return group[tmp[1]];
152                        }
153                    }
154                }
155                for (var groupKey in config.units) {
156                    if (config.units[groupKey].hasOwnProperty(unit)) {
157                        return config.units[groupKey][unit];
158                    }
159                }
160            } else {
161                if (config.units.hasOwnProperty(unit)) {
162                    return config.units[unit];
163                }
164            }
165        }
166        return false;
167    };
168
169    /**
170     * create a concatenated element (consisting of multiple others)
171     *
172     * @param {Object} object the temporary object with the data we need
173     * @param {String} nodeType the node type to embed this into
174     * @param {String} nodeClass the node class to embed this into
175     * @returns {string}
176     */
177    MindworkingUnitHandler.prototype.createElementConcatenated = function (object, nodeType, nodeClass) {
178        var fields = [];
179        if (nodeClass !== undefined) {
180            nodeClass = "class='" + nodeClass + "'";
181        } else {
182            nodeClass = "";
183        }
184        for (var i in object.data) {
185            fields.push(this.createElement(object.data[i], 'span'));
186        }
187        var concatenated = "";
188
189        var action = object.action;
190
191        // right now the only action available is join, not sure if any others will be needed.
192        switch (action[0]) {
193            case "join":
194                concatenated = fields.join(action[1]);
195                break;
196        }
197
198        return "<" + nodeType + " " + nodeClass + ">" + concatenated + "</" + nodeType + ">";
199    };
200
201    /**
202     * create an element that can be re-rendered with the UnitHandler from a fact field object
203     * the value will be inserted at this point as well
204     * will also create non re-renderable elements if they don't have a renderable type
205     *
206     * @param {Object} object the fact field object, providing type, value, unit
207     * @param {String} nodeType the type of html element to create
208     * @param {String} [nodeClass] the class of the created element
209     * @returns {string}
210     */
211    MindworkingUnitHandler.prototype.createElement = function (object, nodeType, nodeClass) {
212        if (object.hasOwnProperty('action')) {
213            return this.createElementConcatenated(object, nodeType, nodeClass);
214        }
215        if (nodeType === undefined) nodeType = "span";
216        if (nodeClass !== undefined) {
217            nodeClass = "class='" + nodeClass + "'";
218        } else {
219            nodeClass = "";
220        }
221        var type = object.hasOwnProperty('type') ? object.type : "string";
222        var value = object.hasOwnProperty('value') ? object.value : "";
223        var unit = object.hasOwnProperty('unit') ? object.unit : this.hasUnitType(type) ? this.unitTypes[type].base : false;
224
225        var output = value;
226
227        var attributes = "";
228
229        switch (type) {
230            case "area":
231            case "distance":
232            case "currency":
233            case "ratio":
234                // these can be converted and formatted, according to their type
235                attributes += this.renderIndicator + "='1'";
236                attributes += "data-type='" + type + "'";
237                if (unit !== false) attributes += "data-unit='" + unit + "'";
238                attributes += "data-value='" + value + "'";
239                output = this.renderValue(value, type, unit);
240                break;
241            case "integer":
242            case "number":
243                // standard numbers are without unit, so they just get formatted, not converted
244                attributes += this.renderIndicator + "='1'";
245                attributes += "data-type='number'";
246                if (unit !== false) attributes += "data-unit='none'";
247                attributes += "data-value='" + value + "'";
248                output = this.renderValue(value, type, unit);
249                break;
250            case "date":
251                // dates can be formatted with their type, but don't have a unit
252                attributes += this.renderIndicator + "='1'";
253                attributes += "data-type='date'";
254                if (unit !== false) attributes += "data-unit='none'";
255                attributes += "data-value='" + value + "'";
256                output = this.renderValue(value, type, unit);
257                break;
258            case "period":
259                // periods can have a unit
260                attributes += this.renderIndicator + "='1'";
261                attributes += "data-type='period'";
262                if (unit !== false) attributes += "data-unit='" + unit + "'";
263                attributes += "data-value='" + value + "'";
264                output = this.renderValue(value, type, unit);
265                break;
266            case "months":
267                attributes += this.renderIndicator + "='1'";
268                attributes += "data-type='months'";
269                if (unit !== false) attributes += "data-unit='" + unit + "'";
270                attributes += "data-value='" + value + "'";
271                output = this.renderValue(value, type, unit);
272                break;
273            case "stringpercent":
274                attributes += this.renderIndicator + "='1'";
275                attributes += "data-type='stringpercent'";
276                if (unit !== false) attributes += "data-unit='" + unit + "'";
277                attributes += "data-value='" + value + "'";
278                output = this.renderValue(value, type, unit);
279                break;
280        }
281
282        return "<" + nodeType + " " + nodeClass + " " + attributes + ">" + output + "</" + nodeType + ">";
283    };
284
285    /**
286     * renders the renderable elements in a given jQuery element
287     *
288     * @param $node
289     *
290     * @returns {MindworkingUnitHandler}
291     */
292    MindworkingUnitHandler.prototype.renderElementsIn = function ($node) {
293        if ($node === undefined) $node = $('body');
294        //var ts = Date.now();
295
296        var $elements = $node.find("[" + this.renderIndicator + "=1]");
297        var _self = this;
298
299        $elements.each(function () {
300            _self.renderElement($(this));
301        });
302
303        //console.log("rendering data for " + $elements.length + " fields took " + (Math.round((Date.now() - ts) * 1000) / 1000) + "ms");
304
305        return this;
306    };
307
308    /**
309     * render the value as specified in the attributes and the current config
310     *
311     * @param {jQuery} $node
312     *
313     * @returns {MindworkingUnitHandler}
314     */
315    MindworkingUnitHandler.prototype.renderElement = function ($node) {
316        var type = $node.data('type');
317        var value = $node.data('value');
318        var unit = $node.data('unit');
319
320        $node.html(this.renderValue(value, type, unit));
321
322        return this;
323    };
324
325    /**
326     * generate the value of a given field with its unit configuration
327     *
328     * @param {*} value
329     * @param {String} type
330     * @param {String} [unit]
331     * @returns {{}|syntax}
332     */
333    MindworkingUnitHandler.prototype.renderValue = function (value, type, unit) {
334        var optimize = this.renderOptions.optimize;
335        var currency = this.renderOptions.currency;
336        var group = this.renderOptions.group;
337        var format = this.renderOptions.format;
338        var locale = this.renderOptions.locale;
339
340        var config = this.unitTypes.hasOwnProperty(type) ? this.unitTypes[type] : false;
341        if (unit === false || unit === undefined) unit = config.base;
342
343        var newValue = value;
344        var newUnitCfg = this.getUnitConfiguration(unit, config);
345        var formatType = "number";
346
347        switch (type) {
348            case "currency":
349                // currency should not be optimized, just converted
350                newValue = this.convert(value, config, unit, currency);
351                newUnitCfg = this.getUnitConfiguration(currency, config);
352                formatType = "currency";
353                break;
354            case "string":
355            case "year":
356            case "stringpercent":
357                // strings will not be formatted
358                formatType = "string";
359                break;
360            case "date":
361                formatType = "date";
362                break;
363            case "period":
364                formatType = "period";
365                break;
366            case "integer":
367                newValue = Math.round(value);
368            // fallthrough is intended, integer should be handled as number
369            case "number":
370            case "percentage":
371            case "ratio":
372                formatType = "number";
373                break;
374            case "area":
375                formatType = "area";
376            // fallthrough is intended, area supports optimization
377            default:
378                if (optimize) {
379                    var optimized = this.optimize(value, config, unit, group);
380                    newValue = optimized[0];
381                    newUnitCfg = optimized[1];
382                }
383        }
384
385        if (format) {
386            switch (formatType) {
387                case "string":
388                    // no formatting for strings
389                    break;
390                case "date":
391                    var dateFormat = this.formatting[locale]['time']['date'];
392                    newValue = this.formatDate(newValue, dateFormat);
393                    break;
394                case "period":
395                    var periodFormat = this.formatting[locale]['time']['period'];
396                    newValue = this.formatPeriod(newValue, unit, periodFormat);
397                    break;
398                default:
399                    // all kinds of straightforward numbers
400                    if (!isNaN(parseFloat(newValue))) {
401                        var fc = this.formatting[locale][formatType];
402                        newValue = this.formatNumber(newValue, fc.decimals, fc.separatorDecimals, fc.separatorThousands, fc.forceDecimals, fc.roundMethod);
403                    }
404
405            }
406        }
407
408        if (config !== false) {
409            var renderValue = config.syntax;
410            renderValue = renderValue.replace("%value%", newValue).replace("%unit%", newUnitCfg !== false ? newUnitCfg.display : "");
411        } else {
412            renderValue = newValue;
413        }
414
415        return renderValue;
416    };
417
418    /**
419     * switch the unit group (imperial/metric)
420     * @param {String} group
421     * @param {jQuery} $updateNode
422     * @return {Boolean}
423     */
424    MindworkingUnitHandler.prototype.switchGroup = function (group, $updateNode) {
425        if (this.hasUnitGroup('area', group)) {
426            this.setRenderOptions({'group': group}, $updateNode);
427            return true;
428        }
429        return false;
430    };
431
432    /**
433     * switch the currency unit
434     * @param {String} unit
435     * @param {jQuery} $updateNode
436     * @return {Boolean}
437     */
438    MindworkingUnitHandler.prototype.switchCurrency = function (unit, $updateNode) {
439        if (this.hasUnit('currency', unit)) {
440            this.setRenderOptions({'currency': unit}, $updateNode);
441            return true;
442        }
443        return false;
444    };
445
446    /**
447     * check if unit exists
448     *
449     * @param {String} type
450     * @param {String} unit
451     * @returns {boolean}
452     */
453    MindworkingUnitHandler.prototype.hasUnit = function (type, unit) {
454        if (this.hasUnitType(type)) {
455            var unitType = this.unitTypes[type];
456            var group;
457            if (unitType.grouped) {
458                for (var g in unitType.units) {
459                    if (unitType.units.hasOwnProperty(g)) {
460                        group = unitType[g];
461                        if (group.hasOwnProperty(unit)) return true;
462                    }
463                }
464            } else {
465                if (unitType.units.hasOwnProperty(unit)) return true;
466            }
467        }
468        return false;
469    };
470
471    /**
472     * check if type exists
473     *
474     * @param {String} type
475     * @returns {boolean}
476     */
477    MindworkingUnitHandler.prototype.hasUnitType = function (type) {
478        return this.unitTypes.hasOwnProperty(type);
479    };
480
481    /**
482     * check if group exists
483     *
484     * @param {String} type
485     * @param {String} group
486     * @returns {boolean}
487     */
488    MindworkingUnitHandler.prototype.hasUnitGroup = function (type, group) {
489        if (this.hasUnitType(type)) {
490            var unitType = this.unitTypes[type];
491            if (unitType.grouped) {
492                return unitType.units.hasOwnProperty(group);
493            }
494        }
495        return false;
496    };
497
498    /**
499     * limit date to date between min and max
500     * @param {String} date
501     * @param {String} min
502     * @param {String} max
503     *
504     * todo, not finished, not used yet anyways
505     */
506    MindworkingUnitHandler.prototype.limitDate = function (date, min, max) {
507        var d = new Date(date);
508
509    };
510
511    /**
512     * format a mysql date for rendering
513     *
514     * @param {String} mySqlDateString
515     * @param {String} format
516     * @returns {string}
517     */
518    MindworkingUnitHandler.prototype.formatDate = function (mySqlDateString, format) {
519        var ret = "";
520        var tmp = mySqlDateString.split("-");
521        if (tmp.length === 3) {
522            var date = new Date(mySqlDateString);
523            // js starts months at 0
524            var month = date.getMonth() + 1;
525            var replaces = {
526                "d": date.getDate(),
527                "m": month,
528                "M": Translations.translate("scripts-unit-date-month-" + month),
529                "Y": date.getFullYear(),
530                "dd": this.fillString(date.getDay()),
531                "mm": this.fillString(month)
532            };
533
534            ret = format;
535            for (var i in replaces) {
536                ret = ret.replace("%" + i + "%", replaces[i]);
537            }
538        }
539        return ret;
540    };
541
542    /**
543     * format a period
544     * @param {String} value
545     * @param {String} unit
546     * @param {String} format
547     * @returns {*}
548     */
549    MindworkingUnitHandler.prototype.formatPeriod = function (value, unit, format) {
550        var add = (value === 1) ? "" : "s";
551        var unitTranslation = Translations.translate("scripts-unit-period-" + unit + add);
552
553        return format.replace("%value%", value).replace("%unit%", unitTranslation);
554    };
555
556    /**
557     * fill a string
558     * @param {String} string
559     * @param {Number} length
560     * @param {String} fill
561     * @param {String} prepend
562     * @returns {string}
563     */
564    MindworkingUnitHandler.prototype.fillString = function (string, length, fill, prepend) {
565        if (length === undefined) length = 2;
566        if (fill === undefined) fill = "0";
567        if (prepend === undefined) prepend = true;
568
569        var ret = string.toString();
570        var stringLength = ret.length;
571        if (stringLength < length) {
572            var fillString = new Array(length - stringLength + 1).join("0");
573            ret = (prepend ? fillString : "") + ret + (!prepend ? fillString : "");
574        }
575        return ret;
576    };
577
578    /**
579     * format a number
580     * provides similar functionality to PHP's number_format
581     *
582     * @param {Number} value the value to format
583     * @param {Number} decimals the number of decimals to round to
584     * @param {String} separatorDecimals the decimal separator (normally '.')
585     * @param {String} separatorThousands the separator for thousands (normally '')
586     * @param {Boolean} forceDecimals set to true to enforce showing the set number of decimals even if they evaluate to 0
587     * @param {String}
587 [roundMethod] the method to apply when rounding: round/ceil/floor
588     *
589     * @returns {*}
590     */
591    MindworkingUnitHandler.prototype.formatNumber = function (value, decimals, separatorDecimals, separatorThousands, forceDecimals, roundMethod) {
592        var ret = this.round(value, decimals, roundMethod);
593
594        var tmp = ret.toString().split(".");
595        var valInt = tmp[0];
596        var valDec = tmp.length > 1 ? tmp[1] : "";
597
598        if (separatorThousands !== undefined && separatorThousands !== '') {
599            var rev = valInt.split("").reverse().join("");
600            valInt = rev.match(/.{1,3}/g).join(separatorThousands).split("").reverse().join("");
601        }
602
603        ret = valInt;
604
605        if (decimals > 0) {
606            if (forceDecimals && valDec.length < decimals) {
607                // fill up the decimal value with zeros to force decimals to be displayed
608                valDec += new Array(decimals - valDec.length + 1).join("0");
609            }
610            if (valDec !== "") ret += separatorDecimals + valDec;
611        }
612
613        return ret;
614    };
615
616    /**
617     * round a value
618     *
619     * @param {Number} value
620     * @param {Number} decimals
621     * @param {String} [method] the method to use, 'round', 'ceil' or 'floor'. invalid method will use 'round'
622     *
623     * @returns {number}
624     */
625    MindworkingUnitHandler.prototype.round = function (value, decimals, method) {
626        var factor = Math.pow(10, decimals);
627        var tmp = value * factor;
628        switch (method) {
629            case 'ceil':
630                tmp = Math.ceil(tmp);
631                break;
632            case 'floor':
633                tmp = Math.floor(tmp);
634                break;
635            default:
636                tmp = Math.round(tmp);
637        }
638        return tmp / factor;
639    };
640
641
642})(jQuery);

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.