create repo for 0x2620.org/jpg

This commit is contained in:
j 2013-12-30 16:11:08 +05:30
commit 2aadd63e2b
14 changed files with 7375 additions and 0 deletions

286
css/jpg.css Normal file
View file

@ -0,0 +1,286 @@
a {
color: rgb(255, 255, 255);
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
body {
margin: 0;
background: rgb(64, 64, 64);
font-family: Lucida Grande;
font-size: 10px;
text-align: center;
}
div, img {
-webkit-user-select: none;
}
div.button {
float: left;
width: 18px;
height: 18px;
border: 3px solid rgba(255, 255, 255, 1);
margin: 4px;
background: rgba(0, 0, 0, 0.5);
cursor: pointer;
-moz-border-radius: 12px;
-moz-box-shadow: 0 0 4px rgba(0, 0, 0, 1);
-webkit-border-radius: 12px;
-webkit-box-shadow: 0 0 4px rgba(0, 0, 0, 1);
}
div.button:hover {
background: rgba(0, 0, 0, 0.75);
}
div.background {
position: fixed;
left: 0;
top: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.25);
opacity: 0;
z-index: 1500;
}
div.dialog {
position: fixed;
left: 0;
top: 0;
right: 0;
bottom: 135px;
margin: auto;
width: 405px;
height: 170px;
opacity: 0;
-moz-border-radius: 8px;
-moz-box-shadow: 0 0 4px rgba(0, 0, 0, 1);
-webkit-border-radius: 8px;
-webkit-box-shadow: 0 0 4px rgba(0, 0, 0, 1);
}
div.dialog > .title {
width: 389px;
height: 14px;
padding: 5px 8px 5px 8px;
background: -moz-linear-gradient(top, rgb(80, 80, 80), rgb(48, 48, 48));
background: -webkit-gradient(linear, left top, left bottom, from(rgb(80, 80, 80)), to(rgb(48, 48, 48)));
font-family: Lucida Grande, Verdana, Calibri, Arial;
font-weight: bold;
font-size: 10px;
text-align: left;
color: rgb(192, 192, 192);
-moz-border-radius-topleft: 8px;
-moz-border-radius-topright: 8px;
-webkit-border-top-left-radius: 8px;
-webkit-border-top-right-radius: 8px;
}
div.dialog > .text {
width: 373px;
height: 90px;
padding: 16px;
background: rgb(64, 64, 64);
font-family: Lucida Grande, Verdana, Calibri, Arial;
font-weight: bold;
font-size: 10px;
text-align: left;
color: rgb(192, 192, 192);
}
div.dialog > .buttons {
width: 397px;
height: 24px;
padding: 0 4px 0 4px;
background: -moz-linear-gradient(top, rgb(80, 80, 80), rgb(48, 48, 48));
background: -webkit-gradient(linear, left top, left bottom, from(rgb(80, 80, 80)), to(rgb(48, 48, 48)));
-moz-border-radius-bottomleft: 8px;
-moz-border-radius-bottomright: 8px;
-webkit-border-bottom-left-radius: 8px;
-webkit-border-bottom-right-radius: 8px;
}
div.map {
position: fixed;
right: 8px;
bottom: 32px;
height: 90px;
border: 2px solid rgba(255, 255, 255, 1);
z-index: 500;
-moz-box-shadow: 0 0 4px rgba(0, 0, 0, 1);
-webkit-box-shadow: 0 0 4px rgba(0, 0, 0, 1);
}
div.map > .box {
position: absolute;
left: 0;
top: 0;
border: 2px solid rgba(255, 255, 255, 1);
-moz-box-shadow: 0 0 4px rgba(0, 0, 0, 1);
-webkit-box-shadow: 0 0 4px rgba(0, 0, 0, 1);
cursor: move;
}
div.map > .interface {
position: fixed;
right: 8px;
bottom: 32px;
height: 90px;
cursor: pointer;
}
div.input {
font-family: Lucida Grande, Verdana, Calibri, Arial;
font-weight: bold;
font-size: 10px;
color: rgb(192, 192, 192);
text-align: center;
}
div.input, div.select {
float: left;
width: 117px;
height: 12px;
padding: 1px 8px 1px 8px;
border: 1px solid rgb(128, 128, 128);
margin: 4px;
background: rgba(0, 0, 0, 0.25);
cursor: pointer;
-moz-border-radius: 8px;
-webkit-border-radius: 8px;
-moz-box-shadow: 0 0 4px rgba(0, 0, 0, 1);
-webkit-box-shadow: 0 0 4px rgba(0, 0, 0, 1);
}
div.input:hover, div.select:hover {
background: rgba(0, 0, 0, 0.5);
}
.buttons > div.input {
float: right;
width: 27px;
}
div.select > div {
width: 117px;
font-family: Lucida Grande, Verdana, Calibri, Arial;
font-weight: bold;
font-size: 10px;
text-align: left;
color: rgb(192, 192, 192);
}
img.button {
width: 12px;
height: 12px;
margin: 3px;
}
img.map {
height: 90px;
}
select {
position: relative;
left: -9px;
top: -17px;
width: 135px;
cursor: pointer;
opacity: 0;
}
.original {
position: fixed;
}
.photo {
background: rgb(128, 128, 128);
position: fixed;
-moz-box-shadow: 0 0 4px rgba(0, 0, 0, 1);
-webkit-box-shadow: 0 0 4px rgba(0, 0, 0, 1);
}
div.spinner {
position: fixed;
width: 24px;
height: 24px;
background: rgba(0, 0, 0, 0.25);
opacity: 0;
z-index: 250;
-moz-border-radius: 12px;
-webkit-border-radius: 12px;
}
img.spinner {
width: 18px;
height: 18px;
margin: 3px;
}
.thumbnail {
float: left;
//width: 135px;
height: 90px;
margin: 16px;
cursor: pointer;
-moz-box-shadow: 0 0 4px rgba(0, 0, 0, 1);
-webkit-box-shadow: 0 0 4px rgba(0, 0, 0, 1);
}
.thumbnail.loading {
background: rgb(128, 128, 128);
}
.tooltip {
position: fixed;
width: 135px;
bottom: 6px;
font-family: Lucida Grande, Verdana, Calibri, Arial;
font-weight: bold;
font-size: 10px;
color: rgb(192, 192, 192);
text-align: center;
text-shadow: rgb(0, 0, 0) 1px 1px 2px;
opacity: 0;
z-index: 1000;
}
#album {
//position: absolute;
//top: 0;
//width: auto;
//margin: 0 auto 36px auto;
padding: 12px;
display: table-cell;
}
#buttons {
position: fixed;
left: 0;
right: 0;
width: 160px;
height: 32px;
bottom: 24px;
padding: 4px;
margin: 0px auto 0px auto;
opacity: 0;
z-index: 1000;
//background: red;
}
#interface {
position: fixed;
left: 0;
top: 0;
right: 0;
bottom: 24px;
cursor: move;
display: none;
opacity: 0;
z-index: 500;
}
#layer {
position: fixed;
left: 0;
top: 0;
right: 0;
bottom: 24px;
background: rgb(64, 64, 64);
display: none;
opacity: 0;
}
#menu {
position: fixed;
left: 0;
bottom: 0;
right: 0;
height: 24px;
background: -moz-linear-gradient(top, rgb(80, 80, 80), rgb(48, 48, 48));
background: -webkit-gradient(linear, left top, left bottom, from(rgb(80, 80, 80)), to(rgb(48, 48, 48)));
//opacity: 0.9;
z-index: 500;
-moz-box-shadow: 0 0 4px rgba(0, 0, 0, 1);
-webkit-box-shadow: 0 0 4px rgba(0, 0, 0, 1);
}
#left {
position: fixed;
left: 4px;
}
#right {
position: fixed;
right: 4px;
}

