PageSourceSearch

https://kurzwarenland.de/templates/NOVA/js/jqcloud.js?v=1.7.1

js kurzwarenland.de collected 2026-09-25 10:43:52 UTC 17,885 bytes, 546 lines download raw bytes

1/*!
2 * jQCloud 2.0.3
3 * Copyright 2011 Luca Ongaro (http://www.lucaongaro.eu)
4 * Copyright 2013 Daniel White (http://www.developerdan.com)
5 * Copyright 2014-2017 Damien "Mistic" Sorel (http://www.strangeplanet.fr)
6 * Licensed under MIT (http://opensource.org/licenses/MIT)
7 */
8(function(root, factory) {
9    if (typeof define === 'function' && define.amd) {
10        define(['jquery'], factory);
11    }
12    else if (typeof module === 'object' && module.exports) {
13        module.exports = factory(require('jquery'));
14    }
15    else {
16        factory(root.jQuery);
17    }
18}(this, function($) {
19'use strict';
20
21/*
22 * Plugin class
23 */
24var jQCloud = function(element, word_array, options) {
25    this.$element = $(element);
26
27    this.word_array = word_array || [];
28    this.options = options;
29
30    this.sizeGenerator = null;
31    this.colorGenerator = null;
32
33    // Data used internally
34    this.data = {
35        placed_words: [],
36        timeouts: {},
37        namespace: null,
38        step: null,
39        angle: null,
40        aspect_ratio: null,
41        max_weight: null,
42        min_weight: null,
43        sizes: [],
44        colors: []
45    };
46
47    this.initialize();
48};
49
50jQCloud.DEFAULTS = {
51    width: 100,
52    height: 100,
53    center: { x: 0.5, y: 0.5 },
54    steps: 10,
55    delay: null,
56    shape: 'elliptic',
57    classPattern: 'w{n}',
58    encodeURI: true,
59    removeOverflowing: true,
60    afterCloudRender: null,
61    autoResize: false,
62    colors: null,
63    fontSize: null,
64    template: null
65};
66
67jQCloud.prototype = {
68    initialize: function() {
69        // Set/Get dimensions
70        if (this.options.width) {
71            this.$element.width(this.options.width);
72        }
73        else {
74            this.options.width = this.$element.width();
75        }
76        if (this.options.height) {
77            this.$element.height(this.options.height);
78        }
79        else {
80            this.options.height = this.$element.height();
81        }
82
83        // Default options value
84        this.options = $.extend(true, {}, jQCloud.DEFAULTS, this.options);
85
86        // Ensure delay
87        if (this.options.delay === null) {
88            this.options.delay = this.word_array.length > 50 ? 10 : 0;
89        }
90
91        // Backward compatibility
92        if (this.options.center.x > 1) {
93            this.options.center.x = this.options.center.x / this.options.width;
94            this.options.center.y = this.options.center.y / this.options.height;
95        }
96
97        // Create colorGenerator function from options
98        // Direct function
99        if (typeof this.options.colors == 'function') {
100            this.colorGenerator = this.options.colors;
101        }
102        // Array of sizes
103        else if ($.isArray(this.options.colors)) {
104            var cl = this.options.colors.length;
105            if (cl > 0) {
106                // Fill the sizes array to X items
107                if (cl < this.options.steps) {
108                    for (var i = cl; i < this.options.steps; i++) {
109                        this.options.colors[i] = this.options.colors[cl - 1];
110                    }
111                }
112
113                this.colorGenerator = function(weight) {
114                    return this.options.colors[this.options.steps - weight];
115                };
116            }
117        }
118
119        // Create sizeGenerator function from options
120        // Direct function
121        if (typeof this.options.fontSize == 'function') {
122            this.sizeGenerator = this.options.fontSize;
123        }
124        // Object with 'from' and 'to'
125        else if ($.isPlainObject(this.options.fontSize)) {
126            this.sizeGenerator = function(width, height, weight) {
127                var max = width * this.options.fontSize.from,
128                    min = width * this.options.fontSize.to;
129                return Math.round(min + (max - min) * 1.0 / (this.options.steps - 1) * (weight - 1)) + 'px';
130            };
131        }
132        // Array of sizes
133        else if ($.isArray(this.options.fontSize)) {
134            var sl = this.options.fontSize.length;
135            if (sl > 0) {
136                // Fill the sizes array to X items
137                if (sl < this.options.steps) {
138                    for (var j = sl; j < this.options.steps; j++) {
139                        this.options.fontSize[j] = this.options.fontSize[sl - 1];
140                    }
141                }
142
143                this.sizeGenerator = function(width, height, weight) {
144                    return this.options.fontSize[this.options.steps - weight];
145                };
146            }
147        }
148
149        this.data.angle = Math.random() * 6.28;
150        this.data.step = (this.options.shape === 'rectangular') ? 18.0 : 2.0;
151        this.data.aspect_ratio = this.options.width / this.options.height;
152        this.clearTimeouts();
153
154        // Namespace word ids to avoid collisions between multiple clouds
155        this.data.namespace = (this.$element.attr('id') || Math.floor((Math.random() * 1000000)).toString(36)) + '_word_';
156
157        this.$element.addClass('jqcloud');
158
159        // Container's CSS position cannot be 'static'
160        if (this.$element.css('position') === 'static') {
161            this.$element.css('position', 'relative');
162        }
163
164        // Delay execution so that the browser can render the page before the computatively intensive word cloud drawing
165        this.createTimeout($.proxy(this.drawWordCloud, this), 10);
166
167        // Attach window resize event
168        if (this.options.autoResize) {
169            $(window).on('resize.' + this.data.namespace, throttle(this.resize, 50, this));
170        }
171    },
172
173    // Helper function to keep track of timeouts so they can be destroyed
174    createTimeout: function(callback, time) {
175        var timeout = setTimeout($.proxy(function() {
176            delete this.data.timeouts[timeout];
177            callback();
178        }, this), time);
179        this.data.timeouts[timeout] = true;
180    },
181
182    // Destroy all timeouts
183    clearTimeouts: function() {
184        $.each(this.data.timeouts, function(key) {
185            clearTimeout(key);
186        });
187        this.data.timeouts = {};
188    },
189
190    // Pairwise overlap detection
191    overlapping: function(a, b) {
192        if (Math.abs(2.0 * a.left + a.width - 2.0 * b.left - b.width) < a.width + b.width) {
193            if (Math.abs(2.0 * a.top + a.height - 2.0 * b.top - b.height) < a.height + b.height) {
194                return true;
195            }
196        }
197        return false;
198    },
199
200    // Helper function to test if an element overlaps others
201    hitTest: function(elem) {
202        // Check elements for overlap one by one, stop and return false as soon as an overlap is found
203        for (var i = 0, l = this.data.placed_words.length; i < l; i++) {
204            if (this.overlapping(elem, this.data.placed_words[i])) {
205                return true;
206            }
207        }
208        return false;
209    },
210
211    // Initialize the drawing of the whole cloud
212    drawWordCloud: function() {
213        var i, l;
214
215        this.$element.children('[id^="' + this.data.namespace + '"]').remove();
216
217        if (this.word_array.length === 0) {
218            return;
219        }
220
221        // Make sure every weight is a number before sorting
222        for (i = 0, l = this.word_array.length; i < l; i++) {
223            this.word_array[i].weight = parseFloat(this.word_array[i].weight, 10);
224        }
225
226        // Sort word_array from the word with the highest weight to the one with the lowest
227        this.word_array.sort(function(a, b) {
228            return b.weight - a.weight;
229        });
230
231        // Kepp trace of bounds
232        this.data.max_weight = this.word_array[0].weight;
233        this.data.min_weight = this.word_array[this.word_array.length - 1].weight;
234
235        // Generate colors
236        this.data.colors = [];
237        if (this.colorGenerator) {
238            for (i = 0; i < this.options.steps; i++) {
239                this.data.colors.push(this.colorGenerator(i + 1));
240            }
241        }
242
243        // Generate font sizes
244        this.data.sizes = [];
245        if (this.sizeGenerator) {
246            for (i = 0; i < this.options.steps; i++) {
247                this.data.sizes.push(this.sizeGenerator(this.options.width, this.options.height, i + 1));
248            }
249        }
250
251        // Iterate drawOneWord on every word, immediately or with delay
252        if (this.options.delay > 0) {
253            this.drawOneWordDelayed();
254        }
255        else {
256            for (i = 0, l = this.word_array.length; i < l; i++) {
257                this.drawOneWord(i, this.word_array[i]);
258            }
259
260            if (typeof this.options.afterCloudRender === 'function') {
261                this.options.afterCloudRender.call(this.$element);
262            }
263        }
264    },
265
266    // Function to draw a word, by moving it in spiral until it finds a suitable empty place
267    drawOneWord: function(index, word) {
268        var word_id = this.data.namespace + index,
269            word_selector = '#' + word_id,
270
271        // option.shape == 'elliptic'
272            angle = this.data.angle,
273            radius = 0.0,
274
275        // option.shape == 'rectangular'
276            steps_in_direction = 0.0,
277            quarter_turns = 0.0,
278
279            weight = Math.floor(this.options.steps / 2),
280            word_span,
281            word_size,
282            word_style;
283
284        // Create word attr object
285        word.attr = $.extend({}, word.html, { id: word_id });
286
287        // Linearly map the original weight to a discrete scale from 1 to 10
288        // Only if weights are different
289        if (this.data.max_weight != this.data.min_weight) {
290            weight = Math.round((word.weight - this.data.min_weight) * 1.0 * (this.options.steps - 1) / (this.data.max_weight - this.data.min_weight)) + 1;
291        }
292        word_span = $('<span>').attr(word.attr);
293
294        word_span.addClass('jqcloud-word');
295
296        // Apply class
297        if (this.options.classPattern) {
298            word_span.addClass(this.options.classPattern.replace('{n}', weight));
299        }
300
301        // Apply color
302        if (this.data.colors.length) {
303            word_span.css('color', this.data.colors[weight - 1]);
304        }
305
306        // Apply color from word property
307        if (word.color) {
308            word_span.css('color', word.color);
309        }
310
311        // Apply size
312        if (this.data.sizes.length) {
313            word_span.css('font-size', this.data.sizes[weight - 1]);
314        }
315
316        //Render using template function if provided.
317        if (this.options.template) {
318            word_span.html(this.options.template(word));
319        } else if (word.link) {
320            // Append link if word.link attribute was set
321            // If link is a string, then use it as the link href
322            if (typeof word.link === 'string') {
323                word.link = { href: word.link };
324            }
325
326            if (this.options.encodeURI) {
327                word.link.href = encodeURI(word.link.href).replace(/'/g, '%27');
328            }
329
330            word_span.append($('<a>').attr(word.link).text(word.text));
331        }
332        else {
333            word_span.text(word.text);
334        }
335
336        // Bind handlers to words
337        if (word.handlers) {
338            word_span.on(word.handlers);
339        }
340
341        this.$element.append(word_span);
342
343        word_size = {
344            width: word_span.outerWidth(),
345            height: word_span.outerHeight()
346        };
347        word_size.left = this.options.center.x * this.options.width - word_size.width / 2.0;
348        word_size.top = this.options.center.y * this.options.height - word_size.height / 2.0;
349
350        // Save a reference to the style property, for better performance
351        word_style = word_span[0].style;
352        word_style.position = 'absolute';
353        word_style.left = word_size.left + 'px';
354        word_style.top = word_size.top + 'px';
355
356        while (this.hitTest(word_size)) {
357            // option shape is 'rectangular' so move the word in a rectangular spiral
358            if (this.options.shape === 'rectangular') {
359                steps_in_direction++;
360
361                if (steps_in_direction * this.data.step > (1 + Math.floor(quarter_turns / 2.0)) * this.data.step * ((quarter_turns % 4 % 2) 
361=== 0 ? 1 : this.data.aspect_ratio)) {
362                    steps_in_direction = 0.0;
363                    quarter_turns++;
364                }
365
366                switch (quarter_turns % 4) {
367                    case 1:
368                        word_size.left += this.data.step * this.data.aspect_ratio + Math.random() * 2.0;
369                        break;
370                    case 2:
371                        word_size.top -= this.data.step + Math.random() * 2.0;
372                        break;
373                    case 3:
374                        word_size.left -= this.data.step * this.data.aspect_ratio + Math.random() * 2.0;
375                        break;
376                    case 0:
377                        word_size.top += this.data.step + Math.random() * 2.0;
378                        break;
379                }
380            }
381            // Default settings: elliptic spiral shape
382            else {
383                radius += this.data.step;
384                angle += (index % 2 === 0 ? 1 : -1) * this.data.step;
385
386                word_size.left = this.options.center.x * this.options.width - (word_size.width / 2.0) + (radius * Math.cos(angle)) * this.data.aspect_ratio;
387                word_size.top = this.options.center.y * this.options.height + radius * Math.sin(angle) - (word_size.height / 2.0);
388            }
389            word_style.left = word_size.left + 'px';
390            word_style.top = word_size.top + 'px';
391        }
392
393        // Don't render word if part of it would be outside the container
394        if (this.options.removeOverflowing && (
395                word_size.left < 0 || word_size.top < 0 ||
396                (word_size.left + word_size.width) > this.options.width ||
397                (word_size.top + word_size.height) > this.options.height
398            )
399        ) {
400            word_span.remove();
401            return;
402        }
403
404        // Save position for further usage
405        this.data.placed_words.push(word_size);
406
407        if (typeof word.afterWordRender === 'function') {
408            word.afterWordRender.call(word_span);
409        }
410    },
411
412    // Draw one word then recall the function after a delay
413    drawOneWordDelayed: function(index) {
414        index = index || 0;
415
416        // if not visible then do not attempt to draw
417        if (!this.$element.is(':visible')) {
418            this.createTimeout($.proxy(function() {
419                this.drawOneWordDelayed(index);
420            }, this), 10);
421
422            return;
423        }
424
425        if (index < this.word_array.length) {
426            this.drawOneWord(index, this.word_array[index]);
427
428            this.createTimeout($.proxy(function() {
429                this.drawOneWordDelayed(index + 1);
430            }, this), this.options.delay);
431        }
432        else {
433            if (typeof this.options.afterCloudRender == 'function') {
434                this.options.afterCloudRender.call(this.$element);
435            }
436        }
437    },
438
439    // Destroy any data and objects added by the plugin
440    destroy: function() {
441        if (this.options.autoResize) {
442            $(window).off('resize.' + this.data.namespace);
443        }
444
445        this.clearTimeouts();
446        this.$element.removeClass('jqcloud');
447        this.$element.removeData('jqcloud');
448        this.$element.children('[id^="' + this.data.namespace + '"]').remove();
449    },
450
451    // Update the list of words
452    update: function(word_array) {
453        this.word_array = word_array;
454        this.data.placed_words = [];
455
456        this.clearTimeouts();
457        this.drawWordCloud();
458    },
459
460    resize: function() {
461        var new_size = {
462            width: this.$element.width(),
463            height: this.$element.height()
464        };
465
466        if (new_size.width != this.options.width || new_size.height != this.options.height) {
467            this.options.width = new_size.width;
468            this.options.height = new_size.height;
469            this.data.aspect_ratio = this.options.width / this.options.height;
470
471            this.update(this.word_array);
472        }
473    },
474};
475
476/*
477 * Apply throttling to a callback
478 * @param callback {function}
479 * @param delay {int} milliseconds
480 * @param context {object|null}
481 * @return {function}
482 */
483function throttle(callback, delay, context) {
484    var state = {
485        pid: null,
486        last: 0
487    };
488
489    return function() {
490        var elapsed = new Date().getTime() - state.last,
491            args = arguments,
492            that = this;
493
494        function exec() {
495            state.last = new Date().getTime();
496            return callback.apply(context || that, Array.prototype.slice.call(args));
497        }
498
499        if (elapsed > delay) {
500            return exec();
501        }
502        else {
503            clearTimeout(state.pid);
504            state.pid = setTimeout(exec, delay - elapsed);
505        }
506    };
507}
508
509/*
510 * jQuery plugin
511 */
512$.fn.jQCloud = function(word_array, option) {
513    var args = arguments;
514
515    return this.each(function() {
516        var $this = $(this),
517            data = $this.data('jqcloud');
518
519        if (!data && word_array === 'destroy') {
520            // Don't even try to initialize when called with 'destroy'
521            return;
522        }
523        if (!data) {
524            var options = typeof option === 'object' ? option : {};
525            $this.data('jqcloud', (data = new jQCloud(this, word_array, options)));
526        }
527        else if (typeof word_array === 'string') {
528            data[word_array].apply(data, Array.prototype.slice.call(args, 1));
529        }
530    });
531};
532
533$.fn.jQCloud.defaults = {
534    set: function(options) {
535        $.extend(true, jQCloud.DEFAULTS, options);
536    },
537    get: function(key) {
538        var options = jQCloud.DEFAULTS;
539        if (key) {
540            options = options[key];
541        }
542        return $.extend(true, {}, options);
543    }
544};
545
546}));

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.