<commit_before>var config = require(__dirname + "/config.js"),
    db = require(__dirname + "/database.js"),
    md5 = require(__dirname + "/md5.js"),
    utils = require(__dirname + "/utils.js"),
    mongodb = require("mongodb"),
    objectid = mongodb.BSONPure.ObjectID,
    datetime = require(__dirname + "/datetime.js"),
    errorcodes = require(__dirname + "/errorcodes.js").errorcodes;

var leaderboards = module.exports = {

    /**
     * Lists scores from a leaderboard table
     * @param options:  table, url, highest, mode, page, perpage, filters ass. array, friendslist,
     * @param callback function (error, errorcode, numscores, scores)
     */
    list:function (options, callback) {

        // defaults
        if(!options.page) {
            options.page = 1;
        }

        if(!options.perpage) {
            options.perpage = 20;
        }

        if(!options.highest && !options.lowest) {
            options.highest = true;
        }

        var query = {

            filter: {
                publickey: options.publickey,
                table: options.table
            },

            limit: options.perpage,
            skip: (options.page - 1) * options.perpage,
            sort: {},
            cache: true,
            cachetime: 120
        };

        // per-source website or device scores, websites
		// get truncated to domain.com
        if(options.source) {
            query.filter.source = options.source.indexOf("://") > -1 ? utils.baseurl(options.source) : options.source;
        }

        // filters for custom fields
        for(var x in options.filters) {
            query.filter["fields." + x] = options.filters[x];
        }

        // filtering for playerids
		var playerids = [];

        if(options.playerid && !options.excludeplayerid) {
            playerids.push(options.playerid);
        }

        if(options.friendslist) {
			playerids = playerids.concat(options.friendslist);
		}

		if(playerids.length > 0) {
            query.filter.playerid = { $in: playerids };
        }

        // date mode
		if(!options.mode) {
			options.mode = "alltime";
		} else {
			options.mode = options.mode.toLowerCase();
		}

        switch(options.mode) {
            case "today":
                query.filter.date = {"$gte": datetime.now - (24 * 60 * 60)};
                break;

           case "last7days":
               query.filter.date = {"$gte": (datetime.now - (7 * 24 * 60 * 60))};
                break;

            case "last30days":
                query.filter.date = {"$gte": (datetime.now - (30 * 24 * 60 * 60))};
                break;
        }

        // sorting
        if(options.mode == "newest") {
            query.sort = { date: -1 };
        } else {
            query.sort = { points: options.highest || !options.lowest ? -1 : 1 };
        }

        // the scores
        db.playtomic.leaderboard_scores.getAndCount(query, function(error, scores, numscores){

            if(error) {
                callback("unable to load scores: " + error + " (api.leaderboards.list:104)", errorcodes.GeneralError);
                return;
            }

            // clean up scores
            if(!scores) {
                scores = [];
            }

            callback(null, errorcodes.NoError, numscores, clean(scores, query.skip + 1));
        });
    },

    /**
     * Saves a score
     * @param options: url, name, points, auth, playerid, table, highest, allowduplicates, customfields ass. array
     * @param callback function(error, errorcode)
     */
    save: function(options, callback) {

        // defaults
        if(!options.source) {
            options.source = "";
        } else {
			options.source = options.source.indexOf("://") > -1 ? utils.baseurl(options.source) : options.source;
		}

        if(!options.playername) {
            callback("no name (" + options.playername + ")", errorcodes.InvalidName);
            return;
        }

        if(!options.table) {
            callback("no table name (" + options.table + ")", errorcodes.InvalidName);
            return;
        }

        if(!options.highest && !options.lowest) {
            options.highest = true;
        }

        // small cleanup
        var score = {};

        // fields that just aren't relevant, by doing it this way it's easier to extend because you can
        // just add more fields directly in your game and they will end up in your scores and returned
        // to your game
        var exclude = ["allowduplicates", "highest", "lowest", "numfields", "section", "action",
                        "ip", "date", "url", "rank", "points", "page", "perpage", "global", "filters", "debug"];

        for(var x in options) {
            if(exclude.indexOf(x) > -1) {
                continue;
            }

            score[x] = options[x];
        }

        score.hash = md5(options.publickey +
						 options.ip + "." +
                         options.table + "." +
                         options.playername + "." +
                         options.playerid + "." +
                         options.highest + "." +
                         options.source);
        score.points = options.points;
        score.date = datetime.now;

        // check bans

        // insert
        if(options.hasOwnProperty("allowduplicates") && options.allowduplicates) {

            db.playtomic.leaderboard_scores.insert({doc: score, safe: true}, function(error, item) {

                if(error) {
                    callback("unable to insert score: " + error + " (api.leaderboards.save:192)", errorcodes.GeneralError);
                    return;
                }

                callback(null, errorcodes.NoError, item._id);
            });

            return;
        }

        // check for duplicates, by default we will assume highest unless
        // lowest is explicitly specified
        var dupequery = {
            filter: {
                hash: score.hash
            },
            limit: 1,
            cache: false,
            sort: options.highest ? {score : -1 } : {score: 1 }
        };

        db.playtomic.leaderboard_scores.get(dupequery, function(error, items) {

            // no duplicates
            if(items.length == 0) {

                db.playtomic.leaderboard_scores.insert({doc: score}, function(error, item) {

                    if(error) {
                        callback("unable to insert score: " + error + " (api.leaderboards.save:212)", errorcodes.GeneralError);
                        return;
                    }

                    callback(null, errorcodes.NoError, item._id);
                });

                return;
            }

            // check if the new score is higher or lower
            var dupe = items[0];

            if((dupe.points <= score.points && options.highest) || (dupe.points >= score.points && options.lowest)) {

				score._id = dupe._id;

                var query = {
                    filter: { _id: dupe._id },
                    doc: score
                };

                db.playtomic.leaderboard_scores.update(query, function(error, item) {

                    if(error) {
                        callback("unable to update score: " + error + " (api.leaderboards.save:240)", errorcodes.GeneralError);
                        return;
                    }

                    callback(null, errorcodes.NoError, item._id);
                });
            } else {
                callback(null, errorcodes.NotBestScore);
            }
        });
    },

    saveAndList: function(options, callback) {

        leaderboards.save(options, function(error, errorcode, insertedid) {

            if(error) {
                callback(error + " (api.leaderboards.saveAndList:232)", errorcode);
                return;
            }

            // get scores before or after
            var query = {
                filter: {
                    publickey: options.publickey,
                    table: options.table
                },
                sort: options.highest ? {score : -1} : {score: 1},
                cache: true,
                cachetime: 120
            };

			if(options.playerid && !options.excludeplayerid) {
				query.filter.playerid = options.playerid;
			}

            if(options.highest || !options.lowest) {
                query.filter.points = {"$gte": options.points};
            } else {
                query.filter.points = {"$lte": options.points};
            }

			if(options.filters) {
	            for(var x in options.filters) {
	                query.filter["fields." + x] = options.filters[x];
	            }
			}

            if(options.friendslist) {
                if(options.friendslist.length > 100) {
                    options.friendslist.length = 100;
                }

                query.filter.playerid = { $in: options.friendslist }
            }

			if(options.source) {
				query.filter.source = options.source.indexOf("://") > -1 ? utils.baseurl(options.source) : options.source;
			}

            var serrorcode = errorcode;

            db.playtomic.leaderboard_scores.count(query, function(error, numscores) {

                if(error) {
                    callback(error + " (api.leaderboards.saveAndList:276)", errorcode);
                    return;
                }

				options.page = Math.ceil(numscores / options.perpage) ;

                leaderboards.list(options, function(error, errorcode, numscores, scores) {

                    if(error) {
                        callback(error + " (api.leaderboards.saveAndList:293)", errorcode);
                        return;
                    }

                    if(serrorcode > 0) {
                        errorcode = serrorcode;
                    }

					for(var i=0; i<scores.length; i++)
					{
						if(scores[i].scoreid == insertedid)
						{
							scores[i].submitted = true;
							break;
						}
					}

                    callback(null, errorcode, numscores, scores);
                })
            });
        });
    }
};

function clean(scores, baserank) {

    for(var i=0; i<scores.length; i++) {

        var score = scores[i];

        for(var x in score) {
            if(typeof(score[x]) == "String") {
                score[x] = utils.unescape(score[x]);
            }
        }

        for(var x in score.fields) {
            if(typeof(score.fields[x]) == "String") {
                score.fields[x] = utils.unescape(score.fields[x]);
            }
        }

        score.rank = baserank + i;
        score.scoreid = score._id.toString();
		score.rdate = utils.friendlyDate(utils.fromTimestamp(score.date));
        delete score._id;
        delete score.hash;
    }

    return scores;
}<commit_msg>Added an in-memory index for saveAndList to avoid count operations<commit_after>var config = require(__dirname + "/config.js"),
    db = require(__dirname + "/database.js"),
    md5 = require(__dirname + "/md5.js"),
    utils = require(__dirname + "/utils.js"),
    mongodb = require("mongodb"),
    objectid = mongodb.BSONPure.ObjectID,
    datetime = require(__dirname + "/datetime.js"),
    errorcodes = require(__dirname + "/errorcodes.js").errorcodes;