17
index.html Normal file
View file

@ -0,0 +1,17 @@
<!DOCTYPE html>
<html>
<head>
<title>0x2620.org/jpg</title>
<link rel="shortcut icon" type="image/png" href="../png/0x2620.16.png"/>
<link rel="stylesheet" type="text/css" href="css/jpg.css"/>
<script type="text/javascript" src="js/jquery.js"></script>
<script type="text/javascript" src="js/jpg.js"></script>
</head>
<body>
<div id="album"></div>
<div id="menu">
<div id="left"></div>
<div id="right"></div>
</div>
</body>
</html>

767
js/jpg.js Normal file
View file

@ -0,0 +1,767 @@
/*
2010 0x2620.org GPL v3
TODO: migrate to ox.js
(& sorry for the mess)
*/
$(function() {
if ($.browser.msie) {
alert("Please use Firefox, Safari or Chrome.");
return;
}
var $body = $("body"),
$document = $(document),
$window = $(window),
$album = $("#album"),
json,
users = [],
albums = {},
split = location.hash.substr(1).replace("_", " ").split("/"),
user = split[0] || "S",
album = split[1] || "Beirut",
photoid = split[2] || "",
hash = photoid ? [user, album, photoid].join("/") : [user, album].join("/"),
windowWidth = $window.width(),
windowHeight = $window.height() - 24,
windowRatio = windowWidth / windowHeight,
photo = -1,
photos = [],
thumbnails = [],
playInterval,
buttonTimeout,
$layer,
spinner;
location.hash = hash;
$.each(["Loading", "Close", "Previous", "Play", "Pause", "Next", "ZoomIn", "ZoomOut"], function(i, name) {
$("<img>").attr({
src: "png/symbol" + name + ".png"
});
});
function Album(username, albumname) {
var that = this,
data = json[username][albumname];
that.show = function() {
playInterval && $("#play").trigger("click");
$("#zoomin").attr("src") == "png/symbolZoomOut.png" && $("#zoomin").trigger("click");
$(".photo") && $("#close").trigger("click");
$album.empty();
album = albumname;
photos = [];
photo = -1;
$.each(data, function(i, image) {
photos[i] = image,
thumbnails[i] = new Thumbnail(i).show();
});
$("<br/>").attr({clear: "all"}).appendTo($album);
$("<div>")
.css({
height: "24px"
})
.appendTo($album);
$interface = $("<div>")
.attr({
id: "interface"
})
.appendTo($album);
$layer = $("<div>")
.attr({
id: "layer"
})
.appendTo($album);
setTimeout(function() {
location.hash = [user, album.replace(" ", "_")].join("/");
}, 500);
return that;
}
return that;
}
function Button(photo, name, tooltip) {
var that = this,
$div = $("<div>")
.addClass("button")
.mouseenter(showTooltip)
.mouseleave(hideTooltip)
.click(function() {
hideTooltip();
photo.click(name);
}),
$img = $("<img>")
.addClass("button")
.attr({
id: name.toLowerCase().replace(" ", ""),
src: "png/symbol" + name.replace(" ", "") + ".png",
})
.appendTo($div),
$tooltip;
function hideTooltip() {
$tooltip.animate({
opacity: 0
}, 50, function() {
$tooltip.remove();
});
}
function showTooltip() {
$tooltip.html(that.tooltip).appendTo($body).animate({
opacity: 1
}, 50);
}
that.tooltip = tooltip;
that.show = function() {
$div.appendTo("#buttons");
$tooltip = $("<div>")
.addClass("tooltip")
.css({
left: ($div.offset().left - 52) + "px"
});
}
return that;
}
function Dialog(title, text, buttons) {
var that = this,
$div = $("<div>")
.addClass("background");
$dialog = $("<div>")
.addClass("dialog")
.appendTo($div);
$title = $("<div>")
.addClass("title")
.html(title)
.appendTo($dialog);
$text = $("<div>")
.addClass("text")
.html(text)
.appendTo($dialog);
$buttons = $("<div>")
.addClass("buttons")
.appendTo($dialog);
new Input("Close", function() {
that.close();
}, $buttons).show();
that.close = function() {
$div.animate({
opacity: 0
}, 250, function() {
$div.remove();
});
$dialog.animate({
opacity: 0
}, 250);
return that;
}
that.open = function() {
$div.appendTo($body).animate({
opacity: 1
}, 250);
$dialog.animate({
opacity: 1
}, 250);
return that;
}
return that;
}
function Input(name, fn, $parent) {
var that = this,
$parent = $parent || $("#right"),
$div = $("<div>")
.addClass("input")
.html(name)
.click(fn);
that.show = function() {
$parent.append($div);
return that;
}
return that;
}
function Map() {
var that = this,
offset = $(".original").offset(), // fixme: needed?
thumbnailWidth = 90 * photos[photo].width / photos[photo].height,
thumbnailHeight = 90,
boxWidth = thumbnailWidth / photos[photo].width * windowWidth,
boxHeight = thumbnailHeight / photos[photo].height * windowHeight,
$div = $("<div>")
.addClass("map")
.css({
width: thumbnailWidth + "px"
}),
$img = $("<img>")
.addClass("map")
.attr({
src: photos[photo].file.replace("/IMG", "/90/IMG"),
})
.css({
width: thumbnailWidth + "px"
})
.appendTo($div),
$interface = $("<div>")
.addClass("interface")
.css({
width: thumbnailWidth + "px"
})
.click(function(e) {
var offset = $div.offset(),
left = Math.min(Math.max(e.clientX - offset.left - boxWidth / 2 - 2, 0), thumbnailWidth - boxWidth),
top = Math.min(Math.max(e.clientY - offset.top + $body.scrollTop() - boxHeight / 2 - 2, 0), thumbnailHeight - boxHeight);
$(".original").animate({
left: -(left * photos[photo].width / thumbnailWidth) + "px",
top: -(top * photos[photo].height / thumbnailHeight) + "px"
}, 250, function() {
that.update(true);
});
})
.appendTo($div);
$box = $("<div>")
.addClass("box")
.css({
width: boxWidth + "px",
height: boxHeight + "px"
})
.mousedown(function(e) {
var left = parseInt($box.css("left")) + 2,
top = parseInt($box.css("top")) + 2,
x = e.clientX,
y = e.clientY;
$window.mousemove(function(e) {
left = Math.min(Math.max(left + e.clientX - x, 0), thumbnailWidth - boxWidth);
top = Math.min(Math.max(top + e.clientY - y, 0), thumbnailHeight - boxHeight);
x = e.clientX;
y = e.clientY;
$(".original").css({
left: -(left * photos[photo].width / thumbnailWidth) + "px",
top: -(top * photos[photo].height / thumbnailHeight) + "px"
});
that.update();
});
$window.mouseup(function() {
$window.unbind("mousemove");
});
})
.appendTo($div);
that.show = function() {
$div.appendTo($album);
that.update();
return that;
}
that.update = function(animate) {
var left = -parseInt($(".original").css("left")) * thumbnailWidth / photos[photo].width - 2,
top = -parseInt($(".original").css("top")) * thumbnailHeight / photos[photo].height - 2;
if (animate) {
$box.animate({
left: left + "px",
top: top + "px"
}, 250);
} else {
$box.css({
left: left + "px",
top: top + "px"
});
}
}
return that;
}
function Photo(i) {
var that = this,
image = photos[i],
id = image.file.substr(-8, 4),
title = image.file.substr(4, 8), // fixme: needed?
zoomedIn = false,
keepVisible = false,
$buttons = $("<div>")
.attr({
id: "buttons"
})
.mouseenter(function() {
//console.log("mouseenter")
clearTimeout(buttonTimeout);
keepVisible = true;
})
.mouseleave(function() {
buttonTimeout = setTimeout(hideButtons, 2500);
keepVisible = false;
})
.appendTo($body),
map;
$img = $("<img>")
.addClass("photo")
.attr({
src: image.file.replace("/IMG", "/90/IMG"),
});
$.each([
["Close", "CLOSE [ESCAPE]"],
["Previous", "PREVIOUS [LEFT]"],
["Play", "PLAY/PAUSE [SPACE]"],
["Next", "NEXT [RIGHT]"],
["Zoom In", "ZOOM [=/-]"]
], function(i, button) {
new Button(that, button[0], button[1]).show();
});
function getOriginalCSS(i) {
return {
width: photos[i].width + "px",
height: photos[i].height + "px"
};
}
function getPhotoCSS(i) {
var size = getSize(i);
return {
left: ((windowWidth - size.width) / 2) + "px",
top: ((windowHeight - size.height) / 2) + "px",
width: size.width + "px",
height: size.height + "px"
};
}
function getSize(i) {
var photoRatio = photos[i].width / photos[i].height;
return {
width: windowRatio > photoRatio ? windowHeight * photoRatio : windowWidth,
height: windowRatio > photoRatio ? windowHeight : windowWidth / photoRatio
};
}
function hideButtons() {
return;
//console.log("hideButtons")
$("#buttons").animate({
opacity: 0
}, 250);
}
function showButtons() {
//console.log("showButtons")
clearTimeout(buttonTimeout);
$("#buttons").animate({
opacity: 1
}, 250);
buttonTimeout = setTimeout(hideButtons, 2500);
}
that.click = function(button) {
//console.log("click " + button)
if (button == "Close") {
that.close();
} else if (button == "Previous") {
photo = photo == 0 ? photos.length - 1 : photo - 1;
that.show(photo);
playInterval && updateInterval();
} else if (button == "Play") {
if (!playInterval) {
$("#play").attr({
src: "png/symbolPause.png"
});
playInterval = setInterval(function() {
$("#next").trigger("click");
}, 5000);
setTimeout(function() {
$("#next").trigger("click");
}, 500);
} else {
$("#play").attr({
src: "png/symbolPlay.png"
});
clearInterval(playInterval);
playInterval = 0;
}
} else if (button == "Next") {
photo = photo == photos.length - 1 ? 0 : photo + 1;
that.show(photo);
playInterval && updateInterval();
} else if (button == "Zoom In") {
var left = (photos[photo].width - windowWidth) / -2,
top = (photos[photo].height - windowHeight) / -2,
x, y;
if (!zoomedIn) {
zoomedIn = true;
spinner = new Spinner().start();
var $img_ = $("<img>")
.attr({
src: photos[photo].file
})
.load(function() {
$img.attr({
src: photos[photo].file
});
spinner.stop();
});
$img
.animate({
width: photos[photo].width,
height: photos[photo].height,
left: left,
top: top
}, 250, function() {
$("#zoomin").attr({
src: "png/symbolZoomOut.png"
});
$img.addClass("original");
$interface
.show()
.mousedown(function(e) {
left = parseInt($img.css("left"));
top = parseInt($img.css("top"));
x = e.clientX;
y = e.clientY;
$window.mousemove(function(e) {
left = Math.max(Math.min(left + e.clientX - x, 0), windowWidth - photos[photo].width);
top = Math.max(Math.min(top + e.clientY - y, 0), windowHeight - photos[photo].height);
x = e.clientX;
y = e.clientY;
$img.css({
left: left + "px",
top: top + "px"
});
map.update();
});
$window.mouseup(function() {
$window.unbind("mousemove");
});
});
map = new Map().show();
});
} else {
zoomedIn = false;
$(".map").remove();
$interface.hide();
spinner = new Spinner().start();
var $img_ = $("<img>")
.attr({
src: photos[photo].file.replace("/IMG", "/720/IMG")
})
.load(function() {
$img.attr({
src: photos[photo].file.replace("/IMG", "/720/IMG")
});
spinner.stop();
});
$img.animate(getPhotoCSS(photo), 250, function() {
$("#zoomin").attr({
src: "png/symbolZoomIn.png"
});
});
}
}
function updateInterval() {
clearInterval(playInterval);
playInterval = setInterval(function() {
$("#next").trigger("click");
}, 5000);
}
return that;
}
that.close = function() {
var thumbnailPosition = thumbnails[photo].position();
if (playInterval) {
$("#play").trigger("click");
}
$("#buttons").remove();
$(".map").remove();
$img.animate({
left: thumbnailPosition.left,
top: thumbnailPosition.top,
width: thumbnailPosition.width,
height: thumbnailPosition.height
}, 250, function() {
location.hash = [user, album].join("/");
$img.unbind("mouseenter");
$img.unbind("mouseleave");
$img.unbind("mousemove");
$img.remove();
});
$layer.animate({
opacity: 0
}, 250, function() {
$layer.hide();
});
spinner.stop();
$interface.hide();
$document.unbind("keydown");
return that;
}
that.open = function() {
var thumbnailPosition = thumbnails[i].position();
$img.css({
left: thumbnailPosition.left,
top: thumbnailPosition.top,
width: thumbnailPosition.width,
height: thumbnailPosition.height
})
.appendTo($album)
.animate(getPhotoCSS(i), 250, function() {
location.hash = [user, album, id].join("/");
spinner = new Spinner().start();
var $img_ = $("<img>")
.attr({
src: photos[i].file.replace("/IMG", "/720/IMG")
})
.load(function() {
$img.attr({
src: photos[i].file.replace("/IMG", "/720/IMG")
});
spinner.stop();
});
/*
$img.mouseenter(showButtons)
.mouseleave(hideButtons)
.mousemove(function() {
if (!keepVisible) {
showButtons();
}
});
*/
showButtons();
$document.keydown(function(e) {
//console.log(e.keyCode);
var d = 0;
if (photo > -1) {
if (e.keyCode == 27) {
$("#close").trigger("click");
} else if (e.keyCode == 32) {
showButtons();
$("#play").trigger("click");
} else if (e.keyCode == 37) {
showButtons();
$("#previous").trigger("click");
} else if (e.keyCode == 39) {
showButtons();
$("#next").trigger("click");
} else if (e.keyCode == 187) {
showButtons();
!zoomedIn && $("#zoomin").trigger("click");
} else if (e.keyCode == 189) {
showButtons();
zoomedIn && $("#zoomin").trigger("click");
}
}
});
});
$layer.show().animate({
opacity: 1
}, 250);
photo = i;
return that;
}
that.remove = function() {
$img.remove();
return that;
}
that.show = function(i) {
var $imgOld = $img,
offset = $img.offset(),
size = {
width: $img.width(),
height: $img.height()
};
$img = $("<img>")
.addClass(zoomedIn ? "original" : "photo")
.attr({
src: photos[i].file.replace("/IMG", "/90/IMG"),
})
.css(zoomedIn ? $.extend(getOriginalCSS(i), {
left: offset.left + "px",
top: offset.top + "px"
}) : getPhotoCSS(i))
.css({
opacity: 0
})
.appendTo($album)
.animate({
opacity: 1
}, playInterval ? 500 : 250, function() {
location.hash = [user, album, photos[i].file.substr(-8, 4)].join("/");
$imgOld.remove();
photo = i;
if (zoomedIn) {
if (photos[i].width / size.width == size.width && photos[i].height == size.height) {
$("img.map")
.attr({
src: photos[photo].file.replace("/IMG", "/90/IMG"),
});
} else {
$img.css({
left: offset.left * (photos[i].width - windowWidth) / (size.width - windowWidth),
top: offset.top * (photos[i].height - windowHeight) / (size.height - windowHeight)
});
$("div.map").remove();
map = new Map().show();
}
}
});
spinner = new Spinner().start();
var $img_ = $("<img>")
.attr({
src: photos[photo].file.replace("/IMG", "/720/IMG")
})
.load(function() {
$img.attr({
src: photos[photo].file.replace("/IMG", "/720/IMG")
});
if (zoomedIn) {
$img_ = $("<img>")
.attr({
src: photos[photo].file
})
.load(function() {
$img.attr({
src: photos[photo].file
});
spinner.stop();
});
} else {
spinner.stop();
}
});
return that;
}
return that;
}
function Select(name, options, selected) {
var that = this,
$div = $("<div>")
.addClass("select");
$val = $("<div>")
.html(name + ": " + selected)
.appendTo($div);
$select = $("<select>")
.change(function() {
var val = $select.val()
$val.html(name + ": " + val);
new Album(user, val).show();
})
.appendTo($div);
$.each(options, function(i, option) {
var $option = $("<option>")
.html(option)
.appendTo($select);
option == selected && $option.attr({
selected: "selected"
});
});
that.show = function() {
$("#left").append($div);
return that;
}
return that;
}
function Spinner() {
var that = this,
$div = $("<div>")
.addClass("spinner")
.css({
left: ((windowWidth - 16 - 24) / 2) + "px",
top: ((windowHeight - 24) / 2) + "px",
}),
$img = $("<img>")
.addClass("spinner")
.attr({
src: "png/symbolLoading.png"
})
.appendTo($div),
deg = 0,
interval;
function update() {
$img.css({
MozTransform: "rotate(" + deg + "deg)",
WebkitTransform: "rotate(" + deg + "deg)"
});
}
that.start = function() {
var spinner_ = $(".spinner");
if (spinner_) {
spinner_.remove();
}
deg = 0;
$div.appendTo($album).animate({
opacity: 1
}, 250);
interval = setInterval(function() {
deg = (deg + 30) % 360;
update();
}, 83);
return that;
};
that.stop = function() {
$div.animate({
opacity: 0
}, 250, function() {
$div.remove();
clearTimeout(interval);
deg = 0;
});
return that;
}
return that;
}
function Thumbnail(i) {
var that = this,
image = photos[i],
width = image.width * 90 / image.height,
marginLeft = 16 + Math.floor((135 - width) / 2),
marginRight = 16 + Math.ceil((135 - width) / 2),
$img = $("<img>")
.addClass("thumbnail loading")
.attr({
id: image.file.substr(-8, 4),
src: image.file.replace("/IMG", "/90/IMG"),
})
.css({
width: width + "px",
marginLeft: marginLeft + "px",
marginRight: marginRight + "px"
})
.click(function() {
new Photo(i).open();
})
.load(function() {
$(this).removeClass("loading");
});
that.position = function() {
var offset = $img.offset();
return {
left: offset.left + 4,
top: offset.top + 4 - $body.scrollTop(),
width: $img.width(),
height: $img.height()
};
}
that.show = function() {
$img.appendTo($album);
return that;
}
return that;
}
$.getJSON("json/jpg.json", function(data) {
json = data;
$.each(data, function(username, albumnames) {
users.push(username);
albums[username] = [];
$.each(albumnames, function(albumname, photos) {
albums[username].push(albumname);
});
});
new Select("User", users, user).show();
new Select("Album", albums[user], album).show();
new Input("About: Photos", function() {
new Dialog("About: Photos", "2005-2010 Robert Luxemburg<br/><br/>Creative Commons Attribution Non-Commercial Share Alike 3.0").open();
}).show();
new Input("About: Software", function() {
new Dialog("About: Software", "2010 Robert Luxemburg<br/><br/>GNU General Public License 3.0<br/><br/><a href=\"zip/jpg.zip\">Download</a> (0.1.0, 2010-05-05, 150 KB)").open();
}).show();
new Album(user, album).show();
photoid && setTimeout(function() {
$("#" + photoid).trigger("click")
}, 500);
//$body.append("<br clear=\"all\"/>");
//$body.append("5DM2 - GPL V3 - <a href=\"5DM2.zip\">Download</a>");
});
});

