PageSourceSearch

https://traitdunion.gr/wp-content/themes/traitdunion-eshop/inc/van…siville.responsiveelement.js?ver=1707094006

js traitdunion.gr collected 2026-10-04 02:09:42 UTC 15,300 bytes, 525 lines download raw bytes

1/**
2 * Responsive elements utility.
3 * 
4 * @class
5 * @extends   Responsiville.Debug
6 * @extends   Responsiville.Events
7 * 
8 * @author    Nevma, [email protected]
9 * @license   Nevma Copyright (c) http://www.nevma.gr
10 * 
11 * @fires     Responsiville.Responsiveelement#init
12 * 
13 * @classdesc Takes a casual element and makes it responsive. This way one can
14 *            set different behaviour and styling to the element per breakpoint
15 *            just as they do in a responsive web page as a whole.
16 *
17 * @param {Object} options The initialisation options of the module.
18 * 
19 * @property {Object}  options             The options that define the object
20 *                                         behaviour.
21 * @property {boolean} options.debug       Whether to print debug messages in
22 *                                         the browser console.
23 * @property {boolean} options.slug        A special codename for the instance
24 *                                         of the element, to be used as a
25 *                                         class on its container and as a key
26 *                                         in arrays  where it is grouped with
27 *                                         other elements of its kind. 
28 * @property {string}  options.breakpoints List of name and width pairs that
29 *                                         define the responsive breakpoints of
30 *                                         this element.
31 * @property {string}  options.element     The element to be enhanced with
32 *                                         responsive behaviour.
33 * @property {string}  options.enter       Comma separated list of breakpoints
34 *                                         in which the module enters, which
35 *                                         means it is enabled.
36 * @property {string}  options.leave       Comma separated list of breakpoints
37 *                                         in which the module leaves, which
38 *                                         means it is disabled.
39 */
40Responsiville.Responsiveelement = function( options ) {
41
42    // Responsiville must be initialised.
43    if( ! Responsiville.Main.getInstance() ) {
44        throw new Error( 'Responsiville instantiation error: the framework has not been initialised, call new Responsiville::Main() and then Responsiville::init().' );
45    }
46
47
48
49    // General settings setup.
50    this.enabled       = false;
51    this.options       = jQuery.extend( this.options, Responsiville.Responsiveelement.defaults, options );
52    this.codeName      = 'responsiville.responsiveelement';
53    this.responsiville = Responsiville.Main.getInstance();
54
55
56
57    // Cache important DOM elements.
58    this.$body     = this.responsiville.$body;
59    this.$document = this.responsiville.$document;
60    this.$element  = jQuery( this.options.element );
61
62
63
64    // The previous and current breakpoint of the element.
65    this.previousBreakpoint = null;
66    this.currentBreakpoint  = null;
67
68
69
70    // If no element found raise an error.
71    if( this.$element.length === 0 ) {
72        this.log( 'Responsiville.Responsiveelement instantiation error: no element found (' + this.options.element + ').' );
73        return;
74    }
75
76    if( this.$element.length > 1 ) {
77        this.log( 'Responsiville.Responsiveelement instantiation error: more than one elements found (' + this.options.element + ').' );
78        return;
79    }
80
81
82
83    // Check for developer settings inside the element's data attributes.
84    for( var key in this.options ) {
85
86        var value = this.$element.data( 'responsiville-responsiveelement-' + key );
87        if( typeof value !== 'undefined' ) {
88            this.options[key] = value;
89        }
90
91    }
92
93
94
95    // Uniqueue element slug.
96    if( this.options.slug == '' ) {
97        Responsiville.Responsiveelement.elementsCounter = Responsiville.Responsiveelement.elementsCounter !== undefined ? ++Responsiville.Responsiveelement.elementsCounter : 0;
98        this.options.slug = this.codeName.replace( '.', '-' ) + '-' + Responsiville.Responsiveelement.elementsCounter;
99    }
100
101
102
103    // Sort the breakpoints by ascending width
104    this.options.breakpoints.sort( function( breakpointA, breakpointB ) {
105        return breakpointA.width - breakpointB.width;
106    });
107
108
109
110    // Take special care for enter/leave breakpoints possibly given as HTML data attributes.
111    var htmlBreakpoints = Responsiville.determineEnableDisableBreakpoints({ 
112        enter: this.$element.data( 'responsiville-responsiveelement-enter' ),
113        leave: this.$element.data( 'responsiville-responsiveelement-leave' ) 
114    });
115
116    if( htmlBreakpoints !== null ) {
117        this.options.enter = htmlBreakpoints.enter;
118        this.options.leave = htmlBreakpoints.leave;
119    }
120
121
122
123    // Add this object as the main element's data attribute for API usage.
124    this.$element.data( 'responsiville-api', this );
125    this.$element.data( 'responsiville-responsiveelement-api', this );
126
127    // Uniquely identify this element.
128    this.$element.addClass( this.options.slug );
129
130    // Images inside the responsive element.
131    this.$images = this.$element.find( 'img' );
132
133    // Initialises the responsive element.
134    this.setupEvents();
135
136    // Enable right away if necessary.
137    if( this.responsiville.is( this.options.enter ) ) {
138        this.enable();
139    }
140
141    this.log( 'responsivelements initialised' );
142
143    /**
144     * Called after the module has been initialised.
145     * @event Responsiville.Responsiveelement#init
146     */
147    this.fireEvent( 'init' );
148
149};
150
151
152
153// Extend with logging functions.
154Responsiville.extend( Responsiville.Responsiveelement, Responsiville.Debug.Extensions );
155
156// Extend with event handling mechanism.
157Responsiville.extend( Responsiville.Responsiveelement, Responsiville.Events );
158
159
160
161/**
162 * @property {boolean} AUTO_RUN Controls whether this module should run by 
163 *                              default, without the developer calling it, using
164 *                              its default settings. Defaults to true.
165 */
166Responsiville.Responsiveelement.AUTO_RUN = typeof RESPONSIVILLE_AUTO_INIT !== 'undefined' ? RESPONSIVILLE_AUTO_INIT : true;
167
168
169
170/**
171 * @property {Object} defaults Default values for this module settings.
172 */
173Responsiville.Responsiveelement.defaults = {
174    debug       : false,
175    slug        : '',
176    breakpoints : [{ breakpoint1: 1024 }, { breakpoint2: 2048 }, { breakpoint3: 9999 }],
177    element     : '.responsiville-responsiveelement',
178    enter       : 'small, mobile, tablet, laptop, desktop, large, xlarge',
179    leave       : ''
180};
181
182
183
184/**
185 * Runs through the page and searches for elements that apply to the current 
186 * module in order to apply it to them automatically. Useful for automatically
187 * creating elements with this module's behaviour just by setting up the
188 * predefined classes and data attributes in HTML elements of the page.
189 *
190 * @function
191 * @static
192 * 
193 * @return {void}
194 */
195Responsiville.Responsiveelement.autoRun = function() {
196
197    jQuery( Responsiville.Responsiveelement.defaults.element ).each( function() {
198
199        new Responsiville.Responsiveelement({
200            debug     : Responsiville.Main.getInstance().options.debug,
201            element  : this
202        });
203        
204    });
205
206};
207
208
209
210/**
211 * Sets up event handlers necessary for the object to function properly. 
212 * 
213 * @return {void}
214 */
215Responsiville.Responsiveelement.prototype.setupEvents = function() {
216
217    var k, length;
218
219    // Register to be enabled on the required breakpoints.
220    var breakpointsEnter = Responsiville.splitAndTrim( this.options.enter );
221
222    for( k=0, length=breakpointsEnter.length; k<length; k++ ) {
223        this.responsiville.on( 'enter.' + breakpointsEnter[k], this.getBoundFunction( this.enable ) );
224    }
225
226    // Register to be disabled on the required breakpoints.
227    var breakpointsLeave = Responsiville.splitAndTrim( this.options.leave );
228
229    for( k=0, length=breakpointsLeave.length; k<length; k++ ) {
230        this.responsiville.on( 'enter.' + breakpointsLeave[k], this.getBoundFunction( this.disable ) );
231    }
232
233
234
235    // Check which images have loaded and wait for the rest of them.
236    for( k=0, length=this.$images.length; k < length; k++ ) {
237
238        var image = this.$images.eq( k ).get( 0 );
239
240        if( Responsiville.hasImageLoaded( image ) ) {
241            this.imagesLoaded++;
242        } else {
243            image.onload  = this.getBoundFunction( this.imageLoaded );
244            image.onerror = this.getBoundFunction( this.imageError );
245        }
246
247    }
248
249};
250
251
252
253/**
254 * Enables the responsive element.
255 *
256 * @fires Responsiville.Responsiveelement#enabled
257 * 
258 * @return {void}
259 */
260Responsiville.Responsiveelement.prototype.enable = function() {
261
262    if( this.enabled ) {
263        return;
264    }
265    this.enabled = true;
266    this.log( 'enable responsiveelement' );
267
268    // Process element on resize.
269    this.responsiville.on( 'resize', this.getBoundFunction( this.processElement ) );
270    this.processElement();
271
272    /**
273     * Called after the module has been enabled.
274     * @event Responsiville.Responsiveelement#enabled
275     */
276    this.fireEvent( 'enabled' );
277
278};
279
280
281
282/**
283 * Disables the responsive element.
284 * 
285 * @fires Responsiville.Responsiveelement#disabled
286 * 
287 * @return {void}
288 */
289Responsiville.Responsiveelement.prototype.disable = function() {
290
291    if( ! this.enabled ) {
292        return;
293    }
294    this.enabled = false;
295    this.log( 'disable responsiveelement' );
296
297
298    // Process element on resize.
299    this.responsiville.off( 'resize', this.getBoundFunction( this.processElement ) );
300
301    var removeClasses = '';
302    var breakpoint;
303
304    for( var k = 0; k < this.options.breakpoints.length; k++ ) {
305
306        breakpoint = this.options.breakpoints[k];
307        removeClasses += breakpoint.name + ' ';
308
309    }
310
311    this.$element.removeClass( removeClasses );
312
313    /**
314     * Called after the module has been disabled.
315     * @event Responsiville.Responsiveelement#disabled
316     */
317    this.fireEvent( 'disabled' );
318
319};
320
321
322
323/**
324 * Callback that runs when an image inside the responsive element
325 * has successfully loaded.
326 * 
327 * @param {Event} event The image loaded event that originally fired.
328 * 
329 * @fires Responsiville.Responsiveelement#image.loaded
330 * 
331 * @return {void}
332 */
333Responsiville.Responsiveelement.prototype.imageLoaded = function( event ) {
334
335    this.imagesLoaded++;
336
337    if( this.enabled && this.imagesLoaded == this.$images.length ) {
338        this.processElement();
339    }
340
341    /**
342     * Called after an image inside the responsive element has loaded.
343     * @event Responsiville.Responsiveelement#image.loaded
344     */
345    this.fireEvent( 'image.loaded', [ event ] );
346
347};
348
349
350
351/**
352 * Runs when an image inside the responsive element has aborted loading due to
353 * any error.
354 * 
355 * @param {Event} event The image error event that originally fired.
356 * 
357 * @fires Responsiville.Responsiveelement#image.error
358 * 
359 * @return {void}
360 */
361Responsiville.Responsiveelement.prototype.imageError = function( event ) {
362
363    this.imagesLoaded++;
364
365    if( this.enabled && this.imagesLoaded == this.$images.length ) {
366        this.processElement();
367    }
368
369    /**
370     * Called after an image inside the responsive element was unable to load
371     * due to an error.
372     * @event Responsiville.Responsiveelement#image.error
373     */
374    this.fireEvent( 'image.error', [ event ] );
375
376};
377
378
379
380/**
381 * Runs all the callback functions registered on the responsive element's
382 * breakpoint changes.
383 *
384 * @return {void}
385 */
386Responsiville.Responsiveelement.prototype.runBreakpointChangeCallbacks = function() {
387
388    // Run all callbacks set on leaving the previous breakpoint.
389    this.fireEvent( 'leave.' + this.previousBreakpoint.name, this.$element );
390
391    // Run all callbacks set on entering the current breakpoint.
392    this.fireEvent( 'enter.' + this.currentBreakpoint.name, this.$element );
393
394};
395
396
397
398/**
399 * Does whatever necessary on the element in order to satisfy its responsive
400 * behaviour. Runs on window resize events and on Responsiville resize and 
401 * breakpoint change events.
402 *
403 * @fires Responsiville.Responsiveelement#image.processing
404 * @fires Responsiville.Responsiveelement#image.change
405 * @fires Responsiville.Responsiveelement#image.processed
406
407 * @return {void}
408 */
409Responsiville.Responsiveelement.prototype.processElement = function() {
410
411    /**
412     * Called before processing a responsive element.
413     * @event Responsiville.Responsiveelement#processing
414     */
415    this.fireEvent( 'processing' );
416
417
418
419    var k;
420    var breakpoint;
421    var newBreakpoint;
422    var elementWidth;
423
424    elementWidth = this.$element.outerWidth();
425
426    // If an element has zero width, or is display:none, attempt a best guess by traversing its parents.
427    if( elementWidth === 0 ) {
428
429        this.log( 'non-displayed or zero-width element. Checking the element parents.' );
430
431        var $parents = jQuery( this.$element.parents() );
432
433        for( k = 0; k < $parents.length; k++ ) {
434
435            elementWidth = $parents.eq( k ).width();
436            if( $parents.eq( k ).css('display') !== 'none' && elementWidth !== 0 ) {
437                this.log( 'found displayed parent with non-zero width ' + elementWidth + 'px' );
438                break;
439            }
440
441        }
442
443    }
444
445    this.log( 'processing element with width ' + elementWidth + 'px' );
446
447
448
449    // Calculate the classes to remove
450    var removeClasses = '';
451
452    for( k = 0; k < this.options.breakpoints.length; k++ ) {
453
454        breakpoint = this.options.breakpoints[k];
455        removeClasses += breakpoint.name + ' ';
456
457    }
458
459    // Calculate the classes to add
460    var addClasses = '';
461
462    for( k = 0, length = this.options.breakpoints.length; k < length; k++ ) {
463
464        breakpoint = this.options.breakpoints[k];
465        if( elementWidth > breakpoint.width ) {
466            addClasses   += breakpoint.name + ' ';
467            newBreakpoint = breakpoint;
468        } else {
469            break;
470        }
471
472    }
473
474    if( elementWidth <= breakpoint.width ) {
475        addClasses   += breakpoint.name + ' ';
476        newBreakpoint = breakpoint;
477    }
478
479    this.log( 'removing classes: ' + addClasses );
480    this.log( 'adding classes: ' + addClasses );
481
482    this.$element.removeClass( removeClasses );
483    this.$element.addClass( addClasses );
484
485
486
487    // Sets up current breakpoint the first time the module runs.
488    var firstRun = false;
489
490    if( ! this.currentBreakpoint ) {
491        firstRun = true;
492        this.currentBreakpoint = newBreakpoint;
493    }
494
495    var oldBreakpoint     = this.currentBreakpoint;
496    var breakpointChanged = firstRun || oldBreakpoint.name != newBreakpoint.name;
497
498    if( breakpointChanged ) {
499
500        this.log( 'breakpoint change detected from ' + this.currentBreakpoint.name + ' to ' + newBreakpoint.name );
501
502        this.previousBreakpoint = this.currentBreakpoint;
503        this.currentBreakpoint = newBreakpoint;
504
505        this.runBreakpointChangeCallbacks();
506
507        /**
508         * Called after a breakpoint change has occured.
509         * @event Responsiville.Responsiveelement#change
510         */
511        this.fireEvent( 'change', this.$element );
512
513    }
514
515
516
517    this.log( 'processed element' );
518    
519    /**
520     * Called after processing a responsive element.
521     * @event Responsiville.Responsiveelement#processed.element
522     */
523    this.fireEvent( 'processed' );
524
525};

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.