var leaderboards = module.exports = {

    /**
     * Lists scores from a leaderboard table
     * @param options:  table, url, highest, mode, page, perpage, filters ass. array, friendslist,
     * @param callback function (error, errorcode, numscores, scores)
     */
    list:function (options, callback) {

        // defaults
        if(!options.page) {
            options.page = 1;
        }

        if(!options.perpage) {
            options.perpage = 20;
        }

        if(!options.highest && !options.lowest) {
            options.highest = true;
        }

        var query = {

            filter: {
                publickey: options.publickey,
                table: options.table
            },

            limit: options.perpage,
            skip: (options.page - 1) * options.perpage,
            sort: {},
            cache: options.hasOwnProperty("cache") ? options.cache : true,
            cachetime: 120
        };

        // per-source website or device scores, websites
		// get truncated to domain.com
        if(options.source) {
            query.filter.source = options.source.indexOf("://") > -1 ? utils.baseurl(options.source) : options.source;
        }

        // filters for custom fields
        for(var x in options.filters) {
            query.filter["fields." + x] = options.filters[x];
        }

        // filtering for playerids
		var playerids = [];

        if(options.playerid && !options.excludeplayerid) {
            playerids.push(options.playerid);
        }

        if(options.friendslist) {
			playerids = playerids.concat(options.friendslist);
		}

		if(playerids.length > 0) {
            query.filter.playerid = { $in: playerids };
        }

        // date mode
		if(!options.mode) {
			options.mode = "alltime";
		} else {
			options.mode = options.mode.toLowerCase();
		}

        switch(options.mode) {
            case "today":
                query.filter.date = {"$gte": datetime.now - (24 * 60 * 60)};
                break;

           case "last7days":
               query.filter.date = {"$gte": (datetime.now - (7 * 24 * 60 * 60))};
                break;

            case "last30days":
                query.filter.date = {"$gte": (datetime.now - (30 * 24 * 60 * 60))};
                break;
        }

        // sorting
        if(options.mode == "newest") {
            query.sort = { date: -1 };
        } else {
            query.sort = { points: options.highest || !options.lowest ? -1 : 1 };
        }

        // the scores
        //console.log(JSON.stringify(query));

        db.playtomic.leaderboard_scores.getAndCount(query, function(error, scores, numscores){

            if(error) {
                callback("unable to load scores: " + error + " (api.leaderboards.list:104)", errorcodes.GeneralError);
                return;
            }

            // clean up scores
            if(!scores) {
                scores = [];
            }

            callback(null, errorcodes.NoError, numscores, clean(scores, query.skip + 1));
        });
    },

    /**
     * Saves a score
     * @param options: url, name, points, auth, playerid, table, highest, allowduplicates, customfields ass. array
     * @param callback function(error, errorcode)
     */
    save: function(options, callback) {

        // defaults
        if(!options.source) {
            options.source = "";
        } else {
			options.source = options.source.indexOf("://") > -1 ? utils.baseurl(options.source) : options.source;
		}

        if(!options.playername) {
            callback("no name (" + options.playername + ")", errorcodes.InvalidName);
            return;
        }

        if(!options.table) {
            callback("no table name (" + options.table + ")", errorcodes.InvalidName);
            return;
        }

        if(!options.highest && !options.lowest) {
            options.highest = true;
        }

        // small cleanup
        var score = {};

        // fields that just aren't relevant, by doing it this way it's easier to extend because you can
        // just add more fields directly in your game and they will end up in your scores and returned
        // to your game
        var exclude = ["allowduplicates", "highest", "lowest", "numfields", "section", "action",
                        "ip", "date", "url", "rank", "points", "page", "perpage", "global", "filters", "debug"];

        for(var x in options) {
            if(exclude.indexOf(x) > -1) {
                continue;
            }

            score[x] = options[x];
        }

        score.hash = md5(options.publickey +
						 options.ip + "." +
                         options.table + "." +
                         options.playername + "." +
                         options.playerid + "." +
                         options.highest + "." +
                         options.source);
        score.points = options.points;
        score.date = datetime.now;

        // check bans

        // insert
        if(options.hasOwnProperty("allowduplicates") && options.allowduplicates) {

            db.playtomic.leaderboard_scores.insert({doc: score, safe: true}, function(error, item) {

                if(error) {
                    callback("unable to insert score: " + error + " (api.leaderboards.save:192)", errorcodes.GeneralError);
                    return;
                }

                callback(null, errorcodes.NoError, item._id, item);
            });

            return;
        }

        // check for duplicates, by default we will assume highest unless
        // lowest is explicitly specified
        var dupequery = {
            filter: {
                hash: score.hash
            },
            limit: 1,
            cache: false,
            sort: options.highest ? {score : -1 } : {score: 1 }
        };

        db.playtomic.leaderboard_scores.get(dupequery, function(error, items) {

            // no duplicates
            if(items.length == 0) {

                db.playtomic.leaderboard_scores.insert({doc: score}, function(error, item) {

                    if(error) {
                        callback("unable to insert score: " + error + " (api.leaderboards.save:212)", errorcodes.GeneralError);
                        return;
                    }

                    callback(null, errorcodes.NoError, item._id, item);
                });

                return;
            }

            // check if the new score is higher or lower
            var dupe = items[0];

            if((dupe.points <= score.points && options.highest) || (dupe.points >= score.points && options.lowest)) {

				score._id = dupe._id;

                var query = {
                    filter: { _id: dupe._id },
                    doc: score
                };

                db.playtomic.leaderboard_scores.update(query, function(error, item) {

                    if(error) {
                        callback("unable to update score: " + error + " (api.leaderboards.save:240)", errorcodes.GeneralError);
                        return;
                    }

                    callback(null, errorcodes.NoError, item._id, item);
                });
            } else {
                callback(null, errorcodes.NotBestScore);
            }
        });
    },

    saveAndList: function(options, callback) {

        //console.log("---");

        leaderboards.save(options, function(error, errorcode, insertedid, insertedscore) {

            if(error) {
                callback(error + " (api.leaderboards.saveAndList:232)", errorcode);
                return;
            }

             if(options.lowest) {
                options.highest = false;
             }

            // get scores before or after
            var query = {
                filter: {
                    publickey: options.publickey,
                    table: options.table
                },
                sort: options.highest ? {points : -1} : {points: 1},
                cache: true,
                cachetime: 120
            };

			if(options.playerid && !options.excludeplayerid) {
				query.filter.playerid = options.playerid;
			}

			if(options.filters) {
	            for(var x in options.filters) {
	                query.filter["fields." + x] = options.filters[x];
	            }
			}

            if(options.friendslist) {
                if(options.friendslist.length > 100) {
                    options.friendslist.length = 100;
                }

                if(query.filter.playerid) {
                    options.friendslist.push(query.filter.playerid);
                }

                query.filter.playerid = { $in: options.friendslist }
            }

			if(options.source) {
				query.filter.source = options.source.indexOf("://") > -1 ? utils.baseurl(options.source) : options.source;
			}

            // index the freshly saved score
            manuallyIndexScore(query.filter, options.highest, options.points);
            var serrorcode = errorcode;

            rank(query.filter, options.highest, options.points, function(error, numscores)
            {
                options.page = Math.ceil((numscores + 1) / options.perpage);

                leaderboards.list(options, function(error, errorcode, numscores, scores) {

                    if(error) {
                        callback(error + " (api.leaderboards.saveAndList:293)", errorcode);
                        return;
                    }

                    if(serrorcode > 0) {
                        errorcode = serrorcode;
                    }

                    var foundsubmitted = false;
                    var i;

                    for(i=0; i<scores.length; i++)
                    {
                        if(scores[i].scoreid == insertedid)
                        {
                            scores[i].submitted = true;
                            foundsubmitted = true;
                        }
                    }

                    // we can miss if someone requested the page we're on and the next
                    // person got stuck with the cached results, for this case we find
                    // where they should be and inject them
                    var inserted = false;
                    if(!foundsubmitted) {
                        for(i=0; i<scores.length; i++) {

                            if( (options.highest && scores[i].points > insertedscore.points) ||
                               (!options.highest && scores[i].points < insertedscore.points)) {
                                continue;
                            }

                            inserted.splice(i, 0, insertedscore);
                            inserted = true;
                            break;
                        }

                        scores.pop();

                        if(!inserted) {
                            scores.push(insertedscore);
                        }
                    }

                    callback(null, errorcode, numscores, scores);
                });
            });
        });
    }
};

/**
 * Strips unnceessary data and tidies a score object
 */
function clean(scores, baserank) {

    for(var i=0; i<scores.length; i++) {

        var score = scores[i];

        for(var x in score) {
            if(typeof(score[x]) == "String") {
                score[x] = utils.unescape(score[x]);
            }
        }

        for(var x in score.fields) {
            if(typeof(score.fields[x]) == "String") {
                score.fields[x] = utils.unescape(score.fields[x]);
            }
        }

        score.rank = baserank + i;
        score.scoreid = score._id.toString();
		score.rdate = utils.friendlyDate(utils.fromTimestamp(score.date));
        delete score._id;
        delete score.hash;
    }

    return scores;
}

/*
 * Gets the rank of a score based on its filtering
 * either from an existing index, or manually while
 * it creates a new index
 */
function rank(filter, highest, points, callback) {

    // no indexes for individual players
    if(filter.playerid) {
        return rankManual(filter, highest, points, callback);
    }

    var hash = md5(JSON.stringify(filter) + "." + highest);
    var i;

    // the index itself exists, we check if we have the score
    // and if not we add it to the index
    if(index[hash]) {

        var arr = index[hash];

        /*var totalscores = 0;

        for(i=0; i<arr.length; i++) {
            totalscores += arr[i].scores;
            console.log(JSON.stringify(arr[i]));
        }*/

        var before = 0;
        var found = false;

        for(i=0; i<arr.length; i++) {

            if((highest && arr[i].points > points) || (!highest && arr[i].points < points)) {
                before += arr[i].scores;
                continue;
            }

            found = true;
            break;
        }

        if(!found) {
            manuallyIndexScore(filter, highest, points);
        }

        return callback(null, before);
    }

    // check if we already have this index queued
    var found = false;
    for(i=0; i<indexes.length; i++) {
        if(indexes[i].key == hash) {
            found = true;
            break;
        }
    }

    // create the inde
    if(!found) {
        indexes.push({
            key: hash,
            filter: filter,
            highest: highest,
            lastupdated: 0,
            lastcheck: 0
        });
    }

    // manually get this score's rank
    return rankManual(filter, highest, points, callback);
}

/**
 * Manually counts the scores that occured
 * before another one, this should only happen
 * the first few times while the leaderboard
 * index is being built
 */
function rankManual(filter, highest, points, callback) {
    filter = JSON.parse(JSON.stringify(filter));
    filter.points = highest ? { $gte: points } : { $lte: points };

    db.playtomic.leaderboard_scores.count({ filter: filter }, function(error, numscores) {
        return callback(error, numscores);
    });
}

/**
 * Adds a score to our index manually which is eventually
 * removed when the db is re-polled
 */