6240
js/jquery.js vendored Normal file

File diff suppressed because it is too large Load diff

BIN
png/symbolClose.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

BIN
png/symbolLoading.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

BIN
png/symbolNext.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

BIN
png/symbolPause.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

BIN
png/symbolPlay.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

BIN
png/symbolPrevious.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

BIN
png/symbolZoomIn.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

BIN
png/symbolZoomOut.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

48
py/jpg.py Normal file
View file

@ -0,0 +1,48 @@
from __future__ import division
import os
import Image
import simplejson
sourcePath = "../jpg/"
json = {}
for dirname, dirs, files in os.walk(sourcePath):
for filename in files:
if len(filename) == 12 and filename[-4:] == ".JPG" and not "90" in dirname and not "720" in dirname:
split = dirname.split("/")
username = split[-2]
albumname = split[-1]
if not username in json:
json[username] = {}
if not albumname in json[username]:
json[username][albumname] = []
sourceFile = dirname + "/" + filename
sourceImage = Image.open(sourceFile)
sourceWidth = sourceImage.size[0]
sourceHeight = sourceImage.size[1]
print "reading " + sourceFile + " (" + str(sourceWidth) + "x" + str(sourceHeight) + ")"
for targetSize in [90, 720]:
targetPath = dirname + "/" + str(targetSize) + "/"
if not os.path.exists(targetPath):
os.makedirs(targetPath)
targetFile = targetPath + filename
if not os.path.exists(targetFile):
if sourceWidth > sourceHeight:
targetWidth = int(targetSize * sourceWidth / sourceHeight)
targetHeight = targetSize
else:
targetWidth = targetSize
targetHeight = int(targetSize * sourceHeight / sourceWidth)
print "writing " + targetFile + " (" + str(targetWidth) + "x" + str(targetHeight) + ")"
targetImage = Image.new("RGB", (targetWidth, targetHeight))
targetImage.paste(sourceImage.resize((targetWidth, targetHeight), Image.ANTIALIAS), (0, 0, targetWidth, targetHeight))
targetImage.save(targetFile)
json[username][albumname].append({
"file": sourceFile[3:],
"width": sourceWidth,
"height": sourceHeight
})
f = open("../json/jpg.json", "w")
f.write(simplejson.dumps(json, sort_keys=True, indent=4))
f.close()

17
readme.txt Normal file
View file

@ -0,0 +1,17 @@
0x2620.org/jpg 0.1.0 2010-05-05
a lightweight photo sharing web app
2010 robert luxemburg
published under the GPL v3
put your photos in jpg/username/albumname
and run jpg.py to create smaller versions
then put the whole thing on a web server
or just open index.html in your browser
works with firefox, safari and chrome
does not work with internet explorer
this software will evolve eventually, but for now it's just
intended to allow a few friends to drop picasa and/or flickr