October 17, 2007

Portable Image Viewer

Like I said, I planned to work on some fun applications of the YUI Anywhere idea.

I sat down after school for a minute and decided it would be cool to be able to click a button on my bookmarks bar which would give me a slideshow of all the images on any web page. Then I discovered the yui-loader, which is designed to load the other yui modules, such as the animation utility. The yui-loader is still in beta phase, but it works fine in Firefox.

Here is what I came up with: (working example)

var DW = (typeof DW == 'undefined') ? {} : DW;

/* When someone clicks the bookmark, if the class is already defined, it will display the viewer */
if(typeof DW.imageViewer != 'undefined') {
DW.imageViewer.show();
} else {
DW.imageViewer = function() {

var Event;
var Anim;
var Dom;
var Overlay;

var _item;
var _image;
var _src;
var _current;
var _imageViewer;

var init = function() {
Event = YAHOO.util.Event;
Anim = YAHOO.util.Anim;
Dom = YAHOO.util.Dom;
Overlay = YAHOO.widget.Overlay;

_src = Dom.batch(_item, function(o){
return o.src;
});

_current = 0;

_imageViewer = new Overlay('imageViewer', {
effect: {effect:YAHOO.widget.ContainerEffect.FADE,duration:0.30},
zIndex: 90,
width: Dom.getViewportWidth() + 'px',
height: Dom.getDocumentHeight() + 'px',
visible: false,
x: 0,
y: 0
});

setUpHeader();
setUpBody();

_imageViewer.render(document.body);

setStyles('imageHolder', {
'width': '100%',
'textAlign': 'center',
'marginTop': '10px'
});

setStyles(_image, {
'padding': '5px',
'border': '1px solid #7E7E7E'
});

setStyles('imageViewer', {
'textAlign': 'center',
'backgroundColor': '#000'
});

Event.on(_image, 'click', linked);
Event.on(_image, 'mouseover', checkLinked);

setUpButtons();
putCurrentImage();
show();
};

var setUpHeader = function() {

var closeButton = newEl({
'el': 'input',
'id': 'closeButton',
'type': 'button',
'value': 'close'
});

_imageViewer.appendToHeader(closeButton);

var headerButtons = newEl({
'el': 'div',
'id': 'headerButtons'
});

var prevButton = newEl({
'el': 'input',
'type': 'button',
'value': 'previous',
'id': 'previousButton'
});

var nextButton = newEl({
'el': 'input',
'type': 'button',
'value': 'next',
'id': 'nextButton'
});

headerButtons.appendChild(prevButton);
headerButtons.appendChild(nextButton);

_imageViewer.appendToHeader(headerButtons);
};

var setUpBody = function() {
var imageHolder = newEl({
'el': 'div',
'id': 'imageHolder'
});

_image = newEl({
'el': 'img',
'id': 'currentImage',
'src': _src[0]
});

imageHolder.appendChild(_image);

_imageViewer.appendToBody(imageHolder);
};

var newEl = function(attrs) {
var el = document.createElement(attrs.el);
for(var i in attrs) {
el[i] = attrs[i];
};
return el;
};

var setStyles = function(el, style) {
for(var i in style) {
Dom.setStyle(el, i, style[i]);
}
};

var setUpButtons = function() {
var b = ['previousButton', 'nextButton', 'closeButton'];
Event.on(b[0], 'click', changeImage, {delta: -1});
Event.on(b[1], 'click', changeImage, {delta: 1});
Event.on(b[2], 'click', hide);

setStyles(b[2], {
'position': 'absolute',
'top': '10px',
'left': '10px'
});

setStyles('headerButtons', {
'marginTop': '10px'
});

setStyles(b, {
'color': '#2C2C2C',
'border': '1px solid #2C2C2C',
'backgroundColor': '#000',
'padding': '5px',
'margin': '5px'
});

Event.on(b, 'mouseover', function() {
Dom.setStyle(b, 'color', '#FFF');
Dom.setStyle(b, 'border', '1px solid #FFF');
});

Event.on(b, 'mouseout', function() {
Dom.setStyle(b, 'color', '#2C2C2C');
Dom.setStyle(b, 'border', '1px solid #2C2C2C');
});
};

var changeImage = function(e, o) {
_current += o.delta;

if(_current < 0) {
_current = (_src.length - 1);
}
else if(_current > (_src.length - 1)) {
_current = 0;
}
putCurrentImage();
};

var putCurrentImage = function() {
var fadeOut = new Anim(_image, {
opacity: { to: 0 }
}, 0.25, YAHOO.util.Easing.easeOut);
fadeOut.onComplete.subscribe(putCurrentImageHelper);
fadeOut.animate();
};

var putCurrentImageHelper = function() {
_image.src = _src[_current];

var fadeIn = new Anim(_image, {
opacity: { to: 1 }
}, 0.25, YAHOO.util.Easing.easeOut);

fadeIn.animate();
};

var flashBack = function() {
var flashBack = new YAHOO.util.ColorAnim(_image, {
backgroundColor: { to: '#000'}
}, 0.30);
flashBack.animate();
};

var getPar = function() {
return Dom.getAncestorByTagName(_item[_current], 'A');
};

var checkLinked = function() {
var found = false;
var par = getPar();
if(par !== null) {
for(var i = 0; i < extensionTest.length; i++) {
if(extensionTest[i].test(par.href)) {
found = true;
}
}
if(!found) {
/* yellow */
var flashYellow = new YAHOO.util.ColorAnim(_image, {
backgroundColor: { to: '#FAEB31'}
}, 0.30);
flashYellow.onComplete.subscribe(flashBack);
flashYellow.animate();
}
else {
/* green */
var flashGreen = new YAHOO.util.ColorAnim(_image, {
backgroundColor: { to: '#54A14E'}
}, 0.30);
flashGreen.onComplete.subscribe(flashBack);
flashGreen.animate();
}
}
else {
/* blue */
var flashBlue = new YAHOO.util.ColorAnim(_image, {
backgroundColor: { to: '#06e'}
}, 0.30);
flashBlue.onComplete.subscribe(flashBack);
flashBlue.animate();
}
};

var extensionTest = [new RegExp('.jpg$', 'i'), new RegExp('.gif$', 'i'), new RegExp('.png$', 'i'), new RegExp('.jpeg$', 'i')];

var linked = function() {
var found = false;
var par = getPar();
if(par !== null) {
for(var i = 0; i < extensionTest.length; i++) {
if(extensionTest[i].test(par.href)) {
found = true;
}
}
if(!found) {
window.location = par.href;
}
else {
_image.src = par.href;
}
}
else {
/* blue */
var flashBlue = new YAHOO.util.ColorAnim(_image, {
backgroundColor: { to: '#06e'}
}, 0.30);
flashBlue.onComplete.subscribe(flashBack);
flashBlue.animate();
}
};

var stripPx = function(str) {
return parseInt(str.substring(0, str.indexOf('p')));
};

/* resizeImage is not used */
var resizeImage = function(ratio) {
var w = stripPx(Dom.getStyle(_item[_current], 'width')) * ratio;
var h = stripPx(Dom.getStyle(_item[_current], 'height')) * ratio;
Dom.setStyle(_item[_current], 'width', w + 'px');
Dom.setStyle(_item[_current], 'height', h + 'px');
};

var show = function() {
Dom.setStyle('imageViewer', 'display', '');
_imageViewer.show();
};

var hide = function() {
Dom.setStyle('imageViewer', 'display', 'none');
_imageViewer.hide();
};

var loaded = function() {
_item = YAHOO.util.Dom.getElementsBy(function(){return true;},'img');
if(_item.length !== 0) {
init();
}
};

return function() {
loader = new YAHOO.util.YUILoader();
loader.require('animation', 'container');
loader.loadOptional = true;
loader.insert(loaded);

return {
show : show,
hide : hide
};
}();
}();
}
Don't let me hog all the fun! Just drag these links to your bookmarks bar, or make a bookmarks group for "YUI Anywhere"; that may be a wise approach, because I feel that these types of things are going to get very popular.

YUI Loader

DW.imageViewer

Here is a direct link to the bookmark page.

Originally I wanted to only have to click one button, but it turned out to be complicated. Plus, the two buttons provide greater extendability. The first button prepares the loader to load in the scripts that other modules will need. Then, the DW.imageViewer button will simply load itself, nothing more.

In the code, you may notice that all quotes are single quotes or escaped single quotes, this was for when I minified it to make it a link.

DW.imageViewer :
I thought I would list some features:
  • It sets the mood by fading in a dark screen.
  • If there are no images on the page, it bluntly does nothing, bluntly.
  • It allows you next/previous your way through each image on the screen.
  • When you hover the mouse over the image it will flash one of three colors:
  • Green, the image was inside of a link
  • Clicking a green flashing image will load the linked image into the viewer
  • Yellow: The image links to an external page
  • Clicking a yellow flashing image will go to that page
  • Blue: When there is no link, the image will flash blue
  • To close the viewer, click close in the top left
  • To reopen it, click the bookmark again.
There you have it.

No comments: