Skip to content

Commit

Permalink
Added webcam plugin
Browse files Browse the repository at this point in the history
  • Loading branch information
ni-c committed Mar 3, 2013
1 parent 03c1533 commit a0c071d
Show file tree
Hide file tree
Showing 16 changed files with 517 additions and 69 deletions.
10 changes: 10 additions & 0 deletions heimcontrol.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,15 @@ requirejs([ 'http', 'connect', 'mongodb', 'path', 'express', 'node-conf', 'socke
});
});

var clientList = [];
io.sockets.on('connection', function(socket) {
clientList.push(socket.id);
socket.on('disconnect', function() {
var i = clientList.indexOf(socket.id);
clientList.splice(i,1);
});
});

// Express
app.configure(function() {
app.set('events', new Events.EventEmitter());
Expand All @@ -81,6 +90,7 @@ requirejs([ 'http', 'connect', 'mongodb', 'path', 'express', 'node-conf', 'socke
app.set('sockets', io.sockets);
app.set('mongo', Mongo);
app.set('db', db);
app.set('clients', clientList);
app.set('config', config);
app.set('plugin folder', __dirname + '/plugins');
app.set('plugin helper', new PluginHelper(app));
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"pi-gpio": "*",
"ping": ">= 0.1.1",
"wake_on_lan": ">= 0.0.3",
"delivery": ">= 0.0.3",
"duino": "https://github.com/ni-c/duino/tarball/master"
}
}
12 changes: 2 additions & 10 deletions plugins/arduino/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ define([ 'duino' ], function(duino) {
var that = this;

app.get('events').on('settings-saved', function() {
that.refresh();
that.init();
});

app.get('sockets').on('connection', function(socket) {
Expand All @@ -50,15 +50,6 @@ define([ 'duino' ], function(duino) {

};

/**
* Refreshes the plugin.
*
* @method refresh
*/
Arduino.prototype.refresh = function() {
return this.init();
};

/**
* Toggle an Arduino port
*
Expand Down Expand Up @@ -186,6 +177,7 @@ define([ 'duino' ], function(duino) {
}

var exports = Arduino;

return exports;

});
2 changes: 2 additions & 0 deletions plugins/arduino/views/settings.jade
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
div.container
h1 Settings
h2 Arduino
p Example Wirings: 
a(href="http://ni-c.github.com/heimcontrol.js/plugins/arduino.html") http://ni-c.github.com/heimcontrol.js/plugins/arduino.html
- if (typeof(success) !== 'undefined')
div.alert.alert-success.fade.in
button.close(type="button", data-dismiss="alert") ×
Expand Down
34 changes: 19 additions & 15 deletions plugins/gpio/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,25 +72,27 @@ define([ 'pi-gpio' ], function(gpio) {
*/
Gpio.prototype.parse = function() {
var that = this;
this.app.get('db').collection(this.collection, function(err, collection) {
collection.find({
direction: 'input'
}).toArray(function(err, result) {
result.forEach(function(item) {
gpio.setDirection(parseInt(item.pin), "input", function(err) {
gpio.read(parseInt(item.pin), function(err, value) {
if (!err) {
that.values[item._id] = value;
that.app.get('sockets').emit('gpio-input', {
id: item._id,
value: value
});
}
if (that.app.get('clients').length > 0) {
that.app.get('db').collection(this.collection, function(err, collection) {
collection.find({
direction: 'input'
}).toArray(function(err, result) {
result.forEach(function(item) {
gpio.setDirection(parseInt(item.pin), "input", function(err) {
gpio.read(parseInt(item.pin), function(err, value) {
if (!err) {
that.values[item._id] = value;
that.app.get('sockets').emit('gpio-input', {
id: item._id,
value: value
});
}
});
});
});
});
});
});
}
};

/**
Expand All @@ -110,6 +112,8 @@ define([ 'pi-gpio' ], function(gpio) {
return callback(null, items);
}

var exports = Gpio;

return Gpio;

});
2 changes: 2 additions & 0 deletions plugins/gpio/views/settings.jade
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
div.container
h1 Settings
h2 GPIO
p Installation Guide: 
a(href="http://ni-c.github.com/heimcontrol.js/plugins/gpio.html") http://ni-c.github.com/heimcontrol.js/plugins/gpio.html
- if (typeof(success) !== 'undefined')
div.alert.alert-success.fade.in
button.close(type="button", data-dismiss="alert") ×
Expand Down
22 changes: 13 additions & 9 deletions plugins/wakeonlan/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,19 +45,21 @@ define([ 'ping', 'wake_on_lan' ], function(ping, wol) {
*/
Wakeonlan.prototype.ping = function() {
var that = this;
this.app.get('db').collection(this.collection, function(err, collection) {
collection.find({}).toArray(function(err, result) {
result.forEach(function(item) {
ping.sys.probe(item.host, function(value) {
that.values[item._id] = value;
that.app.get('sockets').emit('wakeonlan-ping', {
id: item._id,
alive: value
if (that.app.get('clients').length > 0) {
that.app.get('db').collection(this.collection, function(err, collection) {
collection.find({}).toArray(function(err, result) {
result.forEach(function(item) {
ping.sys.probe(item.host, function(value) {
that.values[item._id] = value;
that.app.get('sockets').emit('wakeonlan-ping', {
id: item._id,
alive: value
});
});
});
});
});
});
}
};

/**
Expand Down Expand Up @@ -90,6 +92,8 @@ define([ 'ping', 'wake_on_lan' ], function(ping, wol) {
return callback(null, items);
}

var exports = Wakeonlan;

return Wakeonlan;

});
153 changes: 153 additions & 0 deletions plugins/webcam/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
if (typeof define !== 'function') {
var define = require('amdefine')(module);
}

/**
* Webcam Plugin. Plugin that uses streamer to create images from the webcam and pushes them to the clients
*
* @class Webcam
* @constructor
*/

define([ 'child_process', 'delivery', 'fs' ], function( ChildProcess, Delivery, Fs ) {

var Webcam = function(app) {

this.name = 'Webcam';
this.id = 'webcam';
this.collection = 'Webcam';
this.icon = 'icon-play-circle';

this.app = app;
this.pluginHelper = app.get('plugin helper');

this.webcamList = [];
this.webcams = {};

this.deliveryList = [];

this.init();

var that = this;

app.get('events').on('settings-saved', function() {
that.init();
});

app.get('sockets').on('connection', function(socket) {
var delivery = Delivery.listen(socket);
that.deliveryList.push(delivery);

socket.on('disconnect', function() {
that.deliveryList.forEach(function(delivery) {
if (delivery.socket.id == socket.id) {
var i = that.deliveryList.indexOf(delivery);
that.deliveryList.splice(i,1);
}
});
});

});
};

/**
* Initialize the webcams
*
* @method init
*/
Webcam.prototype.init = function() {

var that = this;
this.webcamList.forEach(function(webcam) {
clearInterval(webcam);
});
this.webcamList = [];

return this.app.get('db').collection(that.collection, function(err, collection) {
collection.find({}).toArray(function(err, result) {
if ((!err) && (result.length > 0)) {
result.forEach(function(item) {
function capture() {
if (that.app.get('clients').length > 0) {
var filename = '/tmp/' + item._id + '.jpeg';
that.streamer(item.input, filename, '1280x720', function(err, result) {
if (err) {
console.log(err);
} else {
that.deliveryList.forEach(function(delivery) {
delivery.send({
name: item._id + '.jpg',
path : filename,
});
});
}
});
}
}
var intervalId = setInterval(capture, parseInt(item.interval)*1000);
that.webcamList.push(intervalId);
});
}
});
});
}

/**
* Create an image using streamer
*
* @method streamer
* @param {String} input The input to use, e.g. '/dev/video0'
* @param {String} output The output file, e.g. '/tmp/image.jpg'
* @param {String} resolution The resolution to use, e.g. '1280x720'
* @param {Function} callback The callback method to execute after manipulation
* @param {String} callback.err null if no error occured, otherwise the error
* @param {Object} callback.result The result of the exec call
*/
Webcam.prototype.streamer = function(input, output, resolution, callback) {

var exec = ChildProcess.exec;
var cmd = 'streamer -c ' + input + ' -o ' + output + ' -s ' + resolution;
exec(cmd, function(err, stdout, stderr) {
if(err) {
callback(err);
} else {
callback(null, stdout);
}
});
}
/**
* Manipulate the items array before render
*
* @method beforeRender
* @param {Array} items An array containing the items to be rendered
* @param {Function} callback The callback method to execute after manipulation
* @param {String} callback.err null if no error occured, otherwise the error
* @param {Object} callback.result The manipulated items
*/
Webcam.prototype.beforeRender = function(items, callback) {
var that = this;
var devList = Fs.readdirSync('/dev/');
var deviceList = [];
devList.forEach(function(dev) {
if (dev.substr(0,5) == 'video') {
deviceList.push('/dev/' + dev);
}
})
items.forEach(function(item) {
console.log(deviceList.indexOf(item.input));
if (deviceList.indexOf(item.input)==-1) {
console.log('Webcam Plugin: Device "' + item.input + '" not found.');
var i = items.indexOf(item);
items.splice(i,1);
} else {
item.deviceList = deviceList;
}
});
return callback(null, items);
}

var exports = Webcam;

return Webcam;

});
3 changes: 3 additions & 0 deletions plugins/webcam/public/css/webcam.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.plugin-container.webcam:after {
content: "Webcam";
}
15 changes: 15 additions & 0 deletions plugins/webcam/public/js/webcam.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
require([ "jquery", "/socket.io/socket.io.js", "delivery" ], function() {

var socket = io.connect();


var delivery = new Delivery(socket);

delivery.on('receive.success',function(file) {
console.log(file);
if (file.isImage()) {
$('img[data-id="' + file.name + '"]').attr('src', file.dataURL());
};
});

});
7 changes: 7 additions & 0 deletions plugins/webcam/views/item.jade
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
each item in items
div.plugin-container.webcam
p
strong #{item.input} 
span.muted  Webcam
div.webcam-img
img(src="data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEASABIAAD//gATQ3JlYXRlZCB3aXRoIEdJTVD/2wBDAAIBAQEBAQIBAQECAgICAgQDAgICAgUEBAMEBgUGBgYFBgYGBwkIBgcJBwYGCAsICQoKCgoKBggLDAsKDAkKCgr/2wBDAQICAgICAgUDAwUKBwYHCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgr/wAARCALQBQADASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAn/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/8QAFAEBAAAAAAAAAAAAAAAAAAAAAP/EABQRAQAAAAAAAAAAAAAAAAAAAAD/2gAMAwEAAhEDEQA/AL+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//9k=", data-id="#{item._id}.jpg")
Loading

0 comments on commit a0c071d

Please sign in to comment.