PageSourceSearch

https://bordairrace.com/components/com_roksprocket/layouts/mosaic/assets/js/mosaic.js

js bordairrace.com collected 2026-09-27 02:00:16 UTC 23,291 bytes, 714 lines download raw bytes

1/*!
2 * @version   $Id: mosaic.js 10889 2013-05-30 07:48:35Z btowles $
3 * @author    RocketTheme http://www.rockettheme.com
4 * @copyright Copyright (C) 2007 - 2019 RocketTheme, LLC
5 * @license   http://www.gnu.org/licenses/gpl-2.0.html GNU/GPLv2 only
6 */
7((function(){
8	if (typeof this.RokSprocket == 'undefined') this.RokSprocket = {};
9	else Object.merge(this.RokSprocket, {Mosaic: null, MosaicBuilder: null});
10
11	var supportsOrientationChange = "onorientationchange" in window;
12
13	var Mosaic = new Class({
14
15		Implements: [Options, Events],
16
17		options: {
18			settings: {}
19		},
20
21		initialize: function(options){
22			this.setOptions(options);
23
24			this.mosaics = document.getElements('[data-mosaic]');
25			this.mosaic = {};
26			this.settings = {};
27			this.curve = Browser.opera ? {equation: 'ease-in-out'} : {curve: 'cubic-bezier(0.37,0.61,0.59,0.87)'};
28
29			try {
30				RokMediaQueries.on('every', this.mediaQuery.bind(this));
31			}
32			catch(error) { if (typeof console != 'undefined') console.error('Error while trying to add a RokMediaQuery "match" event', error); }
33		},
34
35		attach: function(mosaic, settings){
36			mosaic = typeOf(mosaic) == 'number' ?
37					document.getElements('[data-mosaic=' + this.getID(mosaic) + ']')
38					:
39					mosaic;
40			settings = typeOf(settings) == 'string' ? JSON.decode(settings) : settings;
41
42			var containers = (mosaic ? new Elements([mosaic]).flatten() : this.mosaics);
43
44			containers.each(function(container){
45				container.store('roksprocket:mosaic:attached', true);
46
47				this.setSettings(container, settings, 'restore');
48
49				var relay = {
50					loadmore: container.retrieve('roksprocket:mosaic:loadmore', function(event, page){
51						if (event) event.preventDefault();
52						this.loadMore.call(this, event, container, page);
53					}.bind(this)),
54
55					ordering: container.retrieve('roksprocket:mosaic:ordering', function(event, element){
56						this.orderBy.call(this, event, container, element);
57					}.bind(this)),
58
59					filtering: container.retrieve('roksprocket:mosaic:filtering', function(event, element){
60						this.filterBy.call(this, event, container, element);
61					}.bind(this)),
62
63					document: document.retrieve('roksprocket:mosaic:document', function(event, element){
64						this.toggleShift.call(this, event, container, element);
65					}.bind(this))
66				};
67
68				container.addEvent('click:relay([data-mosaic-loadmore])', relay['loadmore']);
69				container.addEvent('click:relay([data-mosaic-orderby])', relay['ordering']);
70				container.addEvent('click:relay([data-mosaic-filterby])', relay['filtering']);
71
72				container.retrieve('roksprocket:mosaic:ajax', new RokSprocket.Request({
73					model: 'mosaic',
74					model_action: 'getPage',
75					onRequest: this.onRequest.bind(this, container),
76					onSuccess: function(response){
77						this.onSuccess(response, container, container.retrieve('roksprocket:mosaic:ajax'));
78					}.bind(this)
79				}));
80
81				document.addEvents({
82					'keydown:keys(shift)': relay['document'],
83					'keyup:keys(shift)': relay['document']
84				});
85
86				this.initializeMosaic(container, function(){
87					this.mediaQuery.delay(5, this, RokMediaQueries.getQuery());
88				}.bind(this));
89			}, this);
90		},
91
92		detach: function(mosaic){
93			mosaic = typeOf(mosaic) == 'number' ?
94					document.getElements('[data-mosaic=' + this.getID(mosaic) + ']')
95					:
96					mosaic;
97
98			var containers = (mosaic ? new Elements([mosaic]).flatten() : this.mosaics);
99
100			containers.each(function(container){
101				container.store('roksprocket:mosaic:attached', false);
102				var relay = {
103					loadmore: container.retrieve('roksprocket:mosaic:loadmore'),
104					ordering: container.retrieve('roksprocket:mosaic:ordering'),
105					filtering: container.retrieve('roksprocket:mosaic:filtering'),
106					document: document.retrieve('roksprocket:mosaic:document')
107				};
108
109				container.removeEvent('click:relay([data-mosaic-loadmore])', relay['loadmore']);
110				container.removeEvent('click:relay([data-mosaic-orderby])', relay['ordering']);
111				container.removeEvent('click:relay([data-mosaic-filterby])', relay['filtering']);
112				document.removeEvents({
113					'keydown:keys(shift)': relay['document'],
114					'keyup:keys(shift)': relay['document']
115				});
116
117			}, this);
118		},
119
120		mediaQuery: function(query){
121			var mosaic;
122
123			for (var id in this.mosaic){
124				mosaic = this.mosaic[id];
125				mosaic.resize('fast');
126			}
127		},
128
129		setSettings: function(container, settings, restore){
130			var id = this.getID(container),
131				options = Object.clone(this.getSettings(container) || this.options.settings);
132
133			if (!restore || !this.settings['id-' + id]){
134				this.settings['id-' + id] = Object.merge(options, settings || options);
135			}
136		},
137
138		getSettings: function(container){
139			var id = this.getID(container);
140
141			return this.settings['id-' + id];
142		},
143
144		getContainer: function(container){
145			if (!container) container = document.getElements('[data-mosaic]');
146			if (typeOf(container) == 'number') container = document.getElement('[data-mosaic='+container+']');
147			if (typeOf(container) == 'string') container = document.getElement(container);
148
149			return container;
150		},
151
152		getID: function(id){
153			if (typeOf(id) == 'number') id = document.getElement('[data-mosaic='+id+']');
154			if (typeOf(id) == 'string') id = document.getElement(id);
155			return !id ? id : id.get('data-mosaic');
156		},
157
158		loadMore: function(event, container, page){
159			container = this.getContainer(container);
160			page = (typeOf(page) == 'number') ? page : this.getSettings(container).page || 1;
161			if (!container.retrieve('roksprocket:mosaic:attached')) return;
162
163			var ajax = container.retrieve('roksprocket:mosaic:ajax'),
164				filterby = container.getElement('[data-mosaic-filterby].active'),
165				params = {
166					moduleid: container.get('data-mosaic'),
167					behavior: !page ? 'reset' : 'append',
168					displayed: !page ? [] : this.getSettings(container).displayed || [],
169					filter: filterby ? filterby.get('data-mosaic-filterby') || 'all' : 'all',
170					page: ++page
171				};
172
173			if (event && event.shift) params.all = true;
174
175			if (!ajax.isRunning()){
176				ajax.cancel().setParams(params).send();
177			}
178		},
179
180		filterBy: function(event, container, element){
181			container.getElements('[data-mosaic-filterby]').removeClass('active');
182			element.addClass('active');
183
184			container.addClass('refreshing');
185			this.loadMore(event, container, 0);
186
187		},
188
189		nextAll: function(containers, element){
190			containers = this.getContainer(containers);
191			if (typeOf(containers) == 'element') return this.next(containers, element);
192
193			containers.each(function(container){
194				this.next(container, element);
195			}, this);
196		},
197
198		toggleShift: function(event, container, element){
199			var type = event.type || 'keyup',
200				loadmore = document.getElements('[data-mosaic-loadmore]');
201
202			if (!loadmore.length) return true;
203
204			if (type == 'keydown') loadmore.addClass('load-all');
205			else loadmore.removeClass('load-all');
206		},
207
208		onRequest: function(container){
209			var loadmore = container.getElements('[data-mosaic-loadmore]');
210			if (loadmore) loadmore.addClass('loader');
211
212			this.detach(container);
213		},
214
215		onSuccess: function(response, container){
216			var id = 'id-' + this.getID(container),
217				ajax = container.retrieve('roksprocket:mosaic:ajax'),
218				items = container.getElement('[data-mosaic-items]'),
219				html = response.getPath('payload.html'),
220				page = response.getPath('payload.page'),
221				more = response.getPath('payload.more'),
222				behavior = response.getPath('payload.behavior'),
223				displayed = response.getPath('payload.displayed'),
224				settings = this.getSettings(container),
225				animations = settings.animations,
226				imgList;
227
228			this.setSettings(container, {page: (behavior == 'reset' ? 1 : page), displayed: displayed});
229			container.removeClass('refreshing');
230
231			var dummy = new Element('div', {html: html}),
232				elements = dummy.getChildren(),
233				styles = {},
234				options = {};
235
236			styles = this.getAnimation(container, '_set').style;
237
238			moofx(elements).style(styles);
239			items.adopt(elements);
240			imgList = new Elements(elements.getElements('img').flatten());
241
242			this._loadImages(imgList.get('src'), function(){
243				if (behavior == 'reset'){
244					this.mosaic[id].bricks.each(function(element, i){
245						(function(){
246							styles = this.getAnimation(container, '_out');
247							moofx(element).style(styles.style);
248							options = Object.merge({}, this.curve, {duration: '250ms', callback: function(){ element.dispose(); }});
249							moofx(element).animate(styles.animate, options);
250						}).delay(i * 50, this);
251					}, this);
252				}
253				this.mosaic[id][behavior](elements, function(){
254					loadmore = container.getElements('[data-mosaic-loadmore]');
255					if (loadmore) loadmore.removeClass('loader');
256
257					elements = this.mosaic[id].bricks.filter(function(element){ return elements.contains(element); });
258					elements.each(function(element, i){
259						(function(){
260							styles = this.getAnimation(container, '_in');
261							options = Object.merge({}, this.curve, {curve: 'cubic-bezier(0.37,0.61,0.59,0.87)', duration: '300ms'});
262							moofx(element).animate(styles.animate, options);
263						}).delay(i * 100, this);
264					}, this);
265
266					this.attach(container);
267					container.getElements('[data-mosaic-loadmore]').removeClass('load-all')[!more ? 'addClass' : 'removeClass']('hide');
268
269				}.bind(this));
270			}.bind(this));
271
272		},
273
274		getAnimation: function(container, what){
275			var settings = this.getSettings(container),
276				type = settings.animations || null,
277				styles = {},
278				animation = {
279					_set: {style: {opacity: 0}, animate: {}},
280					_out: {style: {opacity: 1}, animate: {opacity: 0}},
281					_in: {style: {}, animate: {opacity: 1}}
282				};
283
284			type = type ? type.erase('fade') : null;
285			if (type && type.contains('flip')) type = type.erase('scale').erase('rotate');
286
287			// types: _set / _out / _in
288			switch(type ? type.join(',') : null){
289				case 'scale':
290					animation['_set']['style'] = Object.merge(animation['_set']['style'], {
291						transform: 'scale(0.5)'
292					});
293					animation['_out']['style'] = Object.merge(animation['_out']['style'], {
294						'transform-origin': '50% 50%'
295					});
296					animation['_out']['animate'] = Object.merge(animation['_out']['animate'], {
297						transform: Browser.ie9 ? 'scale(0.001)' : 'scale(0)'
298					});
299					animation['_in']['animate'] = Object.merge(animation['_in']['animate'], {
300						transform: Browser.ie9 || Browser.opera ? 'matrix(1, 0, 0, 1, 0, 0)' : 'scale(1)'
301					});
302					break;
303
304				case 'rotate':
305					animation['_set']['style'] = Object.merge(animation['_set']['style'], {
306						'transform-origin': '0 0',
307						transform: 'rotate(-10deg)'
308					});
309					animation['_out']['style'] = Object.merge(animation['_out']['style'], {
310						'transform-origin': '0 0'
311					});
312					animation['_out']['animate'] = Object.merge(animation['_out']['animate'], {
313						transform: 'rotate(10deg)'
314					});
315					animation['_in']['animate'] = Object.merge(animation['_in']['animate'], {
316						transform: 'rotate(0)'
317					});
318					break;
319
320				case 'rotate,scale': case 'scale,rotate':
321					animation['_set']['style'] = Object.merge(animation['_set']['style'], {
322						'transform-origin': '0 0',
323						transform: 'scale(0.5) rotate(-10deg)'
324					});
325					animation['_out']['style'] = Object.merge(animation['_out']['style'], {
326						'transform-origin': '50% 50%'
327					});
328					animation['_out']['animate'] = Object.merge(animation['_out']['animate'], {
329						transform: Browser.ie9 ? 'scale(0.001) rotate(10deg)' : 'scale(0) rotate(10deg)'
330					});
331					animation['_in']['animate'] = Object.merge(animation['_in']['animate'], {
332						transform: Browser.ie9 || Browser.opera ? 'matrix(1, 0, 0, 1, 0, 0)' : 'scale(1) rotate(0)'
333					});
334					break;
335
336				case 'flip':
337					animation['_set']['style'] = Object.merge(animation['_set']['style'], {
338						'transform-origin': '50% 50%',
339						transform: 'scale(0.5) rotateY(360deg)'
340					});
341					animation['_out']['style'] = Object.merge(animation['_out']['style'], {
342						'transform-origin': '50% 50%'
343					});
344					animation['_out']['animate'] = Object.merge(animation['_out']['animate'], {
345						transform: Browser.ie9 ? 'scale(0.0001) rotateY(360deg)' : 'scale(0.5) rotateY(360deg)'
346					});
347					animation['_in']['animate'] = Object.merge(animation['_in']['animate'], {
348						transform: 'scale(1) rotateY(0)'
349					});
350					break;
351				default:
352			}
353
354			return animation[what];
355
356		},
357
358		orderBy: function(event, container, element){
359			var id = 'id-' + this.getID(container);
360
361			if (!this.mosaic || !this.mosaic[id]) throw new Error('RokSprocket Mosaic: Mosaic class not available');
362
363			var orderBy = element.get('data-mosaic-orderby');
364			this.mosaic[id].order(orderBy/*, null, function(callback){}*/);
365
366			container.getElements('[data-mosaic-orderby]').removeClass('active');
367			if (orderBy != 'random') element.addClass('active');
368		},
369
370		initializeMosaic: function(container, callback){
371			var id = 'id-' + this.getID(container), loadmore;
372
373			if (this.mosaic && this.mosaic[id]){
374				if (typeof callback == 'function') callback.call(this.mosaic[id].bricks);
375
376				loadmore = container.getElements('[data-mosaic-loadmore]');
377				if (loadmore) loadmore.removeClass('loader');
378
379				return this.mosaic[id];
380			}
381
382			var imgList = container.getElements('img'),
383				elements = container.getElement('[data-mosaic-items]'),
384				activeOrder = container.getElement('.active[data-mosaic-orderby]'),
385				options = {
386					container: container,
387					animated: true,
388					gutter: 0,
389					order: activeOrder ? activeOrder.get('data-mosaic-orderby') : (container.getElements('[data-mosaic-orderby]').length ? 'random' : 'default')
390				},
391				items = elements.getElements('[data-mosaic-item]');
392
393			if (!items.length) return this.mosaic[id];
394
395			if (callback && typeof callback == 'function') options.callback = callback;
396
397			moofx(elements).style({'transform-style': 'preserve-3d', 'backface-visibility': 'hidden', opacity: 1});
398			moofx(items).style(this.getAnimation(container, '_in').animate);
399			if (!imgList.length){
400				loadmore = container.getElements('[data-mosaic-loadmore]');
401				if (loadmore) loadmore.removeClass('loader');
402
403				this.mosaic[id] = new RokSprocket.MosaicBuilder(elements, options);
404			} else {
405				this._loadImages(imgList.get('src'), function(){
406					loadmore = container.getElements('[data-mosaic-loadmore]');
407					if (loadmore) loadmore.removeClass('loader');
408
409					this.mosaic[id] = new RokSprocket.MosaicBuilder(elements, options);
410				}.bind(this));
411			}
412
413			return this.mosaic[id];
414		},
415
416		_loadImages: function(images, callback){
417			return images.length ? new Asset.images(images, {onComplete: callback.bind(this)}) : callback.bind(this)();
418		}
419
420	});
421
422
423	var MosaicBuilder = new Class({
424
425		Implements: [Options, Events],
426
427		options: {
428			/*columnWidth: function(){},*/
429			container: null,
430			resizeable: false,
431			animated: false,
432			gutter: 0,
433			fitwidth: false,
434			order: 'default',
435			containerStyle: {
436				position: 'relative'
437			}
438		},
439
440		initialize: function(element, options){
441			this.setOptions(options);
442
443			this.element = document.id(element) || document.getElement(element) || null;
444
445			if (!this.element) throw new Error('Mosaic Builder Error: Element "'+element+'" not found in the DOM.');
446
447			this.styleQueue = [];
448			this.curve = Browser.opera ? {equation: 'ease-in-out'} : {curve: 'cubic-bezier(0.37,0.61,0.59,0.87)'};
449			this.originalState = this.getBricks();
450
451			this.build();
452			this.init(options.callback || null);
453		},
454
455		build: function(){
456			var styles = this.element.style;
457
458			this.originalStyle = {
459				height: styles.height || ''
460			};
461
462			Object.each(this.options.containerStyle, function(value, prop){
463				this.originalStyle[prop] = styles[prop] || '';
464			}, this);
465
466			moofx(this.element).style(this.originalStyle);
467
468			this.offset = {
469				x: this.element.getStyle('padding-left').toInt(),
470				y: this.element.getStyle('padding-top').toInt()
471			};
472
473			this.isFluid = this.options.columnWidth && typeof this.options.columnWidth === 'function';
474
475			this.reloadItems(this.options.order || null);
476
477		},
478
479		init: function(callback){
480			this.getColumns();
481			this.reLayout(callback);
482		},
483
484		getBricks: function(items){
485			return (items ? items : this.element.getElements('[data-mosaic-item]')).setStyle('position', 'absolute');
486		},
487
488		reloadItems: function(order, items){
489			this.bricks = this.getBricks(items);
490			if (order == 'random' || order == 'default'){
491				if (order == 'random') this.bricks = this.bricks.shuffle();
492				if (order == 'default') this.bricks = this.originalState.clone();
493
494				return this.bricks;
495			}
496
497			this.bricks = order ? this.orderBy(order) : this.bricks;
498
499			return this.bricks;
500		},
501
502		orderBy: function(order){
503			var errorCheck = false;
504
505			return this.bricks.sort(function(a, b){
506				var aSort = a.getElement('[data-mosaic-order-' + order + ']'),
507					bSort = b.getElement('[data-mosaic-order-' + order + ']');
508
509				if (!aSort || !bSort){
510					if (console && console.error && !errorCheck) console.error('RokSprocket MosaicBuilder: Trying to sort by "'+order+'" but no sorting rule has been found.');
511					errorCheck = true;
512					return 0;
513				}
514
515				aSort = aSort.get('data-mosaic-order-' + order);
516				bSort = bSort.get('data-mosaic-order-' + order);
517
518				return aSort == bSort ? 0 : (aSort < bSort ? -1 : 1);
519			}.bind(this));
520		},
521
522		reload: function(callback){
523			this.reloadItems();
524			this.init(callback);
525		},
526
527		layout: function(bricks, callback, fast){
528			for (var i = 0, len = bricks.length; i < len; i++){
529				this.placeBrick(bricks[i]);
530			}
531
532			var containerSize = {}, options = {};
533			containerSize.height = Math.max.apply(Math, this.colYs);
534
535			if (this.options.fitwidth){
536				var unused = 0;
537				i = this.cols;
538
539				while (--i){
540					if (this.colYs[i] !== 0) break;
541					unused++;
542				}
543
544				containerSize.width = (this.cols - unused) * this.columnWidth - this.options.gutter;
545			}
546
547			this.styleQueue.push({element: this.element, style: containerSize});
548			var styleFn = !this.isLaidOut ? 'style' : (this.options.animated && !fast ? 'animate' : 'style'), obj;
549
550			this.styleQueue.each(function(style, i){
551				options = Object.merge({}, this.curve, {duration: '400ms'});
552
553				if (i == this.styleQueue.length - 1) if (callback) options.callback = callback.bind(callback, bricks);
554				moofx(style.element)[styleFn](style.style, options);
555			}, this);
556
557			this.styleQueue.empty();
558
559			if (callback && styleFn == 'style') callback.call(bricks);
560
561			this.isLaidOut = true;
562		},
563
564		getColumns: function(){
565			var container = this.options.fitwidth ? this.element.getParent() : this.element,
566				containerWidth = container.offsetWidth;
567
568			this.columnWidth =	this.isFluid ? this.options.columnWidth(containerWidth) : this.options.columnWidth ||
569								(this.bricks.length && this.bricks[0].offsetWidth) ||
570								containerWidth;
571
572			this.columnWidth += this.options.gutter;
573			this.cols = Math.round((containerWidth + this.options.gutter) / this.columnWidth);
574			this.cols = Math.max(this.cols, 1);
575		},
576
577		placeBrick: function(brick){
578			brick = document.id(brick);
579			var colSpan, groupCount, groupY, groupColY;
580
581			colSpan = Math.round(brick.offsetWidth / (this.columnWidth + this.options.gutter));
582			colSpan = Math.min(colSpan, this.cols);
583
584			if (colSpan == 1) groupY = this.colYs;
585			else {
586				groupCount = this.cols + 1 - colSpan;
587				groupY = [];
588
589				(groupCount).times(function(i){
590					groupColY = this.colYs.slice(i, i + colSpan);
591					groupY[i] = Math.max.apply(Math, groupColY);
592				}, this);
593			}
594
595			var minimumY = Math.min.apply(Math, groupY), shortCol = 0;
596
597			for (var i = 0, len = groupY.length; i < len; i++){
598				if (groupY[i] === minimumY){
599					shortCol = i;
600					break;
601				}
602			}
603
604			var position = {
605				top: minimumY + this.offset.y
606			};
607
608			//position.left = this.columnWidth * shortCol + this.offset.x;
609			position.left = shortCol * (100 / this.cols) + '%';
610			this.styleQueue.push({element: brick, style: position});
611
612			var setHeight = minimumY + brick.offsetHeight + ((this.options.gutter || 0)),
613				setSpan = this.cols + 1 - groupY.length;
614
615			(setSpan).times(function(i){
616				this.colYs[shortCol + i] = setHeight;
617			}, this);
618		},
619
620		resize: function(fast){
621			var prevColCount = this.cols;
622			this.getColumns();
623
624			if ((this.isFluid || fast) && this.cols !== prevColCount || fast) this.reLayout(null, fast);
625		},
626
627		reLayout: function(callback, fast){
628			var i = this.cols;
629			this.colYs = [];
630			while (i--) this.colYs.push(0);
631
632			this.layout(this.bricks, callback, fast);
633		},
634
635		reset: function(content, callback){
636			content = content.filter(function(element){
637				return element.get('data-mosaic-item') !== null || element.getElement('data-mosaic-item');
638			});
639
640			this.bricks = this.originalState = new Elements();
641
642			content.setStyles({'top': 0, left: 0, position: 'absolute'});
643			this.appendedBricks.delay(1, this, [content, content, callback]);
644		},
645
646		append: function(content, callback){
647			content = content.filter(function(element){
648				return element.get('data-mosaic-item') !== null || element.getElement('data-mosaic-item');
649			});
650
651			if (!content) return;
652
653			content.setStyles({'top': this.element.getSize().y, left: 0, position: 'absolute'});
654			this.appendedBricks.delay(1, this, [content, null, callback]);
655		},
656
657		appendedBricks: function(content, items, callback){
658			var order = this.options.container.getElement('[data-mosaic-orderby].active') || this.options.container.getElement('[data-mosaic-orderby=random]'),
659				by = order ? order.get('data-mosaic-orderby') : (this.options.container.getElements('[data-mosaic-orderby]').length ? 'random' : 'default');
660
661			this.originalState.append(content);
662			this.order(by, items, callback);
663			//this.reloadItems(by, callback);
664			//this.reload(callback);
665			//this.layout(content, callback);
666		},
667
668		order: function(type, items, callback){
669			this.reloadItems(type, items || null);
670			this.init(callback);
671		}
672
673	});
674
675	this.RokSprocket.Mosaic = Mosaic;
676	this.RokSprocket.MosaicBuilder = MosaicBuilder;
677
678	if (supportsOrientationChange){
679		window.addEventListener('orientationchange', function(){
680			if (typeof RokSprocket == 'undefined' || typeof RokSprocket.instances == 'undefined' || typeof RokSprocket.instances.mosaic == 'undefined') return;
681			var mosaic;
682
683			for (var id in RokSprocket.instances.mosaic.mosaic){
684				mosaic = RokSprocket.instances.mosaic.mosaic[id];
685				mosaic.resize('fast');
686			}
687		});
688	}
689
690	Element.implement({
691		mosaic : function(options) {
692			var mosaic = this.retrieve('roksprocket:mosaic:builder');
693			if (!mosaic) mosaic = this.store('roksprocket:mosaic:builder', new RokSprocket.MosaicBuilder(this, options));
694
695			return mosaic;
696		}
697	});
698
699	/* Ugly workaround for data-sets issue for IE < 9 on Moo < 1.4.4 */
700	if (MooTools.version < "1.4.4" && (Browser.name == 'ie' && Browser.version < 9)){
701		((function(){
702			var dataList = [
703				'rel', 'data-next',
704				'data-mosaic', 'data-mosaic-items', 'data-mosaic-item', 'data-mosaic-content', 'data-mosaic-page', 'data-mosaic-next',
705				'data-mosaic-order', 'data-mosaic-orderby', 'data-mosaic-order-title', 'data-mosaic-order-date',
706				'data-mosaic-filterby', 'data-mosaic-loadmore'
707			];
708
709			dataList.each(function(data){
710				Element.Properties[data] = {get: function(){ return this.getAttribute(data); }};
711			});
712		})());
713	}
714})());

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.