]> mj.ucw.cz Git - gallery.git/blob - highslide/highslide-with-gallery.js
The big rename
[gallery.git] / highslide / highslide-with-gallery.js
1 /**
2  * Name:    Highslide JS
3  * Version: 4.1.13 (2011-10-06)
4  * Config:  default +events +slideshow +positioning +transitions +viewport +thumbstrip
5  * Author:  Torstein Hønsi
6  * Support: www.highslide.com/support
7  * License: www.highslide.com/#license
8  */
9 if (!hs) { var hs = {
10 // Language strings
11 lang : {
12         cssDirection: 'ltr',
13         loadingText : 'Loading...',
14         loadingTitle : 'Click to cancel',
15         focusTitle : 'Click to bring to front',
16         fullExpandTitle : 'Expand to actual size (f)',
17         creditsText : 'Powered by <i>Highslide JS</i>',
18         creditsTitle : 'Go to the Highslide JS homepage',
19         previousText : 'Previous',
20         nextText : 'Next',
21         moveText : 'Move',
22         closeText : 'Close',
23         closeTitle : 'Close (esc)',
24         resizeTitle : 'Resize',
25         playText : 'Play',
26         playTitle : 'Play slideshow (spacebar)',
27         pauseText : 'Pause',
28         pauseTitle : 'Pause slideshow (spacebar)',
29         previousTitle : 'Previous (arrow left)',
30         nextTitle : 'Next (arrow right)',
31         moveTitle : 'Move',
32         fullExpandText : '1:1',
33         number: 'Image %1 of %2',
34         restoreTitle : 'Click to close image, click and drag to move. Use arrow keys for next and previous.'
35 },
36 // See http://highslide.com/ref for examples of settings
37 graphicsDir : 'highslide/graphics/',
38 expandCursor : 'zoomin.cur', // null disables
39 restoreCursor : 'zoomout.cur', // null disables
40 expandDuration : 250, // milliseconds
41 restoreDuration : 250,
42 marginLeft : 15,
43 marginRight : 15,
44 marginTop : 15,
45 marginBottom : 15,
46 zIndexCounter : 1001, // adjust to other absolutely positioned elements
47 loadingOpacity : 0.75,
48 allowMultipleInstances: true,
49 numberOfImagesToPreload : 5,
50 outlineWhileAnimating : 2, // 0 = never, 1 = always, 2 = HTML only
51 outlineStartOffset : 3, // ends at 10
52 padToMinWidth : false, // pad the popup width to make room for wide caption
53 fullExpandPosition : 'bottom right',
54 fullExpandOpacity : 1,
55 showCredits : true, // you can set this to false if you want
56 creditsHref : 'http://highslide.com/',
57 creditsTarget : '_self',
58 enableKeyListener : true,
59 openerTagNames : ['a'], // Add more to allow slideshow indexing
60 transitions : [],
61 transitionDuration: 250,
62 dimmingOpacity: 0, // Lightbox style dimming background
63 dimmingDuration: 50, // 0 for instant dimming
64
65 anchor : 'auto', // where the image expands from
66 align : 'auto', // position in the client (overrides anchor)
67 targetX: null, // the id of a target element
68 targetY: null,
69 dragByHeading: true,
70 minWidth: 200,
71 minHeight: 200,
72 allowSizeReduction: true, // allow the image to reduce to fit client size. If false, this overrides minWidth and minHeight
73 outlineType : 'drop-shadow', // set null to disable outlines
74 skin : {
75         controls:
76                 '<div class="highslide-controls"><ul>'+
77                         '<li class="highslide-previous">'+
78                                 '<a href="#" title="{hs.lang.previousTitle}">'+
79                                 '<span>{hs.lang.previousText}</span></a>'+
80                         '</li>'+
81                         '<li class="highslide-play">'+
82                                 '<a href="#" title="{hs.lang.playTitle}">'+
83                                 '<span>{hs.lang.playText}</span></a>'+
84                         '</li>'+
85                         '<li class="highslide-pause">'+
86                                 '<a href="#" title="{hs.lang.pauseTitle}">'+
87                                 '<span>{hs.lang.pauseText}</span></a>'+
88                         '</li>'+
89                         '<li class="highslide-next">'+
90                                 '<a href="#" title="{hs.lang.nextTitle}">'+
91                                 '<span>{hs.lang.nextText}</span></a>'+
92                         '</li>'+
93                         '<li class="highslide-move">'+
94                                 '<a href="#" title="{hs.lang.moveTitle}">'+
95                                 '<span>{hs.lang.moveText}</span></a>'+
96                         '</li>'+
97                         '<li class="highslide-full-expand">'+
98                                 '<a href="#" title="{hs.lang.fullExpandTitle}">'+
99                                 '<span>{hs.lang.fullExpandText}</span></a>'+
100                         '</li>'+
101                         '<li class="highslide-close">'+
102                                 '<a href="#" title="{hs.lang.closeTitle}" >'+
103                                 '<span>{hs.lang.closeText}</span></a>'+
104                         '</li>'+
105                 '</ul></div>'
106 },
107 // END OF YOUR SETTINGS
108
109
110 // declare internal properties
111 preloadTheseImages : [],
112 continuePreloading: true,
113 expanders : [],
114 overrides : [
115         'allowSizeReduction',
116         'useBox',
117         'anchor',
118         'align',
119         'targetX',
120         'targetY',
121         'outlineType',
122         'outlineWhileAnimating',
123         'captionId',
124         'captionText',
125         'captionEval',
126         'captionOverlay',
127         'headingId',
128         'headingText',
129         'headingEval',
130         'headingOverlay',
131         'creditsPosition',
132         'dragByHeading',
133         'autoplay',
134         'numberPosition',
135         'transitions',
136         'dimmingOpacity',
137
138         'width',
139         'height',
140
141         'wrapperClassName',
142         'minWidth',
143         'minHeight',
144         'maxWidth',
145         'maxHeight',
146         'pageOrigin',
147         'slideshowGroup',
148         'easing',
149         'easingClose',
150         'fadeInOut',
151         'src'
152 ],
153 overlays : [],
154 idCounter : 0,
155 oPos : {
156         x: ['leftpanel', 'left', 'center', 'right', 'rightpanel'],
157         y: ['above', 'top', 'middle', 'bottom', 'below']
158 },
159 mouse: {},
160 headingOverlay: {},
161 captionOverlay: {},
162 timers : [],
163
164 slideshows : [],
165
166 pendingOutlines : {},
167 clones : {},
168 onReady: [],
169 uaVersion: /Trident\/4\.0/.test(navigator.userAgent) ? 8 :
170         parseFloat((navigator.userAgent.toLowerCase().match( /.+(?:rv|it|ra|ie)[\/: ]([\d.]+)/ ) || [0,'0'])[1]),
171 ie : (document.all && !window.opera),
172 //ie : navigator && /MSIE [678]/.test(navigator.userAgent), // ie9 compliant?
173 safari : /Safari/.test(navigator.userAgent),
174 geckoMac : /Macintosh.+rv:1\.[0-8].+Gecko/.test(navigator.userAgent),
175
176 $ : function (id) {
177         if (id) return document.getElementById(id);
178 },
179
180 push : function (arr, val) {
181         arr[arr.length] = val;
182 },
183
184 createElement : function (tag, attribs, styles, parent, nopad) {
185         var el = document.createElement(tag);
186         if (attribs) hs.extend(el, attribs);
187         if (nopad) hs.setStyles(el, {padding: 0, border: 'none', margin: 0});
188         if (styles) hs.setStyles(el, styles);
189         if (parent) parent.appendChild(el);
190         return el;
191 },
192
193 extend : function (el, attribs) {
194         for (var x in attribs) el[x] = attribs[x];
195         return el;
196 },
197
198 setStyles : function (el, styles) {
199         for (var x in styles) {
200                 if (hs.ieLt9 && x == 'opacity') {
201                         if (styles[x] > 0.99) el.style.removeAttribute('filter');
202                         else el.style.filter = 'alpha(opacity='+ (styles[x] * 100) +')';
203                 }
204                 else el.style[x] = styles[x];
205         }
206 },
207 animate: function(el, prop, opt) {
208         var start,
209                 end,
210                 unit;
211         if (typeof opt != 'object' || opt === null) {
212                 var args = arguments;
213                 opt = {
214                         duration: args[2],
215                         easing: args[3],
216                         complete: args[4]
217                 };
218         }
219         if (typeof opt.duration != 'number') opt.duration = 250;
220         opt.easing = Math[opt.easing] || Math.easeInQuad;
221         opt.curAnim = hs.extend({}, prop);
222         for (var name in prop) {
223                 var e = new hs.fx(el, opt , name );
224
225                 start = parseFloat(hs.css(el, name)) || 0;
226                 end = parseFloat(prop[name]);
227                 unit = name != 'opacity' ? 'px' : '';
228
229                 e.custom( start, end, unit );
230         }
231 },
232 css: function(el, prop) {
233         if (el.style[prop]) {
234                 return el.style[prop];
235         } else if (document.defaultView) {
236                 return document.defaultView.getComputedStyle(el, null).getPropertyValue(prop);
237
238         } else {
239                 if (prop == 'opacity') prop = 'filter';
240                 var val = el.currentStyle[prop.replace(/\-(\w)/g, function (a, b){ return b.toUpperCase(); })];
241                 if (prop == 'filter')
242                         val = val.replace(/alpha\(opacity=([0-9]+)\)/,
243                                 function (a, b) { return b / 100 });
244                 return val === '' ? 1 : val;
245         }
246 },
247
248 getPageSize : function () {
249         var d = document, w = window, iebody = d.compatMode && d.compatMode != 'BackCompat'
250                 ? d.documentElement : d.body,
251                 ieLt9 = hs.ie && (hs.uaVersion < 9 || typeof pageXOffset == 'undefined');
252
253         var width = ieLt9 ? iebody.clientWidth :
254                         (d.documentElement.clientWidth || self.innerWidth),
255                 height = ieLt9 ? iebody.clientHeight : self.innerHeight;
256         hs.page = {
257                 width: width,
258                 height: height,
259                 scrollLeft: ieLt9 ? iebody.scrollLeft : pageXOffset,
260                 scrollTop: ieLt9 ? iebody.scrollTop : pageYOffset
261         };
262         return hs.page;
263 },
264
265 getPosition : function(el)      {
266         var p = { x: el.offsetLeft, y: el.offsetTop };
267         while (el.offsetParent) {
268                 el = el.offsetParent;
269                 p.x += el.offsetLeft;
270                 p.y += el.offsetTop;
271                 if (el != document.body && el != document.documentElement) {
272                         p.x -= el.scrollLeft;
273                         p.y -= el.scrollTop;
274                 }
275         }
276         return p;
277 },
278
279 expand : function(a, params, custom, type) {
280         if (!a) a = hs.createElement('a', null, { display: 'none' }, hs.container);
281         if (typeof a.getParams == 'function') return params;
282         try {
283                 new hs.Expander(a, params, custom);
284                 return false;
285         } catch (e) { return true; }
286 },
287 getElementByClass : function (el, tagName, className) {
288         var els = el.getElementsByTagName(tagName);
289         for (var i = 0; i < els.length; i++) {
290                 if ((new RegExp(className)).test(els[i].className)) {
291                         return els[i];
292                 }
293         }
294         return null;
295 },
296 replaceLang : function(s) {
297         s = s.replace(/\s/g, ' ');
298         var re = /{hs\.lang\.([^}]+)\}/g,
299                 matches = s.match(re),
300                 lang;
301         if (matches) for (var i = 0; i < matches.length; i++) {
302                 lang = matches[i].replace(re, "$1");
303                 if (typeof hs.lang[lang] != 'undefined') s = s.replace(matches[i], hs.lang[lang]);
304         }
305         return s;
306 },
307
308
309 focusTopmost : function() {
310         var topZ = 0,
311                 topmostKey = -1,
312                 expanders = hs.expanders,
313                 exp,
314                 zIndex;
315         for (var i = 0; i < expanders.length; i++) {
316                 exp = expanders[i];
317                 if (exp) {
318                         zIndex = exp.wrapper.style.zIndex;
319                         if (zIndex && zIndex > topZ) {
320                                 topZ = zIndex;
321                                 topmostKey = i;
322                         }
323                 }
324         }
325         if (topmostKey == -1) hs.focusKey = -1;
326         else expanders[topmostKey].focus();
327 },
328
329 getParam : function (a, param) {
330         a.getParams = a.onclick;
331         var p = a.getParams ? a.getParams() : null;
332         a.getParams = null;
333
334         return (p && typeof p[param] != 'undefined') ? p[param] :
335                 (typeof hs[param] != 'undefined' ? hs[param] : null);
336 },
337
338 getSrc : function (a) {
339         var src = hs.getParam(a, 'src');
340         if (src) return src;
341         return a.href;
342 },
343
344 getNode : function (id) {
345         var node = hs.$(id), clone = hs.clones[id], a = {};
346         if (!node && !clone) return null;
347         if (!clone) {
348                 clone = node.cloneNode(true);
349                 clone.id = '';
350                 hs.clones[id] = clone;
351                 return node;
352         } else {
353                 return clone.cloneNode(true);
354         }
355 },
356
357 discardElement : function(d) {
358         if (d) hs.garbageBin.appendChild(d);
359         hs.garbageBin.innerHTML = '';
360 },
361 dim : function(exp) {
362         if (!hs.dimmer) {
363                 isNew = true;
364                 hs.dimmer = hs.createElement ('div', {
365                                 className: 'highslide-dimming highslide-viewport-size',
366                                 owner: '',
367                                 onclick: function() {
368                                         if (hs.fireEvent(hs, 'onDimmerClick'))
369
370                                                 hs.close();
371                                 }
372                         }, {
373                                 visibility: 'visible',
374                                 opacity: 0
375                         }, hs.container, true);
376
377                 if (/(Android|iPad|iPhone|iPod)/.test(navigator.userAgent)) {
378                         var body = document.body;
379                         function pixDimmerSize() {
380                                 hs.setStyles(hs.dimmer, {
381                                         width: body.scrollWidth +'px',
382                                         height: body.scrollHeight +'px'
383                                 });
384                         }
385                         pixDimmerSize();
386                         hs.addEventListener(window, 'resize', pixDimmerSize);
387                 }
388         }
389         hs.dimmer.style.display = '';
390
391         var isNew = hs.dimmer.owner == '';
392         hs.dimmer.owner += '|'+ exp.key;
393
394         if (isNew) {
395                 if (hs.geckoMac && hs.dimmingGeckoFix)
396                         hs.setStyles(hs.dimmer, {
397                                 background: 'url('+ hs.graphicsDir + 'geckodimmer.png)',
398                                 opacity: 1
399                         });
400                 else
401                         hs.animate(hs.dimmer, { opacity: exp.dimmingOpacity }, hs.dimmingDuration);
402         }
403 },
404 undim : function(key) {
405         if (!hs.dimmer) return;
406         if (typeof key != 'undefined') hs.dimmer.owner = hs.dimmer.owner.replace('|'+ key, '');
407
408         if (
409                 (typeof key != 'undefined' && hs.dimmer.owner != '')
410                 || (hs.upcoming && hs.getParam(hs.upcoming, 'dimmingOpacity'))
411         ) return;
412
413         if (hs.geckoMac && hs.dimmingGeckoFix) hs.dimmer.style.display = 'none';
414         else hs.animate(hs.dimmer, { opacity: 0 }, hs.dimmingDuration, null, function() {
415                 hs.dimmer.style.display = 'none';
416         });
417 },
418 transit : function (adj, exp) {
419         var last = exp || hs.getExpander();
420         exp = last;
421         if (hs.upcoming) return false;
422         else hs.last = last;
423         hs.removeEventListener(document, window.opera ? 'keypress' : 'keydown', hs.keyHandler);
424         try {
425                 hs.upcoming = adj;
426                 adj.onclick();
427         } catch (e){
428                 hs.last = hs.upcoming = null;
429         }
430         try {
431                 if (!adj || exp.transitions[1] != 'crossfade')
432                 exp.close();
433         } catch (e) {}
434         return false;
435 },
436
437 previousOrNext : function (el, op) {
438         var exp = hs.getExpander(el);
439         if (exp) return hs.transit(exp.getAdjacentAnchor(op), exp);
440         else return false;
441 },
442
443 previous : function (el) {
444         return hs.previousOrNext(el, -1);
445 },
446
447 next : function (el) {
448         return hs.previousOrNext(el, 1);
449 },
450
451 keyHandler : function(e) {
452         if (!e) e = window.event;
453         if (!e.target) e.target = e.srcElement; // ie
454         if (typeof e.target.form != 'undefined') return true; // form element has focus
455         if (!hs.fireEvent(hs, 'onKeyDown', e)) return true;
456         var exp = hs.getExpander();
457
458         var op = null;
459         switch (e.keyCode) {
460                 case 70: // f
461                         if (exp) exp.doFullExpand();
462                         return true;
463                 case 32: // Space
464                         op = 2;
465                         break;
466                 case 34: // Page Down
467                 case 39: // Arrow right
468                 case 40: // Arrow down
469                         op = 1;
470                         break;
471                 case 8:  // Backspace
472                 case 33: // Page Up
473                 case 37: // Arrow left
474                 case 38: // Arrow up
475                         op = -1;
476                         break;
477                 case 27: // Escape
478                 case 13: // Enter
479                         op = 0;
480         }
481         if (op !== null) {if (op != 2)hs.removeEventListener(document, window.opera ? 'keypress' : 'keydown', hs.keyHandler);
482                 if (!hs.enableKeyListener) return true;
483
484                 if (e.preventDefault) e.preventDefault();
485                 else e.returnValue = false;
486                 if (exp) {
487                         if (op == 0) {
488                                 exp.close();
489                         } else if (op == 2) {
490                                 if (exp.slideshow) exp.slideshow.hitSpace();
491                         } else {
492                                 if (exp.slideshow) exp.slideshow.pause();
493                                 hs.previousOrNext(exp.key, op);
494                         }
495                         return false;
496                 }
497         }
498         return true;
499 },
500
501
502 registerOverlay : function (overlay) {
503         hs.push(hs.overlays, hs.extend(overlay, { hsId: 'hsId'+ hs.idCounter++ } ));
504 },
505
506
507 addSlideshow : function (options) {
508         var sg = options.slideshowGroup;
509         if (typeof sg == 'object') {
510                 for (var i = 0; i < sg.length; i++) {
511                         var o = {};
512                         for (var x in options) o[x] = options[x];
513                         o.slideshowGroup = sg[i];
514                         hs.push(hs.slideshows, o);
515                 }
516         } else {
517                 hs.push(hs.slideshows, options);
518         }
519 },
520
521 getWrapperKey : function (element, expOnly) {
522         var el, re = /^highslide-wrapper-([0-9]+)$/;
523         // 1. look in open expanders
524         el = element;
525         while (el.parentNode)   {
526                 if (el.hsKey !== undefined) return el.hsKey;
527                 if (el.id && re.test(el.id)) return el.id.replace(re, "$1");
528                 el = el.parentNode;
529         }
530         // 2. look in thumbnail
531         if (!expOnly) {
532                 el = element;
533                 while (el.parentNode)   {
534                         if (el.tagName && hs.isHsAnchor(el)) {
535                                 for (var key = 0; key < hs.expanders.length; key++) {
536                                         var exp = hs.expanders[key];
537                                         if (exp && exp.a == el) return key;
538                                 }
539                         }
540                         el = el.parentNode;
541                 }
542         }
543         return null;
544 },
545
546 getExpander : function (el, expOnly) {
547         if (typeof el == 'undefined') return hs.expanders[hs.focusKey] || null;
548         if (typeof el == 'number') return hs.expanders[el] || null;
549         if (typeof el == 'string') el = hs.$(el);
550         return hs.expanders[hs.getWrapperKey(el, expOnly)] || null;
551 },
552
553 isHsAnchor : function (a) {
554         return (a.onclick && a.onclick.toString().replace(/\s/g, ' ').match(/hs.(htmlE|e)xpand/));
555 },
556
557 reOrder : function () {
558         for (var i = 0; i < hs.expanders.length; i++)
559                 if (hs.expanders[i] && hs.expanders[i].isExpanded) hs.focusTopmost();
560 },
561 fireEvent : function (obj, evt, args) {
562         return obj && obj[evt] ? (obj[evt](obj, args) !== false) : true;
563 },
564
565 mouseClickHandler : function(e)
566 {
567         if (!e) e = window.event;
568         if (e.button > 1) return true;
569         if (!e.target) e.target = e.srcElement;
570
571         var el = e.target;
572         while (el.parentNode
573                 && !(/highslide-(image|move|html|resize)/.test(el.className)))
574         {
575                 el = el.parentNode;
576         }
577         var exp = hs.getExpander(el);
578         if (exp && (exp.isClosing || !exp.isExpanded)) return true;
579
580         if (exp && e.type == 'mousedown') {
581                 if (e.target.form) return true;
582                 var match = el.className.match(/highslide-(image|move|resize)/);
583                 if (match) {
584                         hs.dragArgs = {
585                                 exp: exp ,
586                                 type: match[1],
587                                 left: exp.x.pos,
588                                 width: exp.x.size,
589                                 top: exp.y.pos,
590                                 height: exp.y.size,
591                                 clickX: e.clientX,
592                                 clickY: e.clientY
593                         };
594
595
596                         hs.addEventListener(document, 'mousemove', hs.dragHandler);
597                         if (e.preventDefault) e.preventDefault(); // FF
598
599                         if (/highslide-(image|html)-blur/.test(exp.content.className)) {
600                                 exp.focus();
601                                 hs.hasFocused = true;
602                         }
603                         return false;
604                 }
605         } else if (e.type == 'mouseup') {
606
607                 hs.removeEventListener(document, 'mousemove', hs.dragHandler);
608
609                 if (hs.dragArgs) {
610                         if (hs.styleRestoreCursor && hs.dragArgs.type == 'image')
611                                 hs.dragArgs.exp.content.style.cursor = hs.styleRestoreCursor;
612                         var hasDragged = hs.dragArgs.hasDragged;
613
614                         if (!hasDragged &&!hs.hasFocused && !/(move|resize)/.test(hs.dragArgs.type)) {
615                                 if (hs.fireEvent(exp, 'onImageClick'))
616                                 exp.close();
617                         }
618                         else if (hasDragged || (!hasDragged && hs.hasHtmlExpanders)) {
619                                 hs.dragArgs.exp.doShowHide('hidden');
620                         }
621
622                         if (hasDragged) hs.fireEvent(hs.dragArgs.exp, 'onDrop', hs.dragArgs);
623                         hs.hasFocused = false;
624                         hs.dragArgs = null;
625
626                 } else if (/highslide-image-blur/.test(el.className)) {
627                         el.style.cursor = hs.styleRestoreCursor;
628                 }
629         }
630         return false;
631 },
632
633 dragHandler : function(e)
634 {
635         if (!hs.dragArgs) return true;
636         if (!e) e = window.event;
637         var a = hs.dragArgs, exp = a.exp;
638
639         a.dX = e.clientX - a.clickX;
640         a.dY = e.clientY - a.clickY;
641
642         var distance = Math.sqrt(Math.pow(a.dX, 2) + Math.pow(a.dY, 2));
643         if (!a.hasDragged) a.hasDragged = (a.type != 'image' && distance > 0)
644                 || (distance > (hs.dragSensitivity || 5));
645
646         if (a.hasDragged && e.clientX > 5 && e.clientY > 5) {
647                 if (!hs.fireEvent(exp, 'onDrag', a)) return false;
648
649                 if (a.type == 'resize') exp.resize(a);
650                 else {
651                         exp.moveTo(a.left + a.dX, a.top + a.dY);
652                         if (a.type == 'image') exp.content.style.cursor = 'move';
653                 }
654         }
655         return false;
656 },
657
658 wrapperMouseHandler : function (e) {
659         try {
660                 if (!e) e = window.event;
661                 var over = /mouseover/i.test(e.type);
662                 if (!e.target) e.target = e.srcElement; // ie
663                 if (!e.relatedTarget) e.relatedTarget =
664                         over ? e.fromElement : e.toElement; // ie
665                 var exp = hs.getExpander(e.target);
666                 if (!exp.isExpanded) return;
667                 if (!exp || !e.relatedTarget || hs.getExpander(e.relatedTarget, true) == exp
668                         || hs.dragArgs) return;
669                 hs.fireEvent(exp, over ? 'onMouseOver' : 'onMouseOut', e);
670                 for (var i = 0; i < exp.overlays.length; i++) (function() {
671                         var o = hs.$('hsId'+ exp.overlays[i]);
672                         if (o && o.hideOnMouseOut) {
673                                 if (over) hs.setStyles(o, { visibility: 'visible', display: '' });
674                                 hs.animate(o, { opacity: over ? o.opacity : 0 }, o.dur);
675                         }
676                 })();
677         } catch (e) {}
678 },
679 addEventListener : function (el, event, func) {
680         if (el == document && event == 'ready') {
681                 hs.push(hs.onReady, func);
682         }
683         try {
684                 el.addEventListener(event, func, false);
685         } catch (e) {
686                 try {
687                         el.detachEvent('on'+ event, func);
688                         el.attachEvent('on'+ event, func);
689                 } catch (e) {
690                         el['on'+ event] = func;
691                 }
692         }
693 },
694
695 removeEventListener : function (el, event, func) {
696         try {
697                 el.removeEventListener(event, func, false);
698         } catch (e) {
699                 try {
700                         el.detachEvent('on'+ event, func);
701                 } catch (e) {
702                         el['on'+ event] = null;
703                 }
704         }
705 },
706
707 preloadFullImage : function (i) {
708         if (hs.continuePreloading && hs.preloadTheseImages[i] && hs.preloadTheseImages[i] != 'undefined') {
709                 var img = document.createElement('img');
710                 img.onload = function() {
711                         img = null;
712                         hs.preloadFullImage(i + 1);
713                 };
714                 img.src = hs.preloadTheseImages[i];
715         }
716 },
717 preloadImages : function (number) {
718         if (number && typeof number != 'object') hs.numberOfImagesToPreload = number;
719
720         var arr = hs.getAnchors();
721         for (var i = 0; i < arr.images.length && i < hs.numberOfImagesToPreload; i++) {
722                 hs.push(hs.preloadTheseImages, hs.getSrc(arr.images[i]));
723         }
724
725         // preload outlines
726         if (hs.outlineType)     new hs.Outline(hs.outlineType, function () { hs.preloadFullImage(0)} );
727         else
728
729         hs.preloadFullImage(0);
730
731         // preload cursor
732         if (hs.restoreCursor) var cur = hs.createElement('img', { src: hs.graphicsDir + hs.restoreCursor });
733 },
734
735
736 init : function () {
737         if (!hs.container) {
738
739                 hs.ieLt7 = hs.ie && hs.uaVersion < 7;
740                 hs.ieLt9 = hs.ie && hs.uaVersion < 9;
741
742                 hs.getPageSize();
743                 for (var x in hs.langDefaults) {
744                         if (typeof hs[x] != 'undefined') hs.lang[x] = hs[x];
745                         else if (typeof hs.lang[x] == 'undefined' && typeof hs.langDefaults[x] != 'undefined')
746                                 hs.lang[x] = hs.langDefaults[x];
747                 }
748
749                 hs.container = hs.createElement('div', {
750                                 className: 'highslide-container'
751                         }, {
752                                 position: 'absolute',
753                                 left: 0,
754                                 top: 0,
755                                 width: '100%',
756                                 zIndex: hs.zIndexCounter,
757                                 direction: 'ltr'
758                         },
759                         document.body,
760                         true
761                 );
762                 hs.loading = hs.createElement('a', {
763                                 className: 'highslide-loading',
764                                 title: hs.lang.loadingTitle,
765                                 innerHTML: hs.lang.loadingText,
766                                 href: 'javascript:;'
767                         }, {
768                                 position: 'absolute',
769                                 top: '-9999px',
770                                 opacity: hs.loadingOpacity,
771                                 zIndex: 1
772                         }, hs.container
773                 );
774                 hs.garbageBin = hs.createElement('div', null, { display: 'none' }, hs.container);
775                 hs.viewport = hs.createElement('div', {
776                                 className: 'highslide-viewport highslide-viewport-size'
777                         }, {
778                                 visibility: (hs.safari && hs.uaVersion < 525) ? 'visible' : 'hidden'
779                         }, hs.container, 1
780                 );
781
782                 // http://www.robertpenner.com/easing/
783                 Math.linearTween = function (t, b, c, d) {
784                         return c*t/d + b;
785                 };
786                 Math.easeInQuad = function (t, b, c, d) {
787                         return c*(t/=d)*t + b;
788                 };
789                 Math.easeOutQuad = function (t, b, c, d) {
790                         return -c *(t/=d)*(t-2) + b;
791                 };
792
793                 hs.hideSelects = hs.ieLt7;
794                 hs.hideIframes = ((window.opera && hs.uaVersion < 9) || navigator.vendor == 'KDE'
795                         || (hs.ieLt7 && hs.uaVersion < 5.5));
796                 hs.fireEvent(this, 'onActivate');
797         }
798 },
799 ready : function() {
800         if (hs.isReady) return;
801         hs.isReady = true;
802         for (var i = 0; i < hs.onReady.length; i++) hs.onReady[i]();
803 },
804
805 updateAnchors : function() {
806         var el, els, all = [], images = [],groups = {}, re;
807
808         for (var i = 0; i < hs.openerTagNames.length; i++) {
809                 els = document.getElementsByTagName(hs.openerTagNames[i]);
810                 for (var j = 0; j < els.length; j++) {
811                         el = els[j];
812                         re = hs.isHsAnchor(el);
813                         if (re) {
814                                 hs.push(all, el);
815                                 if (re[0] == 'hs.expand') hs.push(images, el);
816                                 var g = hs.getParam(el, 'slideshowGroup') || 'none';
817                                 if (!groups[g]) groups[g] = [];
818                                 hs.push(groups[g], el);
819                         }
820                 }
821         }
822         hs.anchors = { all: all, groups: groups, images: images };
823         return hs.anchors;
824
825 },
826
827 getAnchors : function() {
828         return hs.anchors || hs.updateAnchors();
829 },
830
831
832 close : function(el) {
833         var exp = hs.getExpander(el);
834         if (exp) exp.close();
835         return false;
836 }
837 }; // end hs object
838 hs.fx = function( elem, options, prop ){
839         this.options = options;
840         this.elem = elem;
841         this.prop = prop;
842
843         if (!options.orig) options.orig = {};
844 };
845 hs.fx.prototype = {
846         update: function(){
847                 (hs.fx.step[this.prop] || hs.fx.step._default)(this);
848
849                 if (this.options.step)
850                         this.options.step.call(this.elem, this.now, this);
851
852         },
853         custom: function(from, to, unit){
854                 this.startTime = (new Date()).getTime();
855                 this.start = from;
856                 this.end = to;
857                 this.unit = unit;// || this.unit || "px";
858                 this.now = this.start;
859                 this.pos = this.state = 0;
860
861                 var self = this;
862                 function t(gotoEnd){
863                         return self.step(gotoEnd);
864                 }
865
866                 t.elem = this.elem;
867
868                 if ( t() && hs.timers.push(t) == 1 ) {
869                         hs.timerId = setInterval(function(){
870                                 var timers = hs.timers;
871
872                                 for ( var i = 0; i < timers.length; i++ )
873                                         if ( !timers[i]() )
874                                                 timers.splice(i--, 1);
875
876                                 if ( !timers.length ) {
877                                         clearInterval(hs.timerId);
878                                 }
879                         }, 13);
880                 }
881         },
882         step: function(gotoEnd){
883                 var t = (new Date()).getTime();
884                 if ( gotoEnd || t >= this.options.duration + this.startTime ) {
885                         this.now = this.end;
886                         this.pos = this.state = 1;
887                         this.update();
888
889                         this.options.curAnim[ this.prop ] = true;
890
891                         var done = true;
892                         for ( var i in this.options.curAnim )
893                                 if ( this.options.curAnim[i] !== true )
894                                         done = false;
895
896                         if ( done ) {
897                                 if (this.options.complete) this.options.complete.call(this.elem);
898                         }
899                         return false;
900                 } else {
901                         var n = t - this.startTime;
902                         this.state = n / this.options.duration;
903                         this.pos = this.options.easing(n, 0, 1, this.options.duration);
904                         this.now = this.start + ((this.end - this.start) * this.pos);
905                         this.update();
906                 }
907                 return true;
908         }
909
910 };
911
912 hs.extend( hs.fx, {
913         step: {
914
915                 opacity: function(fx){
916                         hs.setStyles(fx.elem, { opacity: fx.now });
917                 },
918
919                 _default: function(fx){
920                         try {
921                                 if ( fx.elem.style && fx.elem.style[ fx.prop ] != null )
922                                         fx.elem.style[ fx.prop ] = fx.now + fx.unit;
923                                 else
924                                         fx.elem[ fx.prop ] = fx.now;
925                         } catch (e) {}
926                 }
927         }
928 });
929
930 hs.Outline =  function (outlineType, onLoad) {
931         this.onLoad = onLoad;
932         this.outlineType = outlineType;
933         var v = hs.uaVersion, tr;
934
935         this.hasAlphaImageLoader = hs.ie && hs.uaVersion < 7;
936         if (!outlineType) {
937                 if (onLoad) onLoad();
938                 return;
939         }
940
941         hs.init();
942         this.table = hs.createElement(
943                 'table', {
944                         cellSpacing: 0
945                 }, {
946                         visibility: 'hidden',
947                         position: 'absolute',
948                         borderCollapse: 'collapse',
949                         width: 0
950                 },
951                 hs.container,
952                 true
953         );
954         var tbody = hs.createElement('tbody', null, null, this.table, 1);
955
956         this.td = [];
957         for (var i = 0; i <= 8; i++) {
958                 if (i % 3 == 0) tr = hs.createElement('tr', null, { height: 'auto' }, tbody, true);
959                 this.td[i] = hs.createElement('td', null, null, tr, true);
960                 var style = i != 4 ? { lineHeight: 0, fontSize: 0} : { position : 'relative' };
961                 hs.setStyles(this.td[i], style);
962         }
963         this.td[4].className = outlineType +' highslide-outline';
964
965         this.preloadGraphic();
966 };
967
968 hs.Outline.prototype = {
969 preloadGraphic : function () {
970         var src = hs.graphicsDir + (hs.outlinesDir || "outlines/")+ this.outlineType +".png";
971
972         var appendTo = hs.safari && hs.uaVersion < 525 ? hs.container : null;
973         this.graphic = hs.createElement('img', null, { position: 'absolute',
974                 top: '-9999px' }, appendTo, true); // for onload trigger
975
976         var pThis = this;
977         this.graphic.onload = function() { pThis.onGraphicLoad(); };
978
979         this.graphic.src = src;
980 },
981
982 onGraphicLoad : function () {
983         var o = this.offset = this.graphic.width / 4,
984                 pos = [[0,0],[0,-4],[-2,0],[0,-8],0,[-2,-8],[0,-2],[0,-6],[-2,-2]],
985                 dim = { height: (2*o) +'px', width: (2*o) +'px' };
986         for (var i = 0; i <= 8; i++) {
987                 if (pos[i]) {
988                         if (this.hasAlphaImageLoader) {
989                                 var w = (i == 1 || i == 7) ? '100%' : this.graphic.width +'px';
990                                 var div = hs.createElement('div', null, { width: '100%', height: '100%', position: 'relative', overflow: 'hidden'}, this.td[i], true);
991                                 hs.createElement ('div', null, {
992                                                 filter: "progid:DXImageTransform.Microsoft.AlphaImageLoader(sizingMethod=scale, src='"+ this.graphic.src + "')",
993                                                 position: 'absolute',
994                                                 width: w,
995                                                 height: this.graphic.height +'px',
996                                                 left: (pos[i][0]*o)+'px',
997                                                 top: (pos[i][1]*o)+'px'
998                                         },
999                                 div,
1000                                 true);
1001                         } else {
1002                                 hs.setStyles(this.td[i], { background: 'url('+ this.graphic.src +') '+ (pos[i][0]*o)+'px '+(pos[i][1]*o)+'px'});
1003                         }
1004
1005                         if (window.opera && (i == 3 || i ==5))
1006                                 hs.createElement('div', null, dim, this.td[i], true);
1007
1008                         hs.setStyles (this.td[i], dim);
1009                 }
1010         }
1011         this.graphic = null;
1012         if (hs.pendingOutlines[this.outlineType]) hs.pendingOutlines[this.outlineType].destroy();
1013         hs.pendingOutlines[this.outlineType] = this;
1014         if (this.onLoad) this.onLoad();
1015 },
1016
1017 setPosition : function (pos, offset, vis, dur, easing) {
1018         var exp = this.exp,
1019                 stl = exp.wrapper.style,
1020                 offset = offset || 0,
1021                 pos = pos || {
1022                         x: exp.x.pos + offset,
1023                         y: exp.y.pos + offset,
1024                         w: exp.x.get('wsize') - 2 * offset,
1025                         h: exp.y.get('wsize') - 2 * offset
1026                 };
1027         if (vis) this.table.style.visibility = (pos.h >= 4 * this.offset)
1028                 ? 'visible' : 'hidden';
1029         hs.setStyles(this.table, {
1030                 left: (pos.x - this.offset) +'px',
1031                 top: (pos.y - this.offset) +'px',
1032                 width: (pos.w + 2 * this.offset) +'px'
1033         });
1034
1035         pos.w -= 2 * this.offset;
1036         pos.h -= 2 * this.offset;
1037         hs.setStyles (this.td[4], {
1038                 width: pos.w >= 0 ? pos.w +'px' : 0,
1039                 height: pos.h >= 0 ? pos.h +'px' : 0
1040         });
1041         if (this.hasAlphaImageLoader) this.td[3].style.height
1042                 = this.td[5].style.height = this.td[4].style.height;
1043
1044 },
1045
1046 destroy : function(hide) {
1047         if (hide) this.table.style.visibility = 'hidden';
1048         else hs.discardElement(this.table);
1049 }
1050 };
1051
1052 hs.Dimension = function(exp, dim) {
1053         this.exp = exp;
1054         this.dim = dim;
1055         this.ucwh = dim == 'x' ? 'Width' : 'Height';
1056         this.wh = this.ucwh.toLowerCase();
1057         this.uclt = dim == 'x' ? 'Left' : 'Top';
1058         this.lt = this.uclt.toLowerCase();
1059         this.ucrb = dim == 'x' ? 'Right' : 'Bottom';
1060         this.rb = this.ucrb.toLowerCase();
1061         this.p1 = this.p2 = 0;
1062 };
1063 hs.Dimension.prototype = {
1064 get : function(key) {
1065         switch (key) {
1066                 case 'loadingPos':
1067                         return this.tpos + this.tb + (this.t - hs.loading['offset'+ this.ucwh]) / 2;
1068                 case 'loadingPosXfade':
1069                         return this.pos + this.cb+ this.p1 + (this.size - hs.loading['offset'+ this.ucwh]) / 2;
1070                 case 'wsize':
1071                         return this.size + 2 * this.cb + this.p1 + this.p2;
1072                 case 'fitsize':
1073                         return this.clientSize - this.marginMin - this.marginMax;
1074                 case 'maxsize':
1075                         return this.get('fitsize') - 2 * this.cb - this.p1 - this.p2 ;
1076                 case 'opos':
1077                         return this.pos - (this.exp.outline ? this.exp.outline.offset : 0);
1078                 case 'osize':
1079                         return this.get('wsize') + (this.exp.outline ? 2*this.exp.outline.offset : 0);
1080                 case 'imgPad':
1081                         return this.imgSize ? Math.round((this.size - this.imgSize) / 2) : 0;
1082
1083         }
1084 },
1085 calcBorders: function() {
1086         // correct for borders
1087         this.cb = (this.exp.content['offset'+ this.ucwh] - this.t) / 2;
1088
1089         this.marginMax = hs['margin'+ this.ucrb];
1090 },
1091 calcThumb: function() {
1092         this.t = this.exp.el[this.wh] ? parseInt(this.exp.el[this.wh]) :
1093                 this.exp.el['offset'+ this.ucwh];
1094         this.tpos = this.exp.tpos[this.dim];
1095         this.tb = (this.exp.el['offset'+ this.ucwh] - this.t) / 2;
1096         if (this.tpos == 0 || this.tpos == -1) {
1097                 this.tpos = (hs.page[this.wh] / 2) + hs.page['scroll'+ this.uclt];
1098         };
1099 },
1100 calcExpanded: function() {
1101         var exp = this.exp;
1102         this.justify = 'auto';
1103
1104         // get alignment
1105         if (exp.align == 'center') this.justify = 'center';
1106         else if (new RegExp(this.lt).test(exp.anchor)) this.justify = null;
1107         else if (new RegExp(this.rb).test(exp.anchor)) this.justify = 'max';
1108
1109
1110         // size and position
1111         this.pos = this.tpos - this.cb + this.tb;
1112
1113         if (this.maxHeight && this.dim == 'x')
1114                 exp.maxWidth = Math.min(exp.maxWidth || this.full, exp.maxHeight * this.full / exp.y.full);
1115
1116         this.size = Math.min(this.full, exp['max'+ this.ucwh] || this.full);
1117         this.minSize = exp.allowSizeReduction ?
1118                 Math.min(exp['min'+ this.ucwh], this.full) :this.full;
1119         if (exp.isImage && exp.useBox)  {
1120                 this.size = exp[this.wh];
1121                 this.imgSize = this.full;
1122         }
1123         if (this.dim == 'x' && hs.padToMinWidth) this.minSize = exp.minWidth;
1124         this.target = exp['target'+ this.dim.toUpperCase()];
1125         this.marginMin = hs['margin'+ this.uclt];
1126         this.scroll = hs.page['scroll'+ this.uclt];
1127         this.clientSize = hs.page[this.wh];
1128 },
1129 setSize: function(i) {
1130         var exp = this.exp;
1131         if (exp.isImage && (exp.useBox || hs.padToMinWidth)) {
1132                 this.imgSize = i;
1133                 this.size = Math.max(this.size, this.imgSize);
1134                 exp.content.style[this.lt] = this.get('imgPad')+'px';
1135         } else
1136         this.size = i;
1137
1138         exp.content.style[this.wh] = i +'px';
1139         exp.wrapper.style[this.wh] = this.get('wsize') +'px';
1140         if (exp.outline) exp.outline.setPosition();
1141         if (this.dim == 'x' && exp.overlayBox) exp.sizeOverlayBox(true);
1142         if (this.dim == 'x' && exp.slideshow && exp.isImage) {
1143                 if (i == this.full) exp.slideshow.disable('full-expand');
1144                 else exp.slideshow.enable('full-expand');
1145         }
1146 },
1147 setPos: function(i) {
1148         this.pos = i;
1149         this.exp.wrapper.style[this.lt] = i +'px';
1150
1151         if (this.exp.outline) this.exp.outline.setPosition();
1152
1153 }
1154 };
1155
1156 hs.Expander = function(a, params, custom, contentType) {
1157         if (document.readyState && hs.ie && !hs.isReady) {
1158                 hs.addEventListener(document, 'ready', function() {
1159                         new hs.Expander(a, params, custom, contentType);
1160                 });
1161                 return;
1162         }
1163         this.a = a;
1164         this.custom = custom;
1165         this.contentType = contentType || 'image';
1166         this.isImage = !this.isHtml;
1167
1168         hs.continuePreloading = false;
1169         this.overlays = [];
1170         this.last = hs.last;
1171         hs.last = null;
1172         hs.init();
1173         var key = this.key = hs.expanders.length;
1174         // override inline parameters
1175         for (var i = 0; i < hs.overrides.length; i++) {
1176                 var name = hs.overrides[i];
1177                 this[name] = params && typeof params[name] != 'undefined' ?
1178                         params[name] : hs[name];
1179         }
1180         if (!this.src) this.src = a.href;
1181
1182         // get thumb
1183         var el = (params && params.thumbnailId) ? hs.$(params.thumbnailId) : a;
1184         el = this.thumb = el.getElementsByTagName('img')[0] || el;
1185         this.thumbsUserSetId = el.id || a.id;
1186         if (!hs.fireEvent(this, 'onInit')) return true;
1187
1188         // check if already open
1189         for (var i = 0; i < hs.expanders.length; i++) {
1190                 if (hs.expanders[i] && hs.expanders[i].a == a
1191                         && !(this.last && this.transitions[1] == 'crossfade')) {
1192                         hs.expanders[i].focus();
1193                         return false;
1194                 }
1195         }
1196
1197         // cancel other
1198         if (!hs.allowSimultaneousLoading) for (var i = 0; i < hs.expanders.length; i++) {
1199                 if (hs.expanders[i] && hs.expanders[i].thumb != el && !hs.expanders[i].onLoadStarted) {
1200                         hs.expanders[i].cancelLoading();
1201                 }
1202         }
1203         hs.expanders[key] = this;
1204         if (!hs.allowMultipleInstances && !hs.upcoming) {
1205                 if (hs.expanders[key-1]) hs.expanders[key-1].close();
1206                 if (typeof hs.focusKey != 'undefined' && hs.expanders[hs.focusKey])
1207                         hs.expanders[hs.focusKey].close();
1208         }
1209
1210         // initiate metrics
1211         this.el = el;
1212         this.tpos = this.pageOrigin || hs.getPosition(el);
1213         hs.getPageSize();
1214         var x = this.x = new hs.Dimension(this, 'x');
1215         x.calcThumb();
1216         var y = this.y = new hs.Dimension(this, 'y');
1217         y.calcThumb();
1218         this.wrapper = hs.createElement(
1219                 'div', {
1220                         id: 'highslide-wrapper-'+ this.key,
1221                         className: 'highslide-wrapper '+ this.wrapperClassName
1222                 }, {
1223                         visibility: 'hidden',
1224                         position: 'absolute',
1225                         zIndex: hs.zIndexCounter += 2
1226                 }, null, true );
1227
1228         this.wrapper.onmouseover = this.wrapper.onmouseout = hs.wrapperMouseHandler;
1229         if (this.contentType == 'image' && this.outlineWhileAnimating == 2)
1230                 this.outlineWhileAnimating = 0;
1231
1232         // get the outline
1233         if (!this.outlineType
1234                 || (this.last && this.isImage && this.transitions[1] == 'crossfade')) {
1235                 this[this.contentType +'Create']();
1236
1237         } else if (hs.pendingOutlines[this.outlineType]) {
1238                 this.connectOutline();
1239                 this[this.contentType +'Create']();
1240
1241         } else {
1242                 this.showLoading();
1243                 var exp = this;
1244                 new hs.Outline(this.outlineType,
1245                         function () {
1246                                 exp.connectOutline();
1247                                 exp[exp.contentType +'Create']();
1248                         }
1249                 );
1250         }
1251         return true;
1252 };
1253
1254 hs.Expander.prototype = {
1255 error : function(e) {
1256         if (hs.debug) alert ('Line '+ e.lineNumber +': '+ e.message);
1257         else window.location.href = this.src;
1258 },
1259
1260 connectOutline : function() {
1261         var outline = this.outline = hs.pendingOutlines[this.outlineType];
1262         outline.exp = this;
1263         outline.table.style.zIndex = this.wrapper.style.zIndex - 1;
1264         hs.pendingOutlines[this.outlineType] = null;
1265 },
1266
1267 showLoading : function() {
1268         if (this.onLoadStarted || this.loading) return;
1269
1270         this.loading = hs.loading;
1271         var exp = this;
1272         this.loading.onclick = function() {
1273                 exp.cancelLoading();
1274         };
1275
1276
1277         if (!hs.fireEvent(this, 'onShowLoading')) return;
1278         var exp = this,
1279                 l = this.x.get('loadingPos') +'px',
1280                 t = this.y.get('loadingPos') +'px';
1281         if (!tgt && this.last && this.transitions[1] == 'crossfade')
1282                 var tgt = this.last;
1283         if (tgt) {
1284                 l = tgt.x.get('loadingPosXfade') +'px';
1285                 t = tgt.y.get('loadingPosXfade') +'px';
1286                 this.loading.style.zIndex = hs.zIndexCounter++;
1287         }
1288         setTimeout(function () {
1289                 if (exp.loading) hs.setStyles(exp.loading, { left: l, top: t, zIndex: hs.zIndexCounter++ })}
1290         , 100);
1291 },
1292
1293 imageCreate : function() {
1294         var exp = this;
1295
1296         var img = document.createElement('img');
1297         this.content = img;
1298         img.onload = function () {
1299                 if (hs.expanders[exp.key]) exp.contentLoaded();
1300         };
1301         if (hs.blockRightClick) img.oncontextmenu = function() { return false; };
1302         img.className = 'highslide-image';
1303         hs.setStyles(img, {
1304                 visibility: 'hidden',
1305                 display: 'block',
1306                 position: 'absolute',
1307                 maxWidth: '9999px',
1308                 zIndex: 3
1309         });
1310         img.title = hs.lang.restoreTitle;
1311         if (hs.safari && hs.uaVersion < 525) hs.container.appendChild(img);
1312         if (hs.ie && hs.flushImgSize) img.src = null;
1313         img.src = this.src;
1314
1315         this.showLoading();
1316 },
1317
1318 contentLoaded : function() {
1319         try {
1320                 if (!this.content) return;
1321                 this.content.onload = null;
1322                 if (this.onLoadStarted) return;
1323                 else this.onLoadStarted = true;
1324
1325                 var x = this.x, y = this.y;
1326
1327                 if (this.loading) {
1328                         hs.setStyles(this.loading, { top: '-9999px' });
1329                         this.loading = null;
1330                         hs.fireEvent(this, 'onHideLoading');
1331                 }
1332                         x.full = this.content.width;
1333                         y.full = this.content.height;
1334
1335                         hs.setStyles(this.content, {
1336                                 width: x.t +'px',
1337                                 height: y.t +'px'
1338                         });
1339                         this.wrapper.appendChild(this.content);
1340                         hs.container.appendChild(this.wrapper);
1341
1342                 x.calcBorders();
1343                 y.calcBorders();
1344
1345                 hs.setStyles (this.wrapper, {
1346                         left: (x.tpos + x.tb - x.cb) +'px',
1347                         top: (y.tpos + x.tb - y.cb) +'px'
1348                 });
1349
1350
1351                 this.initSlideshow();
1352                 this.getOverlays();
1353
1354                 var ratio = x.full / y.full;
1355                 x.calcExpanded();
1356                 this.justify(x);
1357
1358                 y.calcExpanded();
1359                 this.justify(y);
1360                 if (this.overlayBox) this.sizeOverlayBox(0, 1);
1361
1362
1363                 if (this.allowSizeReduction) {
1364                                 this.correctRatio(ratio);
1365                         var ss = this.slideshow;
1366                         if (ss && this.last && ss.controls && ss.fixedControls) {
1367                                 var pos = ss.overlayOptions.position || '', p;
1368                                 for (var dim in hs.oPos) for (var i = 0; i < 5; i++) {
1369                                         p = this[dim];
1370                                         if (pos.match(hs.oPos[dim][i])) {
1371                                                 p.pos = this.last[dim].pos
1372                                                         + (this.last[dim].p1 - p.p1)
1373                                                         + (this.last[dim].size - p.size) * [0, 0, .5, 1, 1][i];
1374                                                 if (ss.fixedControls == 'fit') {
1375                                                         if (p.pos + p.size + p.p1 + p.p2 > p.scroll + p.clientSize - p.marginMax)
1376                                                                 p.pos = p.scroll + p.clientSize - p.size - p.marginMin - p.marginMax - p.p1 - p.p2;
1377                                                         if (p.pos < p.scroll + p.marginMin) p.pos = p.scroll + p.marginMin;
1378                                                 }
1379                                         }
1380                                 }
1381                         }
1382                         if (this.isImage && this.x.full > (this.x.imgSize || this.x.size)) {
1383                                 this.createFullExpand();
1384                                 if (this.overlays.length == 1) this.sizeOverlayBox();
1385                         }
1386                 }
1387                 this.show();
1388
1389         } catch (e) {
1390                 this.error(e);
1391         }
1392 },
1393
1394 justify : function (p, moveOnly) {
1395         var tgtArr, tgt = p.target, dim = p == this.x ? 'x' : 'y';
1396
1397         if (tgt && tgt.match(/ /)) {
1398                 tgtArr = tgt.split(' ');
1399                 tgt = tgtArr[0];
1400         }
1401         if (tgt && hs.$(tgt)) {
1402                 p.pos = hs.getPosition(hs.$(tgt))[dim];
1403                 if (tgtArr && tgtArr[1] && tgtArr[1].match(/^[-]?[0-9]+px$/))
1404                         p.pos += parseInt(tgtArr[1]);
1405                 if (p.size < p.minSize) p.size = p.minSize;
1406
1407         } else if (p.justify == 'auto' || p.justify == 'center') {
1408
1409                 var hasMovedMin = false;
1410
1411                 var allowReduce = p.exp.allowSizeReduction;
1412                 if (p.justify == 'center')
1413                         p.pos = Math.round(p.scroll + (p.clientSize + p.marginMin - p.marginMax - p.get('wsize')) / 2);
1414                 else
1415                         p.pos = Math.round(p.pos - ((p.get('wsize') - p.t) / 2));
1416                 if (p.pos < p.scroll + p.marginMin) {
1417                         p.pos = p.scroll + p.marginMin;
1418                         hasMovedMin = true;
1419                 }
1420                 if (!moveOnly && p.size < p.minSize) {
1421                         p.size = p.minSize;
1422                         allowReduce = false;
1423                 }
1424                 if (p.pos + p.get('wsize') > p.scroll + p.clientSize - p.marginMax) {
1425                         if (!moveOnly && hasMovedMin && allowReduce) {
1426                                 p.size = Math.min(p.size, p.get(dim == 'y' ? 'fitsize' : 'maxsize'));
1427                         } else if (p.get('wsize') < p.get('fitsize')) {
1428                                 p.pos = p.scroll + p.clientSize - p.marginMax - p.get('wsize');
1429                         } else { // image larger than viewport
1430                                 p.pos = p.scroll + p.marginMin;
1431                                 if (!moveOnly && allowReduce) p.size = p.get(dim == 'y' ? 'fitsize' : 'maxsize');
1432                         }
1433                 }
1434
1435                 if (!moveOnly && p.size < p.minSize) {
1436                         p.size = p.minSize;
1437                         allowReduce = false;
1438                 }
1439
1440
1441         } else if (p.justify == 'max') {
1442                 p.pos = Math.floor(p.pos - p.size + p.t);
1443         }
1444
1445
1446         if (p.pos < p.marginMin) {
1447                 var tmpMin = p.pos;
1448                 p.pos = p.marginMin;
1449
1450                 if (allowReduce && !moveOnly) p.size = p.size - (p.pos - tmpMin);
1451
1452         }
1453 },
1454
1455 correctRatio : function(ratio) {
1456         var x = this.x,
1457                 y = this.y,
1458                 changed = false,
1459                 xSize = Math.min(x.full, x.size),
1460                 ySize = Math.min(y.full, y.size),
1461                 useBox = (this.useBox || hs.padToMinWidth);
1462
1463         if (xSize / ySize > ratio) { // width greater
1464                 xSize = ySize * ratio;
1465                 if (xSize < x.minSize) { // below minWidth
1466                         xSize = x.minSize;
1467                         ySize = xSize / ratio;
1468                 }
1469                 changed = true;
1470
1471         } else if (xSize / ySize < ratio) { // height greater
1472                 ySize = xSize / ratio;
1473                 changed = true;
1474         }
1475
1476         if (hs.padToMinWidth && x.full < x.minSize) {
1477                 x.imgSize = x.full;
1478                 y.size = y.imgSize = y.full;
1479         } else if (this.useBox) {
1480                 x.imgSize = xSize;
1481                 y.imgSize = ySize;
1482         } else {
1483                 x.size = xSize;
1484                 y.size = ySize;
1485         }
1486         changed = this.fitOverlayBox(this.useBox ? null : ratio, changed);
1487         if (useBox && y.size < y.imgSize) {
1488                 y.imgSize = y.size;
1489                 x.imgSize = y.size * ratio;
1490         }
1491         if (changed || useBox) {
1492                 x.pos = x.tpos - x.cb + x.tb;
1493                 x.minSize = x.size;
1494                 this.justify(x, true);
1495
1496                 y.pos = y.tpos - y.cb + y.tb;
1497                 y.minSize = y.size;
1498                 this.justify(y, true);
1499                 if (this.overlayBox) this.sizeOverlayBox();
1500         }
1501
1502
1503 },
1504 fitOverlayBox : function(ratio, changed) {
1505         var x = this.x, y = this.y;
1506         if (this.overlayBox) {
1507                 while (y.size > this.minHeight && x.size > this.minWidth
1508                                 &&  y.get('wsize') > y.get('fitsize')) {
1509                         y.size -= 10;
1510                         if (ratio) x.size = y.size * ratio;
1511                         this.sizeOverlayBox(0, 1);
1512                         changed = true;
1513                 }
1514         }
1515         return changed;
1516 },
1517
1518 show : function () {
1519         var x = this.x, y = this.y;
1520         this.doShowHide('hidden');
1521         hs.fireEvent(this, 'onBeforeExpand');
1522         if (this.slideshow && this.slideshow.thumbstrip) this.slideshow.thumbstrip.selectThumb();
1523
1524         // Apply size change
1525         this.changeSize(
1526                 1, {
1527                         wrapper: {
1528                                 width : x.get('wsize'),
1529                                 height : y.get('wsize'),
1530                                 left: x.pos,
1531                                 top: y.pos
1532                         },
1533                         content: {
1534                                 left: x.p1 + x.get('imgPad'),
1535                                 top: y.p1 + y.get('imgPad'),
1536                                 width:x.imgSize ||x.size,
1537                                 height:y.imgSize ||y.size
1538                         }
1539                 },
1540                 hs.expandDuration
1541         );
1542 },
1543
1544 changeSize : function(up, to, dur) {
1545         // transition
1546         var trans = this.transitions,
1547         other = up ? (this.last ? this.last.a : null) : hs.upcoming,
1548         t = (trans[1] && other
1549                         && hs.getParam(other, 'transitions')[1] == trans[1]) ?
1550                 trans[1] : trans[0];
1551
1552         if (this[t] && t != 'expand') {
1553                 this[t](up, to);
1554                 return;
1555         }
1556
1557         if (this.outline && !this.outlineWhileAnimating) {
1558                 if (up) this.outline.setPosition();
1559                 else this.outline.destroy();
1560         }
1561
1562
1563         if (!up) this.destroyOverlays();
1564
1565         var exp = this,
1566                 x = exp.x,
1567                 y = exp.y,
1568                 easing = this.easing;
1569         if (!up) easing = this.easingClose || easing;
1570         var after = up ?
1571                 function() {
1572
1573                         if (exp.outline) exp.outline.table.style.visibility = "visible";
1574                         setTimeout(function() {
1575                                 exp.afterExpand();
1576                         }, 50);
1577                 } :
1578                 function() {
1579                         exp.afterClose();
1580                 };
1581         if (up) hs.setStyles( this.wrapper, {
1582                 width: x.t +'px',
1583                 height: y.t +'px'
1584         });
1585         if (this.fadeInOut) {
1586                 hs.setStyles(this.wrapper, { opacity: up ? 0 : 1 });
1587                 hs.extend(to.wrapper, { opacity: up });
1588         }
1589         hs.animate( this.wrapper, to.wrapper, {
1590                 duration: dur,
1591                 easing: easing,
1592                 step: function(val, args) {
1593                         if (exp.outline && exp.outlineWhileAnimating && args.prop == 'top') {
1594                                 var fac = up ? args.pos : 1 - args.pos;
1595                                 var pos = {
1596                                         w: x.t + (x.get('wsize') - x.t) * fac,
1597                                         h: y.t + (y.get('wsize') - y.t) * fac,
1598                                         x: x.tpos + (x.pos - x.tpos) * fac,
1599                                         y: y.tpos + (y.pos - y.tpos) * fac
1600                                 };
1601                                 exp.outline.setPosition(pos, 0, 1);
1602                         }
1603                 }
1604         });
1605         hs.animate( this.content, to.content, dur, easing, after);
1606         if (up) {
1607                 this.wrapper.style.visibility = 'visible';
1608                 this.content.style.visibility = 'visible';
1609                 this.a.className += ' highslide-active-anchor';
1610         }
1611 },
1612
1613
1614
1615 fade : function(up, to) {
1616         this.outlineWhileAnimating = false;
1617         var exp = this, t = up ? hs.expandDuration : 0;
1618
1619         if (up) {
1620                 hs.animate(this.wrapper, to.wrapper, 0);
1621                 hs.setStyles(this.wrapper, { opacity: 0, visibility: 'visible' });
1622                 hs.animate(this.content, to.content, 0);
1623                 this.content.style.visibility = 'visible';
1624
1625                 hs.animate(this.wrapper, { opacity: 1 }, t, null,
1626                         function() { exp.afterExpand(); });
1627         }
1628
1629         if (this.outline) {
1630                 this.outline.table.style.zIndex = this.wrapper.style.zIndex;
1631                 var dir = up || -1,
1632                         offset = this.outline.offset,
1633                         startOff = up ? 3 : offset,
1634                         endOff = up? offset : 3;
1635                 for (var i = startOff; dir * i <= dir * endOff; i += dir, t += 25) {
1636                         (function() {
1637                                 var o = up ? endOff - i : startOff - i;
1638                                 setTimeout(function() {
1639                                         exp.outline.setPosition(0, o, 1);
1640                                 }, t);
1641                         })();
1642                 }
1643         }
1644
1645
1646         if (up) {}//setTimeout(function() { exp.afterExpand(); }, t+50);
1647         else {
1648                 setTimeout( function() {
1649                         if (exp.outline) exp.outline.destroy(exp.preserveContent);
1650
1651                         exp.destroyOverlays();
1652
1653                         hs.animate( exp.wrapper, { opacity: 0 }, hs.restoreDuration, null, function(){
1654                                 exp.afterClose();
1655                         });
1656                 }, t);
1657         }
1658 },
1659 crossfade : function (up, to, from) {
1660         if (!up) return;
1661         var exp = this,
1662                 last = this.last,
1663                 x = this.x,
1664                 y = this.y,
1665                 lastX = last.x,
1666                 lastY = last.y,
1667                 wrapper = this.wrapper,
1668                 content = this.content,
1669                 overlayBox = this.overlayBox;
1670         hs.removeEventListener(document, 'mousemove', hs.dragHandler);
1671
1672         hs.setStyles(content, {
1673                 width: (x.imgSize || x.size) +'px',
1674                 height: (y.imgSize || y.size) +'px'
1675         });
1676         if (overlayBox) overlayBox.style.overflow = 'visible';
1677         this.outline = last.outline;
1678         if (this.outline) this.outline.exp = exp;
1679         last.outline = null;
1680         var fadeBox = hs.createElement('div', {
1681                         className: 'highslide-'+ this.contentType
1682                 }, {
1683                         position: 'absolute',
1684                         zIndex: 4,
1685                         overflow: 'hidden',
1686                         display: 'none'
1687                 }
1688         );
1689         var names = { oldImg: last, newImg: this };
1690         for (var n in names) {
1691                 this[n] = names[n].content.cloneNode(1);
1692                 hs.setStyles(this[n], {
1693                         position: 'absolute',
1694                         border: 0,
1695                         visibility: 'visible'
1696                 });
1697                 fadeBox.appendChild(this[n]);
1698         }
1699         wrapper.appendChild(fadeBox);
1700         if (overlayBox) {
1701                 overlayBox.className = '';
1702                 wrapper.appendChild(overlayBox);
1703         }
1704         fadeBox.style.display = '';
1705         last.content.style.display = 'none';
1706
1707
1708         if (hs.safari && hs.uaVersion < 525) {
1709                 this.wrapper.style.visibility = 'visible';
1710         }
1711         hs.animate(wrapper, {
1712                 width: x.size
1713         }, {
1714                 duration: hs.transitionDuration,
1715                 step: function(val, args) {
1716                         var pos = args.pos,
1717                                 invPos = 1 - pos;
1718                         var prop,
1719                                 size = {},
1720                                 props = ['pos', 'size', 'p1', 'p2'];
1721                         for (var n in props) {
1722                                 prop = props[n];
1723                                 size['x'+ prop] = Math.round(invPos * lastX[prop] + pos * x[prop]);
1724                                 size['y'+ prop] = Math.round(invPos * lastY[prop] + pos * y[prop]);
1725                                 size.ximgSize = Math.round(
1726                                         invPos * (lastX.imgSize || lastX.size) + pos * (x.imgSize || x.size));
1727                                 size.ximgPad = Math.round(invPos * lastX.get('imgPad') + pos * x.get('imgPad'));
1728                                 size.yimgSize = Math.round(
1729                                         invPos * (lastY.imgSize || lastY.size) + pos * (y.imgSize || y.size));
1730                                 size.yimgPad = Math.round(invPos * lastY.get('imgPad') + pos * y.get('imgPad'));
1731                         }
1732                         if (exp.outline) exp.outline.setPosition({
1733                                 x: size.xpos,
1734                                 y: size.ypos,
1735                                 w: size.xsize + size.xp1 + size.xp2 + 2 * x.cb,
1736                                 h: size.ysize + size.yp1 + size.yp2 + 2 * y.cb
1737                         });
1738                         last.wrapper.style.clip = 'rect('
1739                                 + (size.ypos - lastY.pos)+'px, '
1740                                 + (size.xsize + size.xp1 + size.xp2 + size.xpos + 2 * lastX.cb - lastX.pos) +'px, '
1741                                 + (size.ysize + size.yp1 + size.yp2 + size.ypos + 2 * lastY.cb - lastY.pos) +'px, '
1742                                 + (size.xpos - lastX.pos)+'px)';
1743
1744                         hs.setStyles(content, {
1745                                 top: (size.yp1 + y.get('imgPad')) +'px',
1746                                 left: (size.xp1 + x.get('imgPad')) +'px',
1747                                 marginTop: (y.pos - size.ypos) +'px',
1748                                 marginLeft: (x.pos - size.xpos) +'px'
1749                         });
1750                         hs.setStyles(wrapper, {
1751                                 top: size.ypos +'px',
1752                                 left: size.xpos +'px',
1753                                 width: (size.xp1 + size.xp2 + size.xsize + 2 * x.cb)+ 'px',
1754                                 height: (size.yp1 + size.yp2 + size.ysize + 2 * y.cb) + 'px'
1755                         });
1756                         hs.setStyles(fadeBox, {
1757                                 width: (size.ximgSize || size.xsize) + 'px',
1758                                 height: (size.yimgSize || size.ysize) +'px',
1759                                 left: (size.xp1 + size.ximgPad)  +'px',
1760                                 top: (size.yp1 + size.yimgPad) +'px',
1761                                 visibility: 'visible'
1762                         });
1763
1764                         hs.setStyles(exp.oldImg, {
1765                                 top: (lastY.pos - size.ypos + lastY.p1 - size.yp1 + lastY.get('imgPad') - size.yimgPad)+'px',
1766                                 left: (lastX.pos - size.xpos + lastX.p1 - size.xp1 + lastX.get('imgPad') - size.ximgPad)+'px'
1767                         });
1768
1769                         hs.setStyles(exp.newImg, {
1770                                 opacity: pos,
1771                                 top: (y.pos - size.ypos + y.p1 - size.yp1 + y.get('imgPad') - size.yimgPad) +'px',
1772                                 left: (x.pos - size.xpos + x.p1 - size.xp1 + x.get('imgPad') - size.ximgPad) +'px'
1773                         });
1774                         if (overlayBox) hs.setStyles(overlayBox, {
1775                                 width: size.xsize + 'px',
1776                                 height: size.ysize +'px',
1777                                 left: (size.xp1 + x.cb)  +'px',
1778                                 top: (size.yp1 + y.cb) +'px'
1779                         });
1780                 },
1781                 complete: function () {
1782                         wrapper.style.visibility = content.style.visibility = 'visible';
1783                         content.style.display = 'block';
1784                         hs.discardElement(fadeBox);
1785                         exp.afterExpand();
1786                         last.afterClose();
1787                         exp.last = null;
1788                 }
1789
1790         });
1791 },
1792 reuseOverlay : function(o, el) {
1793         if (!this.last) return false;
1794         for (var i = 0; i < this.last.overlays.length; i++) {
1795                 var oDiv = hs.$('hsId'+ this.last.overlays[i]);
1796                 if (oDiv && oDiv.hsId == o.hsId) {
1797                         this.genOverlayBox();
1798                         oDiv.reuse = this.key;
1799                         hs.push(this.overlays, this.last.overlays[i]);
1800                         return true;
1801                 }
1802         }
1803         return false;
1804 },
1805
1806
1807 afterExpand : function() {
1808         this.isExpanded = true;
1809         this.focus();
1810         if (this.dimmingOpacity) hs.dim(this);
1811         if (hs.upcoming && hs.upcoming == this.a) hs.upcoming = null;
1812         this.prepareNextOutline();
1813         var p = hs.page, mX = hs.mouse.x + p.scrollLeft, mY = hs.mouse.y + p.scrollTop;
1814         this.mouseIsOver = this.x.pos < mX && mX < this.x.pos + this.x.get('wsize')
1815                 && this.y.pos < mY && mY < this.y.pos + this.y.get('wsize');
1816         if (this.overlayBox) this.showOverlays();
1817         hs.fireEvent(this, 'onAfterExpand');
1818
1819 },
1820
1821
1822 prepareNextOutline : function() {
1823         var key = this.key;
1824         var outlineType = this.outlineType;
1825         new hs.Outline(outlineType,
1826                 function () { try { hs.expanders[key].preloadNext(); } catch (e) {} });
1827 },
1828
1829
1830 preloadNext : function() {
1831         var next = this.getAdjacentAnchor(1);
1832         if (next && next.onclick.toString().match(/hs\.expand/))
1833                 var img = hs.createElement('img', { src: hs.getSrc(next) });
1834 },
1835
1836
1837 getAdjacentAnchor : function(op) {
1838         var current = this.getAnchorIndex(), as = hs.anchors.groups[this.slideshowGroup || 'none'];
1839         if (as && !as[current + op] && this.slideshow && this.slideshow.repeat) {
1840                 if (op == 1) return as[0];
1841                 else if (op == -1) return as[as.length-1];
1842         }
1843         return (as && as[current + op]) || null;
1844 },
1845
1846 getAnchorIndex : function() {
1847         var arr = hs.getAnchors().groups[this.slideshowGroup || 'none'];
1848         if (arr) for (var i = 0; i < arr.length; i++) {
1849                 if (arr[i] == this.a) return i;
1850         }
1851         return null;
1852 },
1853
1854
1855 getNumber : function() {
1856         if (this[this.numberPosition]) {
1857                 var arr = hs.anchors.groups[this.slideshowGroup || 'none'];
1858                 if (arr) {
1859                         var s = hs.lang.number.replace('%1', this.getAnchorIndex() + 1).replace('%2', arr.length);
1860                         this[this.numberPosition].innerHTML =
1861                                 '<div class="highslide-number">'+ s +'</div>'+ this[this.numberPosition].innerHTML;
1862                 }
1863         }
1864 },
1865 initSlideshow : function() {
1866         if (!this.last) {
1867                 for (var i = 0; i < hs.slideshows.length; i++) {
1868                         var ss = hs.slideshows[i], sg = ss.slideshowGroup;
1869                         if (typeof sg == 'undefined' || sg === null || sg === this.slideshowGroup)
1870                                 this.slideshow = new hs.Slideshow(this.key, ss);
1871                 }
1872         } else {
1873                 this.slideshow = this.last.slideshow;
1874         }
1875         var ss = this.slideshow;
1876         if (!ss) return;
1877         var key = ss.expKey = this.key;
1878
1879         ss.checkFirstAndLast();
1880         ss.disable('full-expand');
1881         if (ss.controls) {
1882                 this.createOverlay(hs.extend(ss.overlayOptions || {}, {
1883                         overlayId: ss.controls,
1884                         hsId: 'controls',
1885                         zIndex: 5
1886                 }));
1887         }
1888         if (ss.thumbstrip) ss.thumbstrip.add(this);
1889         if (!this.last && this.autoplay) ss.play(true);
1890         if (ss.autoplay) {
1891                 ss.autoplay = setTimeout(function() {
1892                         hs.next(key);
1893                 }, (ss.interval || 500));
1894         }
1895 },
1896
1897 cancelLoading : function() {
1898         hs.discardElement (this.wrapper);
1899         hs.expanders[this.key] = null;
1900         if (hs.upcoming == this.a) hs.upcoming = null;
1901         hs.undim(this.key);
1902         if (this.loading) hs.loading.style.left = '-9999px';
1903         hs.fireEvent(this, 'onHideLoading');
1904 },
1905
1906 writeCredits : function () {
1907         if (this.credits) return;
1908         this.credits = hs.createElement('a', {
1909                 href: hs.creditsHref,
1910                 target: hs.creditsTarget,
1911                 className: 'highslide-credits',
1912                 innerHTML: hs.lang.creditsText,
1913                 title: hs.lang.creditsTitle
1914         });
1915         this.createOverlay({
1916                 overlayId: this.credits,
1917                 position: this.creditsPosition || 'top left',
1918                 hsId: 'credits'
1919         });
1920 },
1921
1922 getInline : function(types, addOverlay) {
1923         for (var i = 0; i < types.length; i++) {
1924                 var type = types[i], s = null;
1925                 if (type == 'caption' && !hs.fireEvent(this, 'onBeforeGetCaption')) return;
1926                 else if (type == 'heading' && !hs.fireEvent(this, 'onBeforeGetHeading')) return;
1927                 if (!this[type +'Id'] && this.thumbsUserSetId)
1928                         this[type +'Id'] = type +'-for-'+ this.thumbsUserSetId;
1929                 if (this[type +'Id']) this[type] = hs.getNode(this[type +'Id']);
1930                 if (!this[type] && !this[type +'Text'] && this[type +'Eval']) try {
1931                         s = eval(this[type +'Eval']);
1932                 } catch (e) {}
1933                 if (!this[type] && this[type +'Text']) {
1934                         s = this[type +'Text'];
1935                 }
1936                 if (!this[type] && !s) {
1937                         this[type] = hs.getNode(this.a['_'+ type + 'Id']);
1938                         if (!this[type]) {
1939                                 var next = this.a.nextSibling;
1940                                 while (next && !hs.isHsAnchor(next)) {
1941                                         if ((new RegExp('highslide-'+ type)).test(next.className || null)) {
1942                                                 if (!next.id) this.a['_'+ type + 'Id'] = next.id = 'hsId'+ hs.idCounter++;
1943                                                 this[type] = hs.getNode(next.id);
1944                                                 break;
1945                                         }
1946                                         next = next.nextSibling;
1947                                 }
1948                         }
1949                 }
1950                 if (!this[type] && !s && this.numberPosition == type) s = '\n';
1951
1952                 if (!this[type] && s) this[type] = hs.createElement('div',
1953                                 { className: 'highslide-'+ type, innerHTML: s } );
1954
1955                 if (addOverlay && this[type]) {
1956                         var o = { position: (type == 'heading') ? 'above' : 'below' };
1957                         for (var x in this[type+'Overlay']) o[x] = this[type+'Overlay'][x];
1958                         o.overlayId = this[type];
1959                         this.createOverlay(o);
1960                 }
1961         }
1962 },
1963
1964
1965 // on end move and resize
1966 doShowHide : function(visibility) {
1967         if (hs.hideSelects) this.showHideElements('SELECT', visibility);
1968         if (hs.hideIframes) this.showHideElements('IFRAME', visibility);
1969         if (hs.geckoMac) this.showHideElements('*', visibility);
1970 },
1971 showHideElements : function (tagName, visibility) {
1972         var els = document.getElementsByTagName(tagName);
1973         var prop = tagName == '*' ? 'overflow' : 'visibility';
1974         for (var i = 0; i < els.length; i++) {
1975                 if (prop == 'visibility' || (document.defaultView.getComputedStyle(
1976                                 els[i], "").getPropertyValue('overflow') == 'auto'
1977                                 || els[i].getAttribute('hidden-by') != null)) {
1978                         var hiddenBy = els[i].getAttribute('hidden-by');
1979                         if (visibility == 'visible' && hiddenBy) {
1980                                 hiddenBy = hiddenBy.replace('['+ this.key +']', '');
1981                                 els[i].setAttribute('hidden-by', hiddenBy);
1982                                 if (!hiddenBy) els[i].style[prop] = els[i].origProp;
1983                         } else if (visibility == 'hidden') { // hide if behind
1984                                 var elPos = hs.getPosition(els[i]);
1985                                 elPos.w = els[i].offsetWidth;
1986                                 elPos.h = els[i].offsetHeight;
1987                                 if (!this.dimmingOpacity) { // hide all if dimming
1988
1989                                         var clearsX = (elPos.x + elPos.w < this.x.get('opos')
1990                                                 || elPos.x > this.x.get('opos') + this.x.get('osize'));
1991                                         var clearsY = (elPos.y + elPos.h < this.y.get('opos')
1992                                                 || elPos.y > this.y.get('opos') + this.y.get('osize'));
1993                                 }
1994                                 var wrapperKey = hs.getWrapperKey(els[i]);
1995                                 if (!clearsX && !clearsY && wrapperKey != this.key) { // element falls behind image
1996                                         if (!hiddenBy) {
1997                                                 els[i].setAttribute('hidden-by', '['+ this.key +']');
1998                                                 els[i].origProp = els[i].style[prop];
1999                                                 els[i].style[prop] = 'hidden';
2000
2001                                         } else if (hiddenBy.indexOf('['+ this.key +']') == -1) {
2002                                                 els[i].setAttribute('hidden-by', hiddenBy + '['+ this.key +']');
2003                                         }
2004                                 } else if ((hiddenBy == '['+ this.key +']' || hs.focusKey == wrapperKey)
2005                                                 && wrapperKey != this.key) { // on move
2006                                         els[i].setAttribute('hidden-by', '');
2007                                         els[i].style[prop] = els[i].origProp || '';
2008                                 } else if (hiddenBy && hiddenBy.indexOf('['+ this.key +']') > -1) {
2009                                         els[i].setAttribute('hidden-by', hiddenBy.replace('['+ this.key +']', ''));
2010                                 }
2011
2012                         }
2013                 }
2014         }
2015 },
2016
2017 focus : function() {
2018         this.wrapper.style.zIndex = hs.zIndexCounter += 2;
2019         // blur others
2020         for (var i = 0; i < hs.expanders.length; i++) {
2021                 if (hs.expanders[i] && i == hs.focusKey) {
2022                         var blurExp = hs.expanders[i];
2023                         blurExp.content.className += ' highslide-'+ blurExp.contentType +'-blur';
2024                                 blurExp.content.style.cursor = hs.ieLt7 ? 'hand' : 'pointer';
2025                                 blurExp.content.title = hs.lang.focusTitle;
2026                         hs.fireEvent(blurExp, 'onBlur');
2027                 }
2028         }
2029
2030         // focus this
2031         if (this.outline) this.outline.table.style.zIndex
2032                 = this.wrapper.style.zIndex - 1;
2033         this.content.className = 'highslide-'+ this.contentType;
2034                 this.content.title = hs.lang.restoreTitle;
2035
2036                 if (hs.restoreCursor) {
2037                         hs.styleRestoreCursor = window.opera ? 'pointer' : 'url('+ hs.graphicsDir + hs.restoreCursor +'), pointer';
2038                         if (hs.ieLt7 && hs.uaVersion < 6) hs.styleRestoreCursor = 'hand';
2039                         this.content.style.cursor = hs.styleRestoreCursor;
2040                 }
2041
2042         hs.focusKey = this.key;
2043         hs.addEventListener(document, window.opera ? 'keypress' : 'keydown', hs.keyHandler);
2044         hs.fireEvent(this, 'onFocus');
2045 },
2046 moveTo: function(x, y) {
2047         this.x.setPos(x);
2048         this.y.setPos(y);
2049 },
2050 resize : function (e) {
2051         var w, h, r = e.width / e.height;
2052         w = Math.max(e.width + e.dX, Math.min(this.minWidth, this.x.full));
2053         if (this.isImage && Math.abs(w - this.x.full) < 12) w = this.x.full;
2054         h = w / r;
2055         if (h < Math.min(this.minHeight, this.y.full)) {
2056                 h = Math.min(this.minHeight, this.y.full);
2057                 if (this.isImage) w = h * r;
2058         }
2059         this.resizeTo(w, h);
2060 },
2061 resizeTo: function(w, h) {
2062         this.y.setSize(h);
2063         this.x.setSize(w);
2064         this.wrapper.style.height = this.y.get('wsize') +'px';
2065 },
2066
2067 close : function() {
2068         if (this.isClosing || !this.isExpanded) return;
2069         if (this.transitions[1] == 'crossfade' && hs.upcoming) {
2070                 hs.getExpander(hs.upcoming).cancelLoading();
2071                 hs.upcoming = null;
2072         }
2073         if (!hs.fireEvent(this, 'onBeforeClose')) return;
2074         this.isClosing = true;
2075         if (this.slideshow && !hs.upcoming) this.slideshow.pause();
2076
2077         hs.removeEventListener(document, window.opera ? 'keypress' : 'keydown', hs.keyHandler);
2078
2079         try {
2080                 this.content.style.cursor = 'default';
2081                 this.changeSize(
2082                         0, {
2083                                 wrapper: {
2084                                         width : this.x.t,
2085                                         height : this.y.t,
2086                                         left: this.x.tpos - this.x.cb + this.x.tb,
2087                                         top: this.y.tpos - this.y.cb + this.y.tb
2088                                 },
2089                                 content: {
2090                                         left: 0,
2091                                         top: 0,
2092                                         width: this.x.t,
2093                                         height: this.y.t
2094                                 }
2095                         }, hs.restoreDuration
2096                 );
2097         } catch (e) { this.afterClose(); }
2098 },
2099
2100 createOverlay : function (o) {
2101         var el = o.overlayId,
2102                 relToVP = (o.relativeTo == 'viewport' && !/panel$/.test(o.position));
2103         if (typeof el == 'string') el = hs.getNode(el);
2104         if (o.html) el = hs.createElement('div', { innerHTML: o.html });
2105         if (!el || typeof el == 'string') return;
2106         if (!hs.fireEvent(this, 'onCreateOverlay', { overlay: el })) return;
2107         el.style.display = 'block';
2108         o.hsId = o.hsId || o.overlayId;
2109         if (this.transitions[1] == 'crossfade' && this.reuseOverlay(o, el)) return;
2110         this.genOverlayBox();
2111         var width = o.width && /^[0-9]+(px|%)$/.test(o.width) ? o.width : 'auto';
2112         if (/^(left|right)panel$/.test(o.position) && !/^[0-9]+px$/.test(o.width)) width = '200px';
2113         var overlay = hs.createElement(
2114                 'div', {
2115                         id: 'hsId'+ hs.idCounter++,
2116                         hsId: o.hsId
2117                 }, {
2118                         position: 'absolute',
2119                         visibility: 'hidden',
2120                         width: width,
2121                         direction: hs.lang.cssDirection || '',
2122                         opacity: 0
2123                 },
2124                 relToVP ? hs.viewport :this.overlayBox,
2125                 true
2126         );
2127         if (relToVP) overlay.hsKey = this.key;
2128
2129         overlay.appendChild(el);
2130         hs.extend(overlay, {
2131                 opacity: 1,
2132                 offsetX: 0,
2133                 offsetY: 0,
2134                 dur: (o.fade === 0 || o.fade === false || (o.fade == 2 && hs.ie)) ? 0 : 250
2135         });
2136         hs.extend(overlay, o);
2137
2138
2139         if (this.gotOverlays) {
2140                 this.positionOverlay(overlay);
2141                 if (!overlay.hideOnMouseOut || this.mouseIsOver)
2142                         hs.animate(overlay, { opacity: overlay.opacity }, overlay.dur);
2143         }
2144         hs.push(this.overlays, hs.idCounter - 1);
2145 },
2146 positionOverlay : function(overlay) {
2147         var p = overlay.position || 'middle center',
2148                 relToVP = (overlay.relativeTo == 'viewport'),
2149                 offX = overlay.offsetX,
2150                 offY = overlay.offsetY;
2151         if (relToVP) {
2152                 hs.viewport.style.display = 'block';
2153                 overlay.hsKey = this.key;
2154                 if (overlay.offsetWidth > overlay.parentNode.offsetWidth)
2155                         overlay.style.width = '100%';
2156         } else
2157         if (overlay.parentNode != this.overlayBox) this.overlayBox.appendChild(overlay);
2158         if (/left$/.test(p)) overlay.style.left = offX +'px';
2159
2160         if (/center$/.test(p))  hs.setStyles (overlay, {
2161                 left: '50%',
2162                 marginLeft: (offX - Math.round(overlay.offsetWidth / 2)) +'px'
2163         });
2164
2165         if (/right$/.test(p)) overlay.style.right = - offX +'px';
2166
2167         if (/^leftpanel$/.test(p)) {
2168                 hs.setStyles(overlay, {
2169                         right: '100%',
2170                         marginRight: this.x.cb +'px',
2171                         top: - this.y.cb +'px',
2172                         bottom: - this.y.cb +'px',
2173                         overflow: 'auto'
2174                 });
2175                 this.x.p1 = overlay.offsetWidth;
2176
2177         } else if (/^rightpanel$/.test(p)) {
2178                 hs.setStyles(overlay, {
2179                         left: '100%',
2180                         marginLeft: this.x.cb +'px',
2181                         top: - this.y.cb +'px',
2182                         bottom: - this.y.cb +'px',
2183                         overflow: 'auto'
2184                 });
2185                 this.x.p2 = overlay.offsetWidth;
2186         }
2187         var parOff = overlay.parentNode.offsetHeight;
2188         overlay.style.height = 'auto';
2189         if (relToVP && overlay.offsetHeight > parOff)
2190                 overlay.style.height = hs.ieLt7 ? parOff +'px' : '100%';
2191
2192         if (/^top/.test(p)) overlay.style.top = offY +'px';
2193         if (/^middle/.test(p))  hs.setStyles (overlay, {
2194                 top: '50%',
2195                 marginTop: (offY - Math.round(overlay.offsetHeight / 2)) +'px'
2196         });
2197         if (/^bottom/.test(p)) overlay.style.bottom = - offY +'px';
2198         if (/^above$/.test(p)) {
2199                 hs.setStyles(overlay, {
2200                         left: (- this.x.p1 - this.x.cb) +'px',
2201                         right: (- this.x.p2 - this.x.cb) +'px',
2202                         bottom: '100%',
2203                         marginBottom: this.y.cb +'px',
2204                         width: 'auto'
2205                 });
2206                 this.y.p1 = overlay.offsetHeight;
2207
2208         } else if (/^below$/.test(p)) {
2209                 hs.setStyles(overlay, {
2210                         position: 'relative',
2211                         left: (- this.x.p1 - this.x.cb) +'px',
2212                         right: (- this.x.p2 - this.x.cb) +'px',
2213                         top: '100%',
2214                         marginTop: this.y.cb +'px',
2215                         width: 'auto'
2216                 });
2217                 this.y.p2 = overlay.offsetHeight;
2218                 overlay.style.position = 'absolute';
2219         }
2220 },
2221
2222 getOverlays : function() {
2223         this.getInline(['heading', 'caption'], true);
2224         this.getNumber();
2225         if (this.caption) hs.fireEvent(this, 'onAfterGetCaption');
2226         if (this.heading) hs.fireEvent(this, 'onAfterGetHeading');
2227         if (this.heading && this.dragByHeading) this.heading.className += ' highslide-move';
2228         if (hs.showCredits) this.writeCredits();
2229         for (var i = 0; i < hs.overlays.length; i++) {
2230                 var o = hs.overlays[i], tId = o.thumbnailId, sg = o.slideshowGroup;
2231                 if ((!tId && !sg) || (tId && tId == this.thumbsUserSetId)
2232                                 || (sg && sg === this.slideshowGroup)) {
2233                         this.createOverlay(o);
2234                 }
2235         }
2236         var os = [];
2237         for (var i = 0; i < this.overlays.length; i++) {
2238                 var o = hs.$('hsId'+ this.overlays[i]);
2239                 if (/panel$/.test(o.position)) this.positionOverlay(o);
2240                 else hs.push(os, o);
2241         }
2242         for (var i = 0; i < os.length; i++) this.positionOverlay(os[i]);
2243         this.gotOverlays = true;
2244 },
2245 genOverlayBox : function() {
2246         if (!this.overlayBox) this.overlayBox = hs.createElement (
2247                 'div', {
2248                         className: this.wrapperClassName
2249                 }, {
2250                         position : 'absolute',
2251                         width: (this.x.size || (this.useBox ? this.width : null)
2252                                 || this.x.full) +'px',
2253                         height: (this.y.size || this.y.full) +'px',
2254                         visibility : 'hidden',
2255                         overflow : 'hidden',
2256                         zIndex : hs.ie ? 4 : 'auto'
2257                 },
2258                 hs.container,
2259                 true
2260         );
2261 },
2262 sizeOverlayBox : function(doWrapper, doPanels) {
2263         var overlayBox = this.overlayBox,
2264                 x = this.x,
2265                 y = this.y;
2266         hs.setStyles( overlayBox, {
2267                 width: x.size +'px',
2268                 height: y.size +'px'
2269         });
2270         if (doWrapper || doPanels) {
2271                 for (var i = 0; i < this.overlays.length; i++) {
2272                         var o = hs.$('hsId'+ this.overlays[i]);
2273                         var ie6 = (hs.ieLt7 || document.compatMode == 'BackCompat');
2274                         if (o && /^(above|below)$/.test(o.position)) {
2275                                 if (ie6) {
2276                                         o.style.width = (overlayBox.offsetWidth + 2 * x.cb
2277                                                 + x.p1 + x.p2) +'px';
2278                                 }
2279                                 y[o.position == 'above' ? 'p1' : 'p2'] = o.offsetHeight;
2280                         }
2281                         if (o && ie6 && /^(left|right)panel$/.test(o.position)) {
2282                                 o.style.height = (overlayBox.offsetHeight + 2* y.cb) +'px';
2283                         }
2284                 }
2285         }
2286         if (doWrapper) {
2287                 hs.setStyles(this.content, {
2288                         top: y.p1 +'px'
2289                 });
2290                 hs.setStyles(overlayBox, {
2291                         top: (y.p1 + y.cb) +'px'
2292                 });
2293         }
2294 },
2295
2296 showOverlays : function() {
2297         var b = this.overlayBox;
2298         b.className = '';
2299         hs.setStyles(b, {
2300                 top: (this.y.p1 + this.y.cb) +'px',
2301                 left: (this.x.p1 + this.x.cb) +'px',
2302                 overflow : 'visible'
2303         });
2304         if (hs.safari) b.style.visibility = 'visible';
2305         this.wrapper.appendChild (b);
2306         for (var i = 0; i < this.overlays.length; i++) {
2307                 var o = hs.$('hsId'+ this.overlays[i]);
2308                 o.style.zIndex = o.zIndex || 4;
2309                 if (!o.hideOnMouseOut || this.mouseIsOver) {
2310                         o.style.visibility = 'visible';
2311                         hs.setStyles(o, { visibility: 'visible', display: '' });
2312                         hs.animate(o, { opacity: o.opacity }, o.dur);
2313                 }
2314         }
2315 },
2316
2317 destroyOverlays : function() {
2318         if (!this.overlays.length) return;
2319         if (this.slideshow) {
2320                 var c = this.slideshow.controls;
2321                 if (c && hs.getExpander(c) == this) c.parentNode.removeChild(c);
2322         }
2323         for (var i = 0; i < this.overlays.length; i++) {
2324                 var o = hs.$('hsId'+ this.overlays[i]);
2325                 if (o && o.parentNode == hs.viewport && hs.getExpander(o) == this) hs.discardElement(o);
2326         }
2327         hs.discardElement(this.overlayBox);
2328 },
2329
2330
2331
2332 createFullExpand : function () {
2333         if (this.slideshow && this.slideshow.controls) {
2334                 this.slideshow.enable('full-expand');
2335                 return;
2336         }
2337         this.fullExpandLabel = hs.createElement(
2338                 'a', {
2339                         href: 'javascript:hs.expanders['+ this.key +'].doFullExpand();',
2340                         title: hs.lang.fullExpandTitle,
2341                         className: 'highslide-full-expand'
2342                 }
2343         );
2344         if (!hs.fireEvent(this, 'onCreateFullExpand')) return;
2345
2346         this.createOverlay({
2347                 overlayId: this.fullExpandLabel,
2348                 position: hs.fullExpandPosition,
2349                 hideOnMouseOut: true,
2350                 opacity: hs.fullExpandOpacity
2351         });
2352 },
2353
2354 doFullExpand : function () {
2355         try {
2356                 if (!hs.fireEvent(this, 'onDoFullExpand')) return;
2357                 if (this.fullExpandLabel) hs.discardElement(this.fullExpandLabel);
2358
2359                 this.focus();
2360                 var xSize = this.x.size,
2361                         ySize = this.y.size;
2362                 this.resizeTo(this.x.full, this.y.full);
2363
2364                 var xpos = this.x.pos - (this.x.size - xSize) / 2;
2365                 if (xpos < hs.marginLeft) xpos = hs.marginLeft;
2366
2367                 var ypos = this.y.pos - (this.y.size - ySize) / 2;
2368                 if (ypos < hs.marginTop) ypos = hs.marginTop;
2369
2370                 this.moveTo(xpos, ypos);
2371                 this.doShowHide('hidden');
2372
2373         } catch (e) {
2374                 this.error(e);
2375         }
2376 },
2377
2378
2379 afterClose : function () {
2380         this.a.className = this.a.className.replace('highslide-active-anchor', '');
2381
2382         this.doShowHide('visible');
2383                 if (this.outline && this.outlineWhileAnimating) this.outline.destroy();
2384
2385                 hs.discardElement(this.wrapper);
2386         this.destroyOverlays();
2387         if (!hs.viewport.childNodes.length) hs.viewport.style.display = 'none';
2388
2389         if (this.dimmingOpacity) hs.undim(this.key);
2390         hs.fireEvent(this, 'onAfterClose');
2391         hs.expanders[this.key] = null;
2392         hs.reOrder();
2393 }
2394
2395 };
2396
2397
2398 hs.Slideshow = function (expKey, options) {
2399         if (hs.dynamicallyUpdateAnchors !== false) hs.updateAnchors();
2400         this.expKey = expKey;
2401         for (var x in options) this[x] = options[x];
2402         if (this.useControls) this.getControls();
2403         if (this.thumbstrip) this.thumbstrip = hs.Thumbstrip(this);
2404 };
2405 hs.Slideshow.prototype = {
2406 getControls: function() {
2407         this.controls = hs.createElement('div', { innerHTML: hs.replaceLang(hs.skin.controls) },
2408                 null, hs.container);
2409
2410         var buttons = ['play', 'pause', 'previous', 'next', 'move', 'full-expand', 'close'];
2411         this.btn = {};
2412         var pThis = this;
2413         for (var i = 0; i < buttons.length; i++) {
2414                 this.btn[buttons[i]] = hs.getElementByClass(this.controls, 'li', 'highslide-'+ buttons[i]);
2415                 this.enable(buttons[i]);
2416         }
2417         this.btn.pause.style.display = 'none';
2418         //this.disable('full-expand');
2419 },
2420 checkFirstAndLast: function() {
2421         if (this.repeat || !this.controls) return;
2422         var exp = hs.expanders[this.expKey],
2423                 cur = exp.getAnchorIndex(),
2424                 re = /disabled$/;
2425         if (cur == 0)
2426                 this.disable('previous');
2427         else if (re.test(this.btn.previous.getElementsByTagName('a')[0].className))
2428                 this.enable('previous');
2429         if (cur + 1 == hs.anchors.groups[exp.slideshowGroup || 'none'].length) {
2430                 this.disable('next');
2431                 this.disable('play');
2432         } else if (re.test(this.btn.next.getElementsByTagName('a')[0].className)) {
2433                 this.enable('next');
2434                 this.enable('play');
2435         }
2436 },
2437 enable: function(btn) {
2438         if (!this.btn) return;
2439         var sls = this, a = this.btn[btn].getElementsByTagName('a')[0], re = /disabled$/;
2440         a.onclick = function() {
2441                 sls[btn]();
2442                 return false;
2443         };
2444         if (re.test(a.className)) a.className = a.className.replace(re, '');
2445 },
2446 disable: function(btn) {
2447         if (!this.btn) return;
2448         var a = this.btn[btn].getElementsByTagName('a')[0];
2449         a.onclick = function() { return false; };
2450         if (!/disabled$/.test(a.className)) a.className += ' disabled';
2451 },
2452 hitSpace: function() {
2453         if (this.autoplay) this.pause();
2454         else this.play();
2455 },
2456 play: function(wait) {
2457         if (this.btn) {
2458                 this.btn.play.style.display = 'none';
2459                 this.btn.pause.style.display = '';
2460         }
2461
2462         this.autoplay = true;
2463         if (!wait) hs.next(this.expKey);
2464 },
2465 pause: function() {
2466         if (this.btn) {
2467                 this.btn.pause.style.display = 'none';
2468                 this.btn.play.style.display = '';
2469         }
2470
2471         clearTimeout(this.autoplay);
2472         this.autoplay = null;
2473 },
2474 previous: function() {
2475         this.pause();
2476         hs.previous(this.btn.previous);
2477 },
2478 next: function() {
2479         this.pause();
2480         hs.next(this.btn.next);
2481 },
2482 move: function() {},
2483 'full-expand': function() {
2484         hs.getExpander().doFullExpand();
2485 },
2486 close: function() {
2487         hs.close(this.btn.close);
2488 }
2489 };
2490 hs.Thumbstrip = function(slideshow) {
2491         function add (exp) {
2492                 hs.extend(options || {}, {
2493                         overlayId: dom,
2494                         hsId: 'thumbstrip',
2495                         className: 'highslide-thumbstrip-'+ mode +'-overlay ' + (options.className || '')
2496                 });
2497                 if (hs.ieLt7) options.fade = 0;
2498                 exp.createOverlay(options);
2499                 hs.setStyles(dom.parentNode, { overflow: 'hidden' });
2500         };
2501
2502         function scroll (delta) {
2503                 selectThumb(undefined, Math.round(delta * dom[isX ? 'offsetWidth' : 'offsetHeight'] * 0.7));
2504         };
2505
2506         function selectThumb (i, scrollBy) {
2507                 if (i === undefined) for (var j = 0; j < group.length; j++) {
2508                         if (group[j] == hs.expanders[slideshow.expKey].a) {
2509                                 i = j;
2510                                 break;
2511                         }
2512                 }
2513                 if (i === undefined) return;
2514                 var as = dom.getElementsByTagName('a'),
2515                         active = as[i],
2516                         cell = active.parentNode,
2517                         left = isX ? 'Left' : 'Top',
2518                         right = isX ? 'Right' : 'Bottom',
2519                         width = isX ? 'Width' : 'Height',
2520                         offsetLeft = 'offset' + left,
2521                         offsetWidth = 'offset' + width,
2522                         overlayWidth = div.parentNode.parentNode[offsetWidth],
2523                         minTblPos = overlayWidth - table[offsetWidth],
2524                         curTblPos = parseInt(table.style[isX ? 'left' : 'top']) || 0,
2525                         tblPos = curTblPos,
2526                         mgnRight = 20;
2527                 if (scrollBy !== undefined) {
2528                         tblPos = curTblPos - scrollBy;
2529
2530                         if (minTblPos > 0) minTblPos = 0;
2531                         if (tblPos > 0) tblPos = 0;
2532                         if (tblPos < minTblPos) tblPos = minTblPos;
2533
2534
2535                 } else {
2536                         for (var j = 0; j < as.length; j++) as[j].className = '';
2537                         active.className = 'highslide-active-anchor';
2538                         var activeLeft = i > 0 ? as[i - 1].parentNode[offsetLeft] : cell[offsetLeft],
2539                                 activeRight = cell[offsetLeft] + cell[offsetWidth] +
2540                                         (as[i + 1] ? as[i + 1].parentNode[offsetWidth] : 0);
2541                         if (activeRight > overlayWidth - curTblPos) tblPos = overlayWidth - activeRight;
2542                         else if (activeLeft < -curTblPos) tblPos = -activeLeft;
2543                 }
2544                 var markerPos = cell[offsetLeft] + (cell[offsetWidth] - marker[offsetWidth]) / 2 + tblPos;
2545                 hs.animate(table, isX ? { left: tblPos } : { top: tblPos }, null, 'easeOutQuad');
2546                 hs.animate(marker, isX ? { left: markerPos } : { top: markerPos }, null, 'easeOutQuad');
2547                 scrollUp.style.display = tblPos < 0 ? 'block' : 'none';
2548                 scrollDown.style.display = (tblPos > minTblPos)  ? 'block' : 'none';
2549
2550         };
2551
2552
2553         // initialize
2554         var group = hs.anchors.groups[hs.expanders[slideshow.expKey].slideshowGroup || 'none'],
2555                 options = slideshow.thumbstrip,
2556                 mode = options.mode || 'horizontal',
2557                 floatMode = (mode == 'float'),
2558                 tree = floatMode ? ['div', 'ul', 'li', 'span'] : ['table', 'tbody', 'tr', 'td'],
2559                 isX = (mode == 'horizontal'),
2560                 dom = hs.createElement('div', {
2561                                 className: 'highslide-thumbstrip highslide-thumbstrip-'+ mode,
2562                                 innerHTML:
2563                                         '<div class="highslide-thumbstrip-inner">'+
2564                                         '<'+ tree[0] +'><'+ tree[1] +'></'+ tree[1] +'></'+ tree[0] +'></div>'+
2565                                         '<div class="highslide-scroll-up"><div></div></div>'+
2566                                         '<div class="highslide-scroll-down"><div></div></div>'+
2567                                         '<div class="highslide-marker"><div></div></div>'
2568                         }, {
2569                                 display: 'none'
2570                         }, hs.container),
2571                 domCh = dom.childNodes,
2572                 div = domCh[0],
2573                 scrollUp = domCh[1],
2574                 scrollDown = domCh[2],
2575                 marker = domCh[3],
2576                 table = div.firstChild,
2577                 tbody = dom.getElementsByTagName(tree[1])[0],
2578                 tr;
2579         for (var i = 0; i < group.length; i++) {
2580                 if (i == 0 || !isX) tr = hs.createElement(tree[2], null, null, tbody);
2581                 (function(){
2582                         var a = group[i],
2583                                 cell = hs.createElement(tree[3], null, null, tr),
2584                                 pI = i;
2585                         hs.createElement('a', {
2586                                 href: a.href,
2587                                 title: a.title,
2588                                 onclick: function() {
2589                                         if (/highslide-active-anchor/.test(this.className)) return false;
2590                                         hs.getExpander(this).focus();
2591                                         return hs.transit(a);
2592                                 },
2593                                 innerHTML: hs.stripItemFormatter ? hs.stripItemFormatter(a) : a.innerHTML
2594                         }, null, cell);
2595                 })();
2596         }
2597         if (!floatMode) {
2598                 scrollUp.onclick = function () { scroll(-1); };
2599                 scrollDown.onclick = function() { scroll(1); };
2600                 hs.addEventListener(tbody, document.onmousewheel !== undefined ?
2601                                 'mousewheel' : 'DOMMouseScroll', function(e) {
2602                         var delta = 0;
2603                         e = e || window.event;
2604                         if (e.wheelDelta) {
2605                                 delta = e.wheelDelta/120;
2606                                 if (hs.opera) delta = -delta;
2607                         } else if (e.detail) {
2608                                 delta = -e.detail/3;
2609                         }
2610                         if (delta) scroll(-delta * 0.2);
2611                         if (e.preventDefault) e.preventDefault();
2612                         e.returnValue = false;
2613                 });
2614         }
2615
2616         return {
2617                 add: add,
2618                 selectThumb: selectThumb
2619         }
2620 };
2621 hs.langDefaults = hs.lang;
2622 // history
2623 var HsExpander = hs.Expander;
2624 if (hs.ie && window == window.top) {
2625         (function () {
2626                 try {
2627                         document.documentElement.doScroll('left');
2628                 } catch (e) {
2629                         setTimeout(arguments.callee, 50);
2630                         return;
2631                 }
2632                 hs.ready();
2633         })();
2634 }
2635 hs.addEventListener(document, 'DOMContentLoaded', hs.ready);
2636 hs.addEventListener(window, 'load', hs.ready);
2637
2638 // set handlers
2639 hs.addEventListener(document, 'ready', function() {
2640         if (hs.expandCursor || hs.dimmingOpacity) {
2641                 var style = hs.createElement('style', { type: 'text/css' }, null,
2642                         document.getElementsByTagName('HEAD')[0]),
2643                         backCompat = document.compatMode == 'BackCompat';
2644
2645
2646                 function addRule(sel, dec) {
2647                         if (hs.ie && (hs.uaVersion < 9 || backCompat)) {
2648                                 var last = document.styleSheets[document.styleSheets.length - 1];
2649                                 if (typeof(last.addRule) == "object") last.addRule(sel, dec);
2650                         } else {
2651                                 style.appendChild(document.createTextNode(sel + " {" + dec + "}"));
2652                         }
2653                 }
2654                 function fix(prop) {
2655                         return 'expression( ( ( ignoreMe = document.documentElement.'+ prop +
2656                                 ' ? document.documentElement.'+ prop +' : document.body.'+ prop +' ) ) + \'px\' );';
2657                 }
2658                 if (hs.expandCursor) addRule ('.highslide img',
2659                         'cursor: url('+ hs.graphicsDir + hs.expandCursor +'), pointer !important;');
2660                 addRule ('.highslide-viewport-size',
2661                         hs.ie && (hs.uaVersion < 7 || backCompat) ?
2662                                 'position: absolute; '+
2663                                 'left:'+ fix('scrollLeft') +
2664                                 'top:'+ fix('scrollTop') +
2665                                 'width:'+ fix('clientWidth') +
2666                                 'height:'+ fix('clientHeight') :
2667                                 'position: fixed; width: 100%; height: 100%; left: 0; top: 0');
2668         }
2669 });
2670 hs.addEventListener(window, 'resize', function() {
2671         hs.getPageSize();
2672         if (hs.viewport) for (var i = 0; i < hs.viewport.childNodes.length; i++) {
2673                 var node = hs.viewport.childNodes[i],
2674                         exp = hs.getExpander(node);
2675                 exp.positionOverlay(node);
2676                 if (node.hsId == 'thumbstrip') exp.slideshow.thumbstrip.selectThumb();
2677         }
2678 });
2679 hs.addEventListener(document, 'mousemove', function(e) {
2680         hs.mouse = { x: e.clientX, y: e.clientY };
2681 });
2682 hs.addEventListener(document, 'mousedown', hs.mouseClickHandler);
2683 hs.addEventListener(document, 'mouseup', hs.mouseClickHandler);
2684
2685 hs.addEventListener(document, 'ready', hs.getAnchors);
2686 hs.addEventListener(window, 'load', hs.preloadImages);
2687 }