function manuallyIndexScore(filter, highest, points) {

    var hash = md5(JSON.stringify(filter) + "." + highest);

    if(!index[hash]) {
        return;
    }

    var arr = index[hash];
    var inserted = false;
    var i;

    for(i=0; i<arr.length; i++) {

        if((highest && arr[i].points > points) || (!highest && arr[i].points < points)) {
            continue;
        }

        if(arr[i].points == points) {
            arr[i].scores++;
        } else {
            arr.splice(i, 0, { points: points, scores: 1});
        }

        inserted = true;
        break;
    }

    if(!inserted) {
        arr.push({points: points, scores: 1});
    }

    // add the delete to the index
    for(i=0; i<indexes.length; i++) {
        if(indexes[i].key != hash) {
            continue;
        }

        if(!indexes[i].delete) {
            indexes[i].delete = [];
        }

        indexes[i].delete.push(points)
    }
}

var index = {};
var indexes = [];

(function() {

    function load() {

        if(indexes.length == 0) {
            return setTimeout(load, 5000);
        }

        // sort by last updated]
        indexes.sort(function(a, b) {
            return a.lastupdated < b.lastupdated ? 1 : -1;
        });

        // most recent data is less than 30 secounds old
        if(datetime.now - indexes[0].lastcheck < 30) {
            return setTimeout(load, 1000);
        }

        var zindex = indexes[0];
        var pindex = index[zindex.key] || [];

        zindex.lastcheck = datetime.now;

        var query = {
            filter: zindex.filter,
            fields: {
                points: 1,
                date: 1
            },

            sort: { points: zindex.highest ? -1 : 1 }
        };

        query.filter.date = {$gt: zindex.lastupdated };

        var pk = zindex.key;

        db.playtomic.leaderboard_scores.get(query, function(error, scores) {

            if(error) {
                if(callback) {
                    callback(error);
                }
                return setTimeout(load, 1000);
            }

            if(scores.length == 0) {
                return setTimeout(load, 1000);
            }

            var ind = {};
            var i;
            var score;

            // fold the existing data back into our new index
            // because the queries only return partial datasets
            for(i=0; i<pindex.length; i++) {
                ind[pindex[i].points] = pindex[i];
            }

            // delete anything we manually added
            if(zindex.delete && zindex.delete.length) {
                for(i=0; i<zindex.delete.length; i++) {
                    //console.log("deleting", zindex.delete[i], JSON.stringify(ind[zindex.delete[i]]));;
                    ind[zindex.delete[i]].scores--;
                }

                zindex.delete.length = 0;
            }

            // add the new data
            for(i=0; i<scores.length; i++)  {
                score = scores[i];

                if(!ind[score.points]) {
                    ind[score.points] = { points: score.points, scores: 1 };
                } else {
                    ind[score.points].scores++;
                }

                if(score.date > zindex.lastupdated) {
                    zindex.lastupdated = score.date;
                }
            }

            // pull it back out into a sorted array
            var arr = [];

            for(x in ind) {
                arr.push(ind[x]);
            }

            //console.log("new index size is", arr.length);

            arr.sort(function(a, b) {
                return zindex.highest
                    ? (a.points < b.points ? 1 : -1)
                    : (a.points > b.points ? 1 : -1);
            });

            //console.log("LEADERBOARD ORDER", "highest=" + zindex.highest, arr[0].points, arr[1].points);

            index[pk] = arr;
            setTimeout(load, 1000);
        });
    }

    load();
})();<|endoftext|>


<commit_before>var subSocket = null;

var mPendingUnits = [];

var mLoadModal = {
    pendingUnit: {},
    cert: null,
    certs: [
        { name: "a.cert" },
        { name: "b.cert" }
    ],
    passphrase: null,
    decrypting: false,
    nextEnabled: true,
    destinations: [
        "Holding",
        "Pre-Ingest",
        "Holding + Sandbox"
    ]
};

//Controller for displaying Pending Units grid
function PendingUnitsCtrl($scope) {
    $scope.pendingUnits = mPendingUnits;

    $scope.loadDialog = function(pendingUnit) {
        //update the model
        mLoadModal.pendingUnit = pendingUnit;

        //show the load modal
        $('#loadModal').modal('show');
    };
}

//Controller for the Load modal dialog
function LoadModalCtrl($scope, $http) {
    $scope.loadModal = mLoadModal;

   updateCertsModel($http);

    $scope.uploadCertDialog = function() {
        //show the uploadCert modal
        $('#uploadCertModal').modal('show');
    };

    $scope.next = function() {
      var selected = $('#LoadWizard').wizard('selectedItem').step;

      if(selected == 1) {
        //changed "Decrypt" -> "Review Unit"

        //instruct the server to decrypt the unit
        decrypt(mLoadModal.pendingUnit, mLoadModal.cert, mLoadModal.passphrase);

        //show waiting for decrypt
        mLoadModal.decrypting = true;
      }

      if(selected == 2) {
        mLoadModal.nextEnabled = false;
      }

      //$('#LoadWizard').wizard('selectItem', { step: selectedIdx + 1 });
      $('#LoadWizard').wizard('next');
    };

    $scope.startLoad = function () {

        //disable the load button on the main dialog for the unit
        //TODO

        //send message to initiate load
        doStartLoad(mLoadModal.pendingUnit, mLoadModal.cert, mLoadModal.passphrase);

        //hide the load button for this unit and show the progress bar for the load
        updatePending(function(model) {
            $.each(model, function(i, v) {
                if(v.src == mLoadModal.pendingUnit.src) {
                    v.showComplete = true;
                }
            });
        });

        //close modal dialog
        $('#loadModal').modal('hide');

        //reset dialog
        mLoadModal.nextEnabled = true; //re-enable for next time
    };
}

function expandPendingUnitParts(pendingUnit) {
  var parts = [];
  var unitName = pendingUnit.label;
  $.each(pendingUnit.parts, function(i, v) {
    parts.push({
      unit: unitName,
      series: v,
      destination: ''
    });
  });
  return parts;
}

function doStartLoad(pendingUnit, cert, pass) {

  subSocket.push(JSON.stringify({
    action: 'load',
    unit: {
        interface: pendingUnit.interface,
        src: pendingUnit.src,
        label: pendingUnit.label,
        part: parts
    },
    certificate: cert.name,
    passphrase: pass
  }));
};

function decrypt(pendingUnit, cert, pass) {
  subSocket.push(JSON.stringify({
    action: 'decrypt',
    unit: {
        uid: pendingUnit.uid
    },
    certificate: cert.name,
    passphrase: pass
  }));
};

function updateCertsModel(http) {
    http({method: "GET", url: "/certificate"})
        .success(function(data, status, headers, config) {
            mLoadModal.certs.length = 0;
            $.each(data.certificate, function(i, v) {
                mLoadModal.certs.push(v);
            });
        })
        .error(function(data, status, headers, config) {
           alert("error $http = " + data);
        });
}

//Controller for the UploadCert modal dialog
function UploadCertModalCtrl($scope, $http) {
    $scope.selectedFiles = [];
    $scope.progress = 0;
    $scope.showProgress = false;

    //updates the available certs in the model (for the LoadModalCtrl)
    $scope.reloadCerts = function() {
      updateCertsModel($http);
    };

    $scope.setFiles = function(fileInputElem) {
        $scope.$apply(function($scope) {
            // Turn the FileList object into an Array in the model
            $scope.selectedFiles.length = 0;
            $.each(fileInputElem.files, function(i, v) {
                $scope.selectedFiles.push(v);
            });
            $scope.progress = 0;
        })
    };

    $scope.displaySize = function(size) {
        return toHumanSize(size);
    };

    $scope.upload = function() {

        var fd = new FormData();
        for(var i in $scope.selectedFiles) {
            fd.append("certificate", $scope.selectedFiles[i]);
        }
        var xhr = new XMLHttpRequest();

        xhr.upload.addEventListener("progress", function(evt){
            $scope.$apply(function(){
                if(!$scope.showProgress) {
                    $scope.showProgress = true;
                }
                if(evt.lengthComputable) {
                    $scope.progress = Math.round(evt.loaded * 100 / evt.total);
                } else {
                    console.log("Error: unable to compute length");
                }
            });
        }, false);

        xhr.addEventListener("load", function(evt){
            alert(evt.target.responseText)
        }, false);

        xhr.addEventListener("error",function(evt){
           alert("There was an error attempting to upload the file.")
        }, false);

        xhr.addEventListener("abort", function(evt){
            alert("The upload has been canceled by the user or the browser dropped the connection.")
        }, false);

        xhr.open("POST", "/certificate");
        xhr.send(fd);

        /* WebSocket version below is not working with
        scalatra-atmosphere as it receives the binarymessage
        as a text message. scalatra-atmosphere needs to be fixed
        so that it sees the message as binary
        */

        /*var socket = $.atmosphere;
        var subSocket;
        var transport = 'websocket';

        var request = {
            url: "/certificate/" + $scope.selectedFiles[0].name,
            method: "POST",
            webSocketBinaryType: "arraybuffer",
            contentType: "application/octet-stream",
            logLevel: "debug",
            transport: transport,
            fallbackTransport: "long-polling"
        };

        request.onOpen = function(response) {
            console.log('Atmosphere connected using ' + response.transport);

            transport = response.transport;

            if (response.transport == "local") {
              subSocket.pushLocal("Name?");
            }

            //send file data
            var reader = new FileReader();
            reader.onload = function(evt) {
                request.data = evt.target.result;
                subSocket.push(request);
                //subSocket.push(evt.target.result);
            };

            //TODO send all files
            reader.readAsArrayBuffer($scope.selectedFiles[0]);
        };

        subSocket = socket.subscribe(request);*/
    };
}

function updateLoadModal(fnUpdateModel) {
    updateModel("#loadModal", fnUpdateModel, mLoadModal)
}

function updatePending(fnUpdateModel) {
    updateModel("#pendingUnits", fnUpdateModel, mPendingUnits)
}

function updateModel(ctrlElemId, fnUpdateModel, m) {
    var ctrlElem = $(ctrlElemId);
    var scope = angular.element(ctrlElem).scope();

      scope.$apply(function() {
          fnUpdateModel(m);
      });
}

function toHumanSize(bytes) {
  var KB = 1024;
  var MB = Math.pow(KB, 2);
  var GB = Math.pow(KB, 3);
  var TB = Math.pow(KB, 4);

  if(bytes / TB > 1) {
     return Math.ceil(bytes / TB) + " TB";
  } else if(bytes / GB > 1) {
     return Math.ceil(bytes / GB) + " GB";
  } else if(bytes / MB > 1) {
    return Math.ceil(bytes / MB) + " MB";
  } else if(bytes / KB > 1) {
    return Math.ceil(bytes / KB) + " KB";
  } else {
    return bytes + " bytes";
  }
}

function toHumanTime(timestamp) {
   var d = new Date(timestamp);
   //return d.format("D, dS M Y @ H:i e");
   return d.format("D, dS M Y @ H:i") + " " + d.toString().replace(/[^\(]+(\(.+\))/, "$1"); //workaround to add timezone as 'e' in above code seems broken in date.js library
}

function copyUnitProperties(srcUnit, destUnit) {
   destUnit.label = srcUnit.label;
   destUnit.size = srcUnit.size;
   destUnit.timestamp = srcUnit.size;
   destUnit.parts = srcUnit.parts;
   //TODO update action and progress of action!
}

$(document).ready(function() {
  "use strict";

  var socket = $.atmosphere;
  var transport = 'websocket';

  var request = {
    url: "/unit",
    contentType: "application/json",
    logLevel: 'debug',
    transport: transport,
    fallbackTransport: 'long-polling'
  };

  request.onOpen = function(response) {
    console.log('Atmosphere connected using ' + response.transport);

    transport = response.transport;

    if (response.transport == "local") {
      subSocket.pushLocal("Name?");
    }

    //request initial unit status
    subSocket.push(JSON.stringify({action: 'pending'}));
  };

  request.onReconnect = function(request, response) {
    socket.info("Reconnecting");
    console.log("Reconnecting...");
  };

  request.onMessage = function(response) {
    var message = response.responseBody;
      try {
          var json = JSON.parse(message);

          //is this an add/update of unit status?
          if(json.update) {
             updatePending(function(model) {
                $.each(json.update.unit, function(i, v) {

                     //format for display
                     v.size = toHumanSize(v.size);
                     v.timestamp = toHumanTime(v.timestamp);

                     //does the model already contain details of this unit?
                     var existingIdx = -1;
                     for(var i = 0; i < model.length; i++) {
                        if(model[i].uid == v.uid) {
                            existingIdx = i;
                            break;
                        }
                     }

                     if(existingIdx > -1) {
                        //do update
                        copyUnitProperties(v, model[existingIdx]);
                     } else {
                        //create new
                        model.push(v);
                     }
                 });
             });

             //update the unit detail part of the load modal dialog
             $.each(json.update.unit, function(i, v) {
                 updateLoadModal(function(model) {
                     if(v.uid == model.pendingUnit.uid) {
                         model.pendingUnit = v;
                         //has part discovery completed? if so disable decrypting message
                         if(v.parts) {
                             model.pendingUnit.parts = expandPendingUnitParts(v);
                             model.decrypting = false;    //hide decrypting message
                         }
                     }
                 });
             });

          //is this a removal from the pending units?
          } else if(json.remove) {
           updatePending(function(model) {
              $.each(model, function(i, v) {
               $.each(json.remove.unit, function(y, vv) {
                  if((!(v === undefined)) && v.uid == vv.uid) {
                      model.splice(i, 1);
                  }
               });
              });
           });
          }

          //is this a pending unit initial status
//          if(json.pending) {
//            updatePending(function(model) {
//                model.length = 0; //empty the model
//                $.each(json.pending.unit, function(i, v) {
//                    v.size = toHumanSize(v.size);
//                    v.timestamp = toHumanTime(v.timestamp);
//                    model.push(v); //add to the model
//                });
//            });
//
//          //is this an addition to the pending units
//          } else if(json.pendingAdd) {
//            updatePending(function(model) {
//                $.each(json.pendingAdd.unit, function(i, v) {
//                    v.size = toHumanSize(v.size);
//                    v.timestamp = toHumanTime(v.timestamp);
//                    model.push(v); //add to the model
//                });
//            });
//
//            //update the unit detail part of the load modal dialog
//            $.each(json.pendingAdd.unit, function(i, v) {
//                updateLoadModal(function(model) {
//                    if(v.src == model.pendingUnit.src) {
//                        model.pendingUnit = v;
//                        //has part discovery completed? if so disable decrypting message
//                        if(v.part) {
//                            model.decrypting = false;    //hide decrypting message
//                        }
//                    }
//                });
//            });
//
//          //is this a removal from the pending units
//          } else if(json.pendingRemove) {
//            updatePending(function(model) {
//               $.each(model, function(i, v) {
//                $.each(json.pendingRemove.unit, function(y, vv) {
//                   if((!(v === undefined)) && v.src == vv.src) {
//                       model.splice(i, 1);
//                   }
//                });
//               });
//            });
//          }
//
//          //is this a UnitLoadStatus update?
//          else if(json.loadStatus) {
//            updatePending(function(model) {
//              $.each(model, function(i, v) {
//                if(v.src == json.loadStatus.unit.src) {
//                    v.complete = json.loadStatus.complete;
//                }
//              });
//            });
//          }

      } catch (e) {
          console.log('Error: ', message.data);
          return;
      }

    console.debug("Received message: " + json.toString());
  };

  request.onClose = function(response) {
    console.log("Connection closed! " + response.responseBody)
  };

  request.onError = function(response) {
    console.log("Error: " + response.responseBody);
  };

  subSocket = socket.subscribe(request);

});<commit_msg>More parts tinkering<commit_after>var subSocket = null;

var mPendingUnits = [];

var mLoadModal = {
    pendingUnit: {},
    cert: null,
    certs: [
        { name: "a.cert" },
        { name: "b.cert" }
    ],
    passphrase: null,
    decrypting: false,
    nextEnabled: true,
    destinations: [
        "Holding",
        "Pre-Ingest",
        "Holding + Sandbox"
    ]
};

//Controller for displaying Pending Units grid
function PendingUnitsCtrl($scope) {
    $scope.pendingUnits = mPendingUnits;

    $scope.loadDialog = function(pendingUnit) {
        //update the model
        mLoadModal.pendingUnit = pendingUnit;

        //show the load modal
        $('#loadModal').modal('show');
    };
}

//Controller for the Load modal dialog
function LoadModalCtrl($scope, $http) {
    $scope.loadModal = mLoadModal;

   updateCertsModel($http);

    $scope.uploadCertDialog = function() {
        //show the uploadCert modal
        $('#uploadCertModal').modal('show');
    };

    $scope.next = function() {
      var selected = $('#LoadWizard').wizard('selectedItem').step;

      if(selected == 1) {
        //changed "Decrypt" -> "Review Unit"

        //instruct the server to decrypt the unit
        decrypt(mLoadModal.pendingUnit, mLoadModal.cert, mLoadModal.passphrase);

        //show waiting for decrypt
        mLoadModal.decrypting = true;
      }

      if(selected == 2) {
        mLoadModal.nextEnabled = false;
      }

      //$('#LoadWizard').wizard('selectItem', { step: selectedIdx + 1 });
      $('#LoadWizard').wizard('next');
    };

    $scope.startLoad = function () {

        //disable the load button on the main dialog for the unit
        //TODO

        //send message to initiate load
        doStartLoad(mLoadModal.pendingUnit, mLoadModal.cert, mLoadModal.passphrase);

        //hide the load button for this unit and show the progress bar for the load
        updatePending(function(model) {
            $.each(model, function(i, v) {
                if(v.src == mLoadModal.pendingUnit.src) {
                    v.showComplete = true;
                }
            });
        });

        //close modal dialog
        $('#loadModal').modal('hide');

        //reset dialog
        mLoadModal.nextEnabled = true; //re-enable for next time
    };
}

function expandPendingUnitParts(pendingUnit) {
  var parts = [];
  var unitName = pendingUnit.label;
  $.each(pendingUnit.parts, function(i, v) {
    parts.push({
      unit: unitName,
      series: v,
      destination: ''
    });
  });
  return parts;
}

function doStartLoad(pendingUnit, cert, pass) {

  subSocket.push(JSON.stringify({
    action: 'load',
    unit: {
        interface: pendingUnit.interface,
        src: pendingUnit.src,
        label: pendingUnit.label,
        part: pendingUnit.parts
    },
    certificate: cert.name,
    passphrase: pass
  }));
};

function decrypt(pendingUnit, cert, pass) {
  subSocket.push(JSON.stringify({
    action: 'decrypt',
    unit: {
        uid: pendingUnit.uid
    },
    certificate: cert.name,
    passphrase: pass
  }));
};

function updateCertsModel(http) {
    http({method: "GET", url: "/certificate"})
        .success(function(data, status, headers, config) {
            mLoadModal.certs.length = 0;
            $.each(data.certificate, function(i, v) {
                mLoadModal.certs.push(v);
            });
        })
        .error(function(data, status, headers, config) {
           alert("error $http = " + data);
        });
}

//Controller for the UploadCert modal dialog
function UploadCertModalCtrl($scope, $http) {
    $scope.selectedFiles = [];
    $scope.progress = 0;
    $scope.showProgress = false;

    //updates the available certs in the model (for the LoadModalCtrl)
    $scope.reloadCerts = function() {
      updateCertsModel($http);
    };

    $scope.setFiles = function(fileInputElem) {
        $scope.$apply(function($scope) {
            // Turn the FileList object into an Array in the model
            $scope.selectedFiles.length = 0;
            $.each(fileInputElem.files, function(i, v) {
                $scope.selectedFiles.push(v);
            });
            $scope.progress = 0;
        })
    };

    $scope.displaySize = function(size) {
        return toHumanSize(size);
    };

    $scope.upload = function() {

        var fd = new FormData();
        for(var i in $scope.selectedFiles) {
            fd.append("certificate", $scope.selectedFiles[i]);
        }
        var xhr = new XMLHttpRequest();

        xhr.upload.addEventListener("progress", function(evt){
            $scope.$apply(function(){
                if(!$scope.showProgress) {
                    $scope.showProgress = true;
                }
                if(evt.lengthComputable) {
                    $scope.progress = Math.round(evt.loaded * 100 / evt.total);
                } else {
                    console.log("Error: unable to compute length");
                }
            });
        }, false);

        xhr.addEventListener("load", function(evt){
            alert(evt.target.responseText)
        }, false);

        xhr.addEventListener("error",function(evt){
           alert("There was an error attempting to upload the file.")
        }, false);

        xhr.addEventListener("abort", function(evt){
            alert("The upload has been canceled by the user or the browser dropped the connection.")
        }, false);

        xhr.open("POST", "/certificate");
        xhr.send(fd);

        /* WebSocket version below is not working with
        scalatra-atmosphere as it receives the binarymessage
        as a text message. scalatra-atmosphere needs to be fixed
        so that it sees the message as binary
        */

        /*var socket = $.atmosphere;
        var subSocket;
        var transport = 'websocket';

        var request = {
            url: "/certificate/" + $scope.selectedFiles[0].name,
            method: "POST",
            webSocketBinaryType: "arraybuffer",
            contentType: "application/octet-stream",
            logLevel: "debug",
            transport: transport,
            fallbackTransport: "long-polling"
        };

        request.onOpen = function(response) {
            console.log('Atmosphere connected using ' + response.transport);

            transport = response.transport;

            if (response.transport == "local") {
              subSocket.pushLocal("Name?");
            }

            //send file data
            var reader = new FileReader();
            reader.onload = function(evt) {
                request.data = evt.target.result;
                subSocket.push(request);
                //subSocket.push(evt.target.result);
            };

            //TODO send all files
            reader.readAsArrayBuffer($scope.selectedFiles[0]);
        };

        subSocket = socket.subscribe(request);*/
    };
}

function updateLoadModal(fnUpdateModel) {
    updateModel("#loadModal", fnUpdateModel, mLoadModal)
}

function updatePending(fnUpdateModel) {
    updateModel("#pendingUnits", fnUpdateModel, mPendingUnits)
}

function updateModel(ctrlElemId, fnUpdateModel, m) {
    var ctrlElem = $(ctrlElemId);
    var scope = angular.element(ctrlElem).scope();

      scope.$apply(function() {
          fnUpdateModel(m);
      });
}

function toHumanSize(bytes) {
  var KB = 1024;
  var MB = Math.pow(KB, 2);
  var GB = Math.pow(KB, 3);
  var TB = Math.pow(KB, 4);

  if(bytes / TB > 1) {
     return Math.ceil(bytes / TB) + " TB";
  } else if(bytes / GB > 1) {
     return Math.ceil(bytes / GB) + " GB";
  } else if(bytes / MB > 1) {
    return Math.ceil(bytes / MB) + " MB";
  } else if(bytes / KB > 1) {
    return Math.ceil(bytes / KB) + " KB";
  } else {
    return bytes + " bytes";
  }
}

function toHumanTime(timestamp) {
   var d = new Date(timestamp);
   //return d.format("D, dS M Y @ H:i e");
   return d.format("D, dS M Y @ H:i") + " " + d.toString().replace(/[^\(]+(\(.+\))/, "$1"); //workaround to add timezone as 'e' in above code seems broken in date.js library
}

function copyUnitProperties(srcUnit, destUnit) {
   destUnit.label = srcUnit.label;
   destUnit.size = srcUnit.size;
   destUnit.timestamp = srcUnit.size;
   if (srcUnit.parts) {
     destUnit.parts = expandPendingUnitParts(srcUnit);
   }
   //TODO update action and progress of action!
}

$(document).ready(function() {
  "use strict";

  var socket = $.atmosphere;
  var transport = 'websocket';

  var request = {
    url: "/unit",
    contentType: "application/json",
    logLevel: 'debug',
    transport: transport,
    fallbackTransport: 'long-polling'
  };

  request.onOpen = function(response) {
    console.log('Atmosphere connected using ' + response.transport);

    transport = response.transport;

    if (response.transport == "local") {
      subSocket.pushLocal("Name?");
    }

    //request initial unit status
    subSocket.push(JSON.stringify({action: 'pending'}));
  };

  request.onReconnect = function(request, response) {
    socket.info("Reconnecting");
    console.log("Reconnecting...");
  };

  request.onMessage = function(response) {
    var message = response.responseBody;
      try {
          var json = JSON.parse(message);

          //is this an add/update of unit status?
          if(json.update) {
             updatePending(function(model) {
                $.each(json.update.unit, function(i, v) {

                     //format for display
                     v.size = toHumanSize(v.size);
                     v.timestamp = toHumanTime(v.timestamp);

                     //does the model already contain details of this unit?
                     var existingIdx = -1;
                     for(var i = 0; i < model.length; i++) {
                        if(model[i].uid == v.uid) {
                            existingIdx = i;
                            break;
                        }
                     }

                     if(existingIdx > -1) {
                        //do update
                        copyUnitProperties(v, model[existingIdx]);
                     } else {
                        //create new
                        model.push(v);
                     }
                 });
             });

             //update the unit detail part of the load modal dialog
             $.each(json.update.unit, function(i, v) {
                 updateLoadModal(function(model) {
                     if(v.uid == model.pendingUnit.uid) {
                         model.pendingUnit = v;
                         //has part discovery completed? if so disable decrypting message
                         if(v.parts) {
                             model.pendingUnit.parts = expandPendingUnitParts(v);
                             model.decrypting = false;    //hide decrypting message
                         }
                     }
                 });
             });

          //is this a removal from the pending units?
          } else if(json.remove) {
           updatePending(function(model) {
              $.each(model, function(i, v) {
               $.each(json.remove.unit, function(y, vv) {
                  if((!(v === undefined)) && v.uid == vv.uid) {
                      model.splice(i, 1);
                  }
               });
              });
           });
          }

          //is this a pending unit initial status
//          if(json.pending) {
//            updatePending(function(model) {
//                model.length = 0; //empty the model
//                $.each(json.pending.unit, function(i, v) {
//                    v.size = toHumanSize(v.size);
//                    v.timestamp = toHumanTime(v.timestamp);
//                    model.push(v); //add to the model
//                });
//            });
//
//          //is this an addition to the pending units
//          } else if(json.pendingAdd) {
//            updatePending(function(model) {
//                $.each(json.pendingAdd.unit, function(i, v) {
//                    v.size = toHumanSize(v.size);
//                    v.timestamp = toHumanTime(v.timestamp);
//                    model.push(v); //add to the model
//                });
//            });
//
//            //update the unit detail part of the load modal dialog
//            $.each(json.pendingAdd.unit, function(i, v) {
//                updateLoadModal(function(model) {
//                    if(v.src == model.pendingUnit.src) {
//                        model.pendingUnit = v;
//                        //has part discovery completed? if so disable decrypting message
//                        if(v.part) {
//                            model.decrypting = false;    //hide decrypting message
//                        }
//                    }
//                });
//            });
//
//          //is this a removal from the pending units
//          } else if(json.pendingRemove) {
//            updatePending(function(model) {
//               $.each(model, function(i, v) {
//                $.each(json.pendingRemove.unit, function(y, vv) {
//                   if((!(v === undefined)) && v.src == vv.src) {
//                       model.splice(i, 1);
//                   }
//                });
//               });
//            });
//          }
//
//          //is this a UnitLoadStatus update?
//          else if(json.loadStatus) {
//            updatePending(function(model) {
//              $.each(model, function(i, v) {
//                if(v.src == json.loadStatus.unit.src) {
//                    v.complete = json.loadStatus.complete;
//                }
//              });
//            });
//          }

      } catch (e) {
          console.log('Error: ', message.data);
          return;
      }

    console.debug("Received message: " + json.toString());
  };

  request.onClose = function(response) {
    console.log("Connection closed! " + response.responseBody)
  };

  request.onError = function(response) {
    console.log("Error: " + response.responseBody);
  };

  subSocket = socket.subscribe(request);

});<|endoftext|>


<commit_before>module.exports = function(grunt) {
  var assert = require("assert");
  var fs   = require("fs");
  var path = require("path");

  var SubProcess  = require("../../../utils/subprocess");
  var extend_args = function extend_args(args, check, to_add) {
    if (check) {
      args.push.apply(args, to_add);
    }
  };

  var gpp_task = function() {
    // Prepare task and options.
    var options = this.options({
      coverage: true,
      debug:    true,
      include:  null,
      optimise: null,
      standard: "c++11",

      // Required options.
      objects_path: null
    }, this.data);

    // Assert require parameters were provided.
    assert(options.objects_path, "A path of the object files is required.");

    // Build paths.
    var sources = grunt.file.expandMapping(options.src, options.objects_path, {
      ext:    ".o",
      extDot: "last"
    });

    // Check all additional include files.
    // If any of them was modified after the target re-build it.
    var include_last_modified = 0;
    if (options.include && options.include.length > 0) {
      // Find greatest last modified time for each include path.
      var times = options.include.map(function(include) {
        var headers = grunt.file.expand(include + "/**/*.h");
        headers = headers.map(function(header) {
          return fs.statSync(header).mtime.getTime();
        });

        if (headers.length === 1) {
          return headers[0];
        }
        return Math.max.apply(Math, headers);
      });

      if (times.length === 1) {
        include_last_modified = times[0];
      }
      include_last_modified = Math.max.apply(Math, times) || 0;
    }

    // Filter paths based on last edit time.
    sources = sources.filter(function(source) {
      try {
        var src    = fs.statSync(source.src[0]).mtime.getTime();
        var target = fs.statSync(source.dest).mtime.getTime();
        return Math.max(src, include_last_modified) >= target;

      } catch(ex) { /* NOOP */ }
      return true;
    });
    if (sources.length === 0) {
      grunt.log.ok("Objects up to date, nothing to do.");
      return;
    }

    // Ensure paths exist.
    sources.map(function(source) {
      var target = path.dirname(source.dest);
      if (!fs.existsSync(target)) {
        grunt.file.mkdir(target);
      }
    });
    if (grunt.option("no-write")) {
      return;
    }

    // Build arguments.
    var args = ["-c", "-std=" + options.standard];
    extend_args(args, options.debug, ["-g"]);
    extend_args(args, options.coverage, ["-fprofile-arcs", "-ftest-coverage"]);
    extend_args(args, options.optimise !== null, ["-O" + options.optimise]);

    if (Array.isArray(options.include) && options.include.length > 0) {
      args.push.apply(args, options.include.map(function(include) {
        return "-I" + path.normalize(include);
      }));
    }

    // Generare SubProcess instances.
    var children = sources.map(function(source) {
      var instance_args = [];
      instance_args.push.apply(instance_args, args);
      instance_args.push("-o", source.dest, source.src[0]);
      return new SubProcess(grunt.log, {
        args: instance_args,
        cmd:  "g++"
      });
    });

    // And compile!
    var done = this.async();
    SubProcess.spawnAll(children, grunt.option("parallel")).then(function() {
      done();
    }).fail(function(code) {
      done(new Error(code));
    });
  };

  grunt.registerMultiTask("g++", "Compile C++ sources into objects.", gpp_task);
};
<commit_msg>[Misc] Restructured Repository interface.<commit_after>module.exports = function(grunt) {
  var assert = require("assert");
  var fs   = require("fs");
  var path = require("path");

  var SubProcess  = require("../../../utils/subprocess");
  var extend_args = function extend_args(args, check, to_add) {
    if (check) {
      args.push.apply(args, to_add);
    }
  };

  var gpp_task = function() {
    // Prepare task and options.
    var options = this.options({
      coverage: true,
      debug:    true,
      include:  null,
      optimise: null,
      standard: "c++11",

      // Required options.
      objects_path: null
    }, this.data);

    // Assert require parameters were provided.
    assert(options.objects_path, "A path of the object files is required.");

    // Build paths.
    var sources = grunt.file.expandMapping(options.src, options.objects_path, {
      ext:    ".o",
      extDot: "last"
    });

    // Check all additional include files.
    // If any of them was modified after the target re-build it.
    var include_last_modified = 0;
    if (options.include && options.include.length > 0) {
      // Find greatest last modified time for each include path.
      var times = options.include.map(function(include) {
        var headers = grunt.file.expand(include + "/**/*.{h,hpp}");
        headers = headers.map(function(header) {
          return fs.statSync(header).mtime.getTime();
        });

        if (headers.length === 1) {
          return headers[0];
        }
        return Math.max.apply(Math, headers);
      });

      if (times.length === 1) {
        include_last_modified = times[0];
      }
      include_last_modified = Math.max.apply(Math, times) || 0;
    }

    // Filter paths based on last edit time.
    sources = sources.filter(function(source) {
      try {
        var src    = fs.statSync(source.src[0]).mtime.getTime();
        var target = fs.statSync(source.dest).mtime.getTime();
        return Math.max(src, include_last_modified) >= target;

      } catch(ex) { /* NOOP */ }
      return true;
    });
    if (sources.length === 0) {
      grunt.log.ok("Objects up to date, nothing to do.");
      return;
    }

    // Ensure paths exist.
    sources.map(function(source) {
      var target = path.dirname(source.dest);
      if (!fs.existsSync(target)) {
        grunt.file.mkdir(target);
      }
    });
    if (grunt.option("no-write")) {
      return;
    }

    // Build arguments.
    var args = ["-c", "-std=" + options.standard];
    extend_args(args, options.debug, ["-g"]);
    extend_args(args, options.coverage, ["-fprofile-arcs", "-ftest-coverage"]);
    extend_args(args, options.optimise !== null, ["-O" + options.optimise]);

    if (Array.isArray(options.include) && options.include.length > 0) {
      args.push.apply(args, options.include.map(function(include) {
        return "-I" + path.normalize(include);
      }));
    }

    // Generare SubProcess instances.
    var children = sources.map(function(source) {
      var instance_args = [];
      instance_args.push.apply(instance_args, args);
      instance_args.push("-o", source.dest, source.src[0]);
      return new SubProcess(grunt.log, {
        args: instance_args,
        cmd:  "g++"
      });
    });

    // And compile!
    var done = this.async();
    SubProcess.spawnAll(children, grunt.option("parallel")).then(function() {
      done();
    }).fail(function(code) {
      done(new Error(code));
    });
  };

  grunt.registerMultiTask("g++", "Compile C++ sources into objects.", gpp_task);
};
<|endoftext|>


<commit_before>// Define Kiosk
var Kiosk = (function($, window, document, undefined) {
  $(document).ready(function() {
    Kiosk.go();
  });

  // Application variables
  var localContentServer = 'http://historiclewes.localhost:8082/';
  var remoteContentServer = 'http://www.historiclewes.org/';

  // this should be set to production unless working locally (and you have proper local setup).
  var kioskMode = 'development';

  // reference to the content server set above
  var contentServer = (kioskMode === 'development') ? localContentServer : remoteContentServer;

  // define our services endpoints back to the parent application
  var services = {
    node: 'kiosk/views/kiosk_nodes?display_id=block_1',
    collections: 'kiosk/views/kiosk_nodes?display_id=block_2',
  }

  var jsonFeeds = {
    tbd: 'tbd'
  };

  var vimeo = {
    'type': 'user',
    'id': 'user1949213'
  };

  // End application variables

  return {
    go: function() {
      var i, j = this.init;

      for (i in j) {
        j.hasOwnProperty(i) && j[i]();
      }
    },

    init: {
      getHeader: function() {
        var template = Handlebars.getTemplate('header');
        $("#header").html(template());
      },

      pageContent: function() {
        var template = Handlebars.getTemplate('home');
        Kiosk.util.updateScreen(template());
      },

      getFooter: function() {
        var template = Handlebars.getTemplate('footer');
        $("#footer").html(template());
      }
    },

    // generic function to call and load local HTML files with optional Handlebars components
    getPage: function(template) {
      var Template = Handlebars.getTemplate(template);
      Kiosk.util.updateScreen(Template());
    },

    // fetch a node
    getNode: function(template, node_id) {
      var template = Handlebars.getTemplate(template);

      DrupalAjaxRequest.fetchNode(node_id, function(response) {
        var context = {
          title: response[0].title,
          body: response[0].body
        }

        Kiosk.util.updateScreen(template(context));
      });
    },

    // fetch Collections
    getCollections: function() {
      var template = Handlebars.getTemplate('collections');

      DrupalAjaxRequest.fetchView('collections', function(response) {
        var context = { items: [] }

        $.each(response, function(key, value) {
          context.items.push({'title' : value.title, 'teaser': value.teaser, 'nid': value.nid});
        });

        Kiosk.util.updateScreen(template(context));
      });
    },

    // fetch Videos
    getVideos: function() {
      var template = Handlebars.getTemplate('videos');
      var context = { items: [] }

      VimeoRequest.getData(vimeo.type, vimeo.id, function(response) {
        $.each(response, function(key, value) {
          context.items.push({ 'title' : value.title, 'vimeo_id': value.id, 'thumbnail_small': value.thumbnail_small, 'thumbnail_medium': value.thumbnail_medium, 'thumbnail_large': value.thumbnail_large});
        });

        Kiosk.util.updateScreen(template(context));
      });
    },

    util: {
      contentUrl: function(feed_id) {
        return contentServer + services[feed_id] + '&jsonp=';
      },

      updateScreen: function(content) {
        $("#main-content").fadeIn('slow').html(content);
      }
    }
  }
})(typeof Zepto === 'function' ? Zepto : jQuery, this, this.document);

// Register a 'link' function through Handlebars as a formatter helper.
Handlebars.registerHelper('link', function(object) {
  return new Handlebars.SafeString(
    "" + object.text + ""
  );
});

Handlebars.getTemplate = function(name) {
  if (Handlebars.templates === undefined || Handlebars.templates[name] === undefined) {
    $.ajax({
      type: 'GET',
      url : 'templates/handlebars/' + name + '.hbs',
      cache: false,
      success : function(data) {
        if (Handlebars.templates === undefined) {
          Handlebars.templates = {};
        }
        Handlebars.templates[name] = Handlebars.compile(data);
      },
      async : false
    });
  }
  return Handlebars.templates[name];
};
<commit_msg>Update the application to handle search callback.<commit_after>// Define Kiosk
var Kiosk = (function($, window, document, undefined) {
  $(document).ready(function() {
    Kiosk.go();
  });

  // Application variables
  var localContentServer = 'http://historiclewes.localhost:8082/';
  var remoteContentServer = 'http://www.historiclewes.org/';

  // this should be set to production unless working locally (and you have proper local setup).
  var kioskMode = 'development';

  // reference to the content server set above
  var contentServer = (kioskMode === 'development') ? localContentServer : remoteContentServer;

  // define our services endpoints back to the parent application
  var services = {
    node: 'kiosk/views/kiosk_nodes?display_id=block_1',
    collections: 'kiosk/views/kiosk_nodes?display_id=block_2',
    search: 'kiosk/views/kiosk_nodes?display_id=block_4'
  }

  var jsonFeeds = {
    tbd: 'tbd'
  };

  var vimeo = {
    'type': 'user',
    'id': 'user1949213'
  };

  // End application variables

  return {
    go: function() {
      var i, j = this.init;

      for (i in j) {
        j.hasOwnProperty(i) && j[i]();
      }
    },

    init: {
      getHeader: function() {
        var template = Handlebars.getTemplate('header');
        $("#header").html(template());
      },

      pageContent: function() {
        var template = Handlebars.getTemplate('home');
        Kiosk.util.updateScreen(template());
      },

      getFooter: function() {
        var template = Handlebars.getTemplate('footer');
        $("#footer").html(template());
      }
    },

    // generic function to call and load local HTML files with optional Handlebars components
    getPage: function(template) {
      var Template = Handlebars.getTemplate(template);
      Kiosk.util.updateScreen(Template());
    },

    // fetch a node
    getNode: function(template, node_id) {
      var template = Handlebars.getTemplate(template);

      DrupalRequest.fetchNode(node_id, function(response) {
        var context = {
          title: response[0].title,
          body: response[0].body
        }

        Kiosk.util.updateScreen(template(context));
      });
    },

    // fetch Collections
    getCollections: function() {
      var template = Handlebars.getTemplate('collections');

      DrupalRequest.fetchView('collections', function(response) {
        var context = { items: [] }

        $.each(response, function(key, value) {
          context.items.push({ 'title' : value.title, 'teaser': value.teaser, 'nid': value.nid });
        });

        Kiosk.util.updateScreen(template(context));
      });
    },

    // fetch Videos
    getVideos: function() {
      var template = Handlebars.getTemplate('videos');
      var context = { items: [] }

      VimeoRequest.getData(vimeo.type, vimeo.id, function(response) {
        $.each(response, function(key, value) {
          context.items.push({ 'title' : value.title, 'vimeo_id': value.id, 'thumbnail_small': value.thumbnail_small, 'thumbnail_medium': value.thumbnail_medium, 'thumbnail_large': value.thumbnail_large});
        });

        Kiosk.util.updateScreen(template(context));
      });
    },

    getSearchResults: function() {
      var template = Handlebars.getTemplate('search');
      var keywords = $('#kiosk-search').attr('value');
      var context = { items: [], keywords: keywords }

      DrupalRequest.doSearch('search', keywords, function(response) {
        $.each(response, function(key, value) {
          context.items.push({ 'title' : value.title, 'nid': value.nid, 'teaser': value.teaser });
        });

        Kiosk.util.updateScreen(template(context));
      });
    },

    util: {
      contentUrl: function(feed_id) {
        return contentServer + services[feed_id] + '&jsonp=';
      },

      updateScreen: function(content) {
        $("#main-content").fadeIn('slow').html(content);
      }
    }
  }
})(typeof Zepto === 'function' ? Zepto : jQuery, this, this.document);

// Register a 'link' function through Handlebars as a formatter helper.
Handlebars.registerHelper('link', function(object) {
  return new Handlebars.SafeString(
    "" + object.text + ""
  );
});

Handlebars.getTemplate = function(name) {
  if (Handlebars.templates === undefined || Handlebars.templates[name] === undefined) {
    $.ajax({
      type: 'GET',
      url : 'templates/handlebars/' + name + '.hbs',
      cache: false,
      success : function(data) {
        if (Handlebars.templates === undefined) {
          Handlebars.templates = {};
        }
        Handlebars.templates[name] = Handlebars.compile(data);
      },
      async : false
    });
  }
  return Handlebars.templates[name];
};
<|endoftext|>


<commit_before>/* Copyright 2015-2016 Teeming Society. Licensed under the Apache License, Version 2.0 (the "License"); DreemGL is a collaboration between Teeming Society & Samsung Electronics, sponsored by Samsung and others.
 You may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
 either express or implied. See the License for the specific language governing permissions and limitations under the License.*/

define.class(function(exports){

	// internal, Reactive renderer

	var initializing = false

	var process_list = []
	var process_timer = undefined

	function processTimeout(){
		process_timer = undefined
		var pl = process_list
		process_list = []
		for(var i = 0; i < pl.length; i+=2){
			//console.log("Processing",pl[i])
			exports.process(pl[i+1], undefined, undefined, true)
		}
	}

	function __atAttributeGet(key){
		if(!initializing){
			//exports.process(this, undefined, undefined, true)
			if(process_list.indexOf(this) === -1){
				process_list.push(key)
				process_list.push(this)
			}
			if(!process_timer){
				process_timer = setTimeout(processTimeout, 0)
			}
		}
	}

	function atAttributeGet(key){
		// lets find out if we already have a listener on it
		if(this.getAttributeConfig(key).rerender !== false && !this.hasListenerProp(key, 'name', '__atAttributeGet')){
			this.addListener(key, __atAttributeGet)
		}
	}

	exports.process = function render(inew_version, old_version, state, rerender, nochild){

		var new_version = inew_version
		var is_root = false

		if(!state){
			state = {wires:[], render_block:[]}
			is_root = true
		}

		// call connect wires before
		if(!rerender) new_version.connectWires(state.wires)

		var old_children = old_version? old_version.children: undefined

		// lets call init only when not already called
		if(!rerender){
			if(old_version && old_version._persists && new_version._persists){
				for(var key in old_version._persists){
					if(!(key in new_version._persists)) continue
					// we should set it using a special emit
					var value =  old_version[key]
					new_version['_' + key] = old_version[key]
					new_version.emit(key, {type:'persist', owner:new_version, key:key, value:value})
				}
			}
			if(new_version.atDraw) new_version.screen.device.draw_hooks.push(new_version)
			if(new_version.atViewInit) new_version.atViewInit(old_version)
			new_version.emit('init', old_version)// old_version && old_version.constructor == new_version.constructor? old_version: undefined)
		}
		else{
			old_children = new_version.children
		}

		// lets only do this if we have a render function
		if(new_version.render){
			// then call render

			// store the attribute dependencies
			new_version.atAttributeGet = atAttributeGet
			new_version.rerender = __atAttributeGet

			// lets check if object.constructor  a module, ifso
			if(new_version.classroot === undefined){
				new_version.classroot = new_version
				//console.log(object)
	 		}

	 		define.atConstructor =  new_version.atStyleConstructor.bind(new_version)

	 		new_version.children = new_version.render()

	 		define.atConstructor = undefined
			new_version.atAttributeGet = undefined
		}
		else{
			new_version.children = new_version.constructor_children
		}

		if(!Array.isArray(new_version.children)){
			if(new_version.children) new_version.children = [new_version.children]
			else new_version.children = []
		}

		if(new_version.atRender) new_version.atRender()

		if(new_version._viewport){
			// set up a new layer
			new_version.parent_viewport = new_version
			new_version.child_viewport_list = []
			if(!rerender && new_version.parent && new_version.parent.parent_viewport){
				new_version.parent.parent_viewport.child_viewport_list.push(new_version)
			}
		}
 		// what we need to do, is

		var new_children = new_version.children

		if(!nochild && new_children) for(var i = 0; i < new_children.length; i++){
			var new_child = new_children[i]

			if(Array.isArray(new_child)){ // splice in the children
				var args = Array.prototype.slice.call(new_child)
				args.unshift(1)
				args.unshift(i)
				Array.prototype.splice.apply(new_children, args)
				i-- // hop back one i so we repeat
				continue
			}

			var old_child = undefined
			if(old_children){
				old_child = old_children[i]
				if(new_children.indexOf(old_child) !== -1) old_child = undefined
			}
			var childreuse = false
			if(new_child.parent){
				childreuse = true
			}
			//var name = new_child.name
			//if(name !== undefined && !(name in new_version)) new_version[name] = new_child

			new_child.parent = new_version
			new_child.screen = new_version.screen
			new_child.rpc = new_version.rpc
			new_child.parent_viewport = new_version.parent_viewport
			new_children[i] = render(new_child, old_child, state, childreuse)
		}

		if(new_version.atChildrenRendered) new_version.atChildrenRendered()

		if(old_children) for(;i < old_children.length;i++){
			var child = old_children[i]
			child.destroyed = true
			child.emit('destroy')
		}

		if(old_version && !rerender){
			old_version.destroyed = true
			// remove draw hook
			if(old_version.atDraw){
				console.log("REMOVING")
				var id = old_version.screen.device.draw_hooks.indexOf(old_version)
				if(id !== -1) old_version.screen.device.draw_hooks.splice(id, 1)
			}

			old_version.emit('destroy')
		}

		if(is_root){
			initializing = true
			for(var i = 0; i < state.wires.length; i++){
				state.wires[i]()
			}
			initializing = false

			// signal to our device we have a newly rendered node
			if(new_version.screen){
				new_version.screen.device.atNewlyRendered(new_version)
			}
		}

		return new_version
	}

	exports.dump = function(node, depth){
		var ret = ''
		if(!depth) depth = ''
		ret += depth + node.name + ': ' + node.constructor.name
		var outer = node.outer
		while(outer){
			ret += " - " + outer.constructor.name
			outer = outer.outer
		}
		if(node.children) for(var i = 0; i<node.children.length; i++){
			ret += "\n"
			ret += this.dump(node.children[i], depth +'-')
		}
		return ret
	}
})
<commit_msg>Fix wire init order<commit_after>/* Copyright 2015-2016 Teeming Society. Licensed under the Apache License, Version 2.0 (the "License"); DreemGL is a collaboration between Teeming Society & Samsung Electronics, sponsored by Samsung and others.
 You may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
 either express or implied. See the License for the specific language governing permissions and limitations under the License.*/

define.class(function(exports){

	// internal, Reactive renderer

	var initializing = false

	var process_list = []
	var process_timer = undefined

	function processTimeout(){
		process_timer = undefined
		var pl = process_list
		process_list = []
		for(var i = 0; i < pl.length; i+=2){
			//console.log("Processing",pl[i])
			exports.process(pl[i+1], undefined, undefined, true)
		}
	}

	function __atAttributeGet(key){
		if(!initializing){
			//exports.process(this, undefined, undefined, true)
			if(process_list.indexOf(this) === -1){
				process_list.push(key)
				process_list.push(this)
			}
			if(!process_timer){
				process_timer = setTimeout(processTimeout, 0)
			}
		}
	}

	function atAttributeGet(key){
		// lets find out if we already have a listener on it
		if(this.getAttributeConfig(key).rerender !== false && !this.hasListenerProp(key, 'name', '__atAttributeGet')){
			this.addListener(key, __atAttributeGet)
		}
	}

	exports.process = function render(inew_version, old_version, state, rerender, nochild){

		var new_version = inew_version
		var is_root = false

		if(!state){
			state = {render_block:[]}
			is_root = true
		}

		// call connect wires before
		if(!rerender){
			var wires = []
			new_version.connectWires(wires)
			initializing = true
			for(var i = 0; i < wires.length; i++){
				wires[i]()
			}
			initializing = false
		}

		var old_children = old_version? old_version.children: undefined

		// lets call init only when not already called
		if(!rerender){
			if(old_version && old_version._persists && new_version._persists){
				for(var key in old_version._persists){
					if(!(key in new_version._persists)) continue
					// we should set it using a special emit
					var value =  old_version[key]
					new_version['_' + key] = old_version[key]
					new_version.emit(key, {type:'persist', owner:new_version, key:key, value:value})
				}
			}
			if(new_version.atDraw) new_version.screen.device.draw_hooks.push(new_version)
			if(new_version.atViewInit) new_version.atViewInit(old_version)
			new_version.emit('init', old_version)// old_version && old_version.constructor == new_version.constructor? old_version: undefined)
		}
		else{
			old_children = new_version.children
		}

		// lets only do this if we have a render function
		if(new_version.render){
			// then call render

			// store the attribute dependencies
			new_version.atAttributeGet = atAttributeGet
			new_version.rerender = __atAttributeGet

			// lets check if object.constructor  a module, ifso
			if(new_version.classroot === undefined){
				new_version.classroot = new_version
				//console.log(object)
	 		}

	 		define.atConstructor =  new_version.atStyleConstructor.bind(new_version)

	 		new_version.children = new_version.render()

	 		define.atConstructor = undefined
			new_version.atAttributeGet = undefined
		}
		else{
			new_version.children = new_version.constructor_children
		}

		if(!Array.isArray(new_version.children)){
			if(new_version.children) new_version.children = [new_version.children]
			else new_version.children = []
		}

		if(new_version.atRender) new_version.atRender()

		if(new_version._viewport){
			// set up a new layer
			new_version.parent_viewport = new_version
			new_version.child_viewport_list = []
			if(!rerender && new_version.parent && new_version.parent.parent_viewport){
				new_version.parent.parent_viewport.child_viewport_list.push(new_version)
			}
		}
 		// what we need to do, is

		var new_children = new_version.children

		if(!nochild && new_children) for(var i = 0; i < new_children.length; i++){
			var new_child = new_children[i]

			if(Array.isArray(new_child)){ // splice in the children
				var args = Array.prototype.slice.call(new_child)
				args.unshift(1)
				args.unshift(i)
				Array.prototype.splice.apply(new_children, args)
				i-- // hop back one i so we repeat
				continue
			}

			var old_child = undefined
			if(old_children){
				old_child = old_children[i]
				if(new_children.indexOf(old_child) !== -1) old_child = undefined
			}
			var childreuse = false
			if(new_child.parent){
				childreuse = true
			}
			//var name = new_child.name
			//if(name !== undefined && !(name in new_version)) new_version[name] = new_child

			new_child.parent = new_version
			new_child.screen = new_version.screen
			new_child.rpc = new_version.rpc
			new_child.parent_viewport = new_version.parent_viewport
			new_children[i] = render(new_child, old_child, state, childreuse)
		}

		if(new_version.atChildrenRendered) new_version.atChildrenRendered()

		if(old_children) for(;i < old_children.length;i++){
			var child = old_children[i]
			child.destroyed = true
			child.emit('destroy')
		}

		if(old_version && !rerender){
			old_version.destroyed = true
			// remove draw hook
			if(old_version.atDraw){
				var id = old_version.screen.device.draw_hooks.indexOf(old_version)
				if(id !== -1) old_version.screen.device.draw_hooks.splice(id, 1)
			}

			old_version.emit('destroy')
		}

		if(is_root){

			// signal to our device we have a newly rendered node
			if(new_version.screen){
				new_version.screen.device.atNewlyRendered(new_version)
			}
		}

		return new_version
	}

	exports.dump = function(node, depth){
		var ret = ''
		if(!depth) depth = ''
		ret += depth + node.name + ': ' + node.constructor.name
		var outer = node.outer
		while(outer){
			ret += " - " + outer.constructor.name
			outer = outer.outer
		}
		if(node.children) for(var i = 0; i<node.children.length; i++){
			ret += "\n"
			ret += this.dump(node.children[i], depth +'-')
		}
		return ret
	}
})
<|endoftext|>


<commit_before>var ItemListView = Backbone.View.extend({
	tagName: 'ul',
	className: 'nav nav-list',
	initialize: function() {
		this.cursor = null;
		this.views = [];

		this.listenTo(this.collection, 'sync', this.addAll);
		this.listenTo(this.collection, 'reset', this.addAll);

		$(this.render().el).appendTo('#item-list');
	},
	remove: function() {
		this.removeAllSubviews();
		Backbone.View.prototype.remove.call(this);
	},
	removeAllSubviews: function() {
		_.each(this.views, function(view) {
			view.remove();
		});
		this.views.length = 0;
	},
	addOne: function(item) {
		if(!SETTINGS.show_read && item.get('read') && !item.get('open')) {
			return;
		}

		var view = new ItemView({model: item});
		this.$el.append(view.render().el);
		this.views.push(view);
	},
	addAll: function() {
		this.$el.empty();
		this.removeAllSubviews();
		this.collection.each(this.addOne, this);
	},
	closeAll: function() {
		this.collection.each(function(item) {
			item.set('open', false);
		});
	},
	moveCursor: function(e) {
		var item = null;

		if(this.cursor === null) {
			if(e.keyCode == 74) {
				item = this.collection.first();
			}
		}
		else {
			var index = this.collection.indexOf(this.collection.get(this.cursor));

			if(e.keyCode == 74) { // J (down)
				item = this.collection.at(index+1);
			}
			else if(e.keyCode == 75) { // K (Up)
				item = this.collection.at(index-1);
			}
		}

		if(item !== null && item !== undefined) {
			this.closeAll();
			item.set('open', true);
			if(!item.get('read')) {
				item.toggleRead();
			}
			this.cursor = item.id;
		}
	}
});
<commit_msg>Fix keyboard shortbuts behavior when unread items are hidden<commit_after>var ItemListView = Backbone.View.extend({
	tagName: 'ul',
	className: 'nav nav-list',
	initialize: function() {
		this.cursor = null;
		this.views = [];

		this.listenTo(this.collection, 'sync', this.addAll);
		this.listenTo(this.collection, 'reset', this.addAll);

		$(this.render().el).appendTo('#item-list');
	},
	remove: function() {
		this.removeAllSubviews();
		Backbone.View.prototype.remove.call(this);
	},
	removeAllSubviews: function() {
		_.each(this.views, function(view) {
			view.remove();
		});
		this.views.length = 0;
	},
	addOne: function(item) {
		if(!SETTINGS.show_read && item.get('read') && !item.get('open')) {
			return;
		}

		var view = new ItemView({model: item});
		this.$el.append(view.render().el);
		this.views.push(view);
	},
	addAll: function() {
		this.$el.empty();
		this.removeAllSubviews();
		this.collection.each(this.addOne, this);
	},
	closeAll: function() {
		this.collection.each(function(item) {
			item.set('open', false);
		});
	},
	moveCursor: function(e) {
		var item = null;

		if(this.cursor === null) {
			if(e.keyCode == 74) {
				item = this.collection.first();
			}
		}
		else {
			var index = this.collection.indexOf(this.collection.get(this.cursor));
			var dir = 0;

			if(e.keyCode == 74) { // J (down)
				dir = 1;
			}
			else if(e.keyCode == 75) { // K (Up)
				dir = -1;
			}

			item = this.collection.at(index+dir);

			// If read items are hidden, loop until we get an unread item
			if(!SETTINGS.show_read) {
				while(item !== null && item !== undefined && item.get('read')) {
					index+=dir;
					item = this.collection.at(index+dir);
				}
			}
		}

		if(item !== null && item !== undefined) {
			this.closeAll();
			item.set('open', true);
			if(!item.get('read')) {
				item.toggleRead();
			}
			this.cursor = item.id;
		}
	}
});
<|endoftext|>


<commit_before><commit_msg>update anychart bundle 1.2.0<commit_after><|endoftext|>


<commit_before>// Video.js
// The set of videos registered in our app.

module.exports = {
	autoPK: false,
	attributes: {
		id :{
			autoIncrement: true,
			primaryKey: true,
			columnName: 'video_id'
		},

		title: {
			type: 'string',
			required: true
		},

	    description: {
	    	type: 'string'
	    },

	    tags: {
	    	collection: 'tag',
	    	via: 'videoWithTags'
	    },

	    // 0 for self only, 1 for public
	    privacy: {
	    	type: 'integer',
	    	required: true,
	    	defaultsTo: 1
	    },

	    duration: {
	    	type: 'string'
	    },

	    shares: {
	    	type: 'integer',
	    	defaultsTo: 0
	    },

	    videoDir: {
	    	type: 'string',
	    	required: true
	    },

	    createdAt: {
	    	type: 'datetime',
	    	defaultsTo: function() {return new Date(); }
	    },

	    updatedAt: {
	    	type: 'datetime',
	    	defaultsTo: function() {return new Date(); }
	    }
	}
}<commit_msg>Add views and thumbnailDir attribute to Video model<commit_after>// Video.js
// The set of videos registered in our app.

module.exports = {
	autoPK: false,
	attributes: {
		id :{
			autoIncrement: true,
			primaryKey: true,
			columnName: 'video_id'
		},

		title: {
			type: 'string',
			required: true
		},

	    description: {
	    	type: 'string'
	    },

	    tags: {
	    	collection: 'tag',
	    	via: 'videoWithTags'
	    },

	    views: {
	    	type: 'integer',
	    	defaultsTo: 0
	    },

	    // 0 for self only, 1 for public
	    privacy: {
	    	type: 'integer',
	    	required: true,
	    	defaultsTo: 1
	    },

	    duration: {
	    	type: 'string'
	    },

	    shares: {
	    	type: 'integer',
	    	defaultsTo: 0
	    },

	    videoDir: {
	    	type: 'string',
	    	required: true
	    },

	    thumbnailDir: {
	    	type: 'string',
	    	required: true
	    },

	    createdAt: {
	    	type: 'datetime',
	    	defaultsTo: function() {return new Date(); }
	    },

	    updatedAt: {
	    	type: 'datetime',
	    	defaultsTo: function() {return new Date(); }
	    }
	}
}<|endoftext|>