<commit_before>var apikeys = {
	"thesaurus": process.env.THESAURUS_API_KEY
};
var Promise 	  = require('bluebird'),
	request 	  = require('request'),
	//failed to promisify xml2js (not sure if by own error or by bug in either lib)
	parseXML 	  = require('xml2js').parseString;

var redis         = require("redis"),
    redisClient   = redis.createClient();

redisClient.on("error", function (err) {
    console.log("Redis Error: ", err);
});

var synonyms = {
    getSynonyms : function(word) {
        var self = this;
        return new Promise(function(resolve, reject) {

            var synonymsList = [];

            //check if synonym exists in cache
            var getCachedSynonymsPromise = self._getCachedSynonyms(word);

            //Handle cache repsonse
            var apiPromise = getCachedSynonymsPromise.then(function(result) {
                    if(!result) {
                        //No cached synonyms found so make api request
                        return Promise.all([self._makeApiRequest(word), self._makeWordnikApiRequest(word)]);
                    }
                    //Cached synonyms found
                    resolve(result);
                })
                .error(function(err) {
                    reject(err);
                })
                //handle the all or continue passing along the empty result
                .then(function(result) {
                    if(result) {
                        //console.log('wordnik api request', result);
                        var joinedSynonyms = result[0]
                            .concat(result[1])
                            .sort()
                            .filter(function(item, pos, arr) {
                                return !pos || item !== arr[pos - 1];
                            });
                        return joinedSynonyms;
                    }
                }, function(err) {
                    reject(err);
                });

            //Handle Api Response
            var setCachedSynonymsPromise = apiPromise.then(function(result) {
                    //check for result to see if apiRequest was even made. this fix works, but seems hacky
                    if(result) {
                        synonymsList = result;
                        return self._setCachedSynonyms(word, result);
                    }
                })
                .error(function(err) {
                    reject(err);
                });

            //Handle setting cache response
            setCachedSynonymsPromise.then(function(result) {
                    if(result) {
                        //console.log('success setting cached synonyms: ', result);
                    } else {
                        //console.log('results already cached');
                    }
                })
                .error(function(err) {
                    //console.log('error setting cached synonyms: ', err);
                })
                .finally(function() {
                    resolve(synonymsList);
                });

        });
    },

    /*
     * Utility Methods
     * These methods below are exposed for unit testing purposes only
     */

    _makeApiRequest : function(word) {

        var self = this;

        return new Promise(function(resolve, reject) {
            //word was not in cache
            var url =
                'http://www.dictionaryapi.com/api/v1/references/thesaurus/xml/' +
                word +
                '?key=' +
                apikeys.thesaurus;

            request(url, function(error, response, body) {
                if(error) {
                    resolve([]);
                    return;
                }
                parseXML(body, function(error, result) {
                    if(error) {
                        resolve([]);
                        return;
                    } else {
                        if(result.suggestion || result.entry_list.suggestion) {
                            resolve([]);
                            return;
                        }
                        var parsedSynonyms = self._parseSynonymsXML(result);
                        if(parsedSynonyms.error) {
                            resolve([]);
                            return;
                        }

                        var synonymsList = self._synonymsList(parsedSynonyms);
                        resolve(synonymsList);
                    }
                })
            });
        });
    },

    _makeWordnikApiRequest : function(word) {

        var self = this;

        return new Promise(function(resolve, reject) {
            //word was not in cache
            var url =
                'http://api.wordnik.com:80/v4/word.json/' +
                word +
                '/relatedWords?useCanonical=false&limitPerRelationshipType=100&api_key=' +
                apikeys.wordnik;

            request(url, function(error, response, body) {
                if(error) {
                    //console.log('Wordnik api error: ', error);
                    resolve([]);
                    return;
                }
                //parsing the json from wordnik is so much easier
                //console.log('Wordnik Response: ', body);
                resolve(self._parseWordnikResults(self._parseRawWordnikBody(body)));
            });
        });
    },

    _parseRawWordnikBody : function(body) {
        try {
            return JSON.parse(body);
        } catch(e) {
            //console.log('Error parsing raw wordnik body');
            return [];
        }
    },

    _parseWordnikResults : function(results) {
        // hash table for fast lookups
        var wantedRelationshipTypes = {
            "verb-form": true,
            "hypernym": true,
            "etymologically-related-term": true,
            "variant": true,
            "same-context": true,
            "synonym": true
        };
        var words = [];
        for(var i = 0; i < results.length; i++) {
            if(wantedRelationshipTypes[results[i].relationshipType] === true) {
                words = words.concat(results[i].words);
            }
        }
        return words;
    },

    _getCachedSynonyms : function(word) {
        return new Promise(function(resolve, reject) {
            redisClient.get('synonyms:' + word, function(err, result) {
                if(err) {
                    reject(err);
                } else if(result) {
                    //results are stored in redis as a string. Need to blow it up into array
                    var resultArray = result.split(',');
                    resolve(resultArray);
                } else {
                    resolve(false);
                }
            });
        });
    },

    _setCachedSynonyms : function(word, synonyms) {
        return new Promise(function(resolve, reject) {
            //cache synonyms results
            redisClient.set('synonyms:' + word, synonyms.join(), function(err, result) {
                if(err) {
                    reject(err);
                } else if(result) {
                    var secondsPerDay = 60 * 60 * 24;
                    var ttlDays = 180;
                    var ttl = ttlDays * secondsPerDay;
                    redisClient.expire('synonyms:' + word, ttl, function(err, res) {
                        //console.log('expire err:', err);
                        //console.log('expire result: ', res);
                    });
                    resolve(result);
                } else {
                    reject('Redis: Unkown error caching synonymsList');
                }
            });
        });
    },

    //this method returns an object of mapped data, or an object with an error property
    _parseSynonymsXML : function(rawSynonyms) {
        //check for needed properties
        if( !rawSynonyms ||
            !rawSynonyms.entry_list ||
            !rawSynonyms.entry_list.entry ||
            rawSynonyms.entry_list.entry.length == 0 ) {

                return {error: '_mapRawSynonyms: rawSynonyms fails validation'};
        }

        //an entry differentiates between words by type like noun or verb
        var entries = [];
        var entryCount = rawSynonyms.entry_list.entry.length;
        for(var i = 0; i < entryCount; i++) {
            var rawEntry = rawSynonyms.entry_list.entry[i];

            var entry = {};
            entry.wordType = rawEntry.fl;

            var senses = [];
            var senseCount  = rawEntry.sens.length;
            for(var j = 0; j < senseCount; j++) {
                var rawSense = rawEntry.sens[j];

                //Checking for valid Sense.
                //  The API sometimes returns invalid Senses mixed with valid ones (investigate why)
                if( !rawSense ||
                    !rawSense.mc ||
                    !rawSense.mc.length ||
                    !rawSense.rel ||
                    !rawSense.rel.length ||
                    !rawSense.syn ||
                    !rawSense.syn.length) {
                    //console.log('rawSense is null. senseCount: ', senseCount, ' j: ', j, ' i: ', i);
                    continue;
                }

                var sense = {};
                sense.meaning = rawSense.mc[0];

                var rel = rawSense.rel[0];
                if(rel._) {
                    rel = rel._;
                }
                var syn = rawSense.syn[0];
                if(syn._) {
                    syn = syn._;
                }
                var relWords = this._extractKeywords(rel);
                var synWords = this._extractKeywords(syn);
                sense.words = synWords.concat(relWords);

                senses.push(sense);
            }
            entry.senses = senses;

            entries.push(entry);
        }

        return entries;
    },

    _extractKeywords : function(str) {
        return str
            //remove whitespace
            .replace(/(\s)/g, '')
            //remove parentheses and words in them
            .replace(/(\(.*\))/g, '')
            //replace semicolons with commas
            .replace(/([;])/g, ',')
            //remove square brackets (are they empty? I forget the format that caused this check to be necessary)
            .replace(/(\[\])/g, '')
            //remove hyphens
            .replace(/(-)/g, '')
            //use commas to split words into an array
            .split(',');
    },

    //Takes in a synonyms array.
    /*
        synonyms[] {
            wordType : ...
            senses[] : {
                meaning : ...
                words[] : ...
            }
        }
    */
    _synonymsList : function(synonyms) {
        //storing domains as a hash
        var synonymsHash = {};

        //so many loops. caching lengths to squeeze performance a bit.
        var synonymsCount = synonyms.length;
        for(var i = 0; i < synonymsCount; i++) {
            var synonymSet = synonyms[i]
            var senseCount = synonymSet.senses.length;
            for(var j = 0; j < senseCount; j++) {
                var sense = synonymSet.senses[j];

                var wordCount = sense.words.length;
                for(var k = 0; k < wordCount; k++) {
                    var synonym = sense.words[k];
                    synonymsHash[synonym] = true;
                }
            }
        }

        var result = [], prop, i;

        for (prop in synonymsHash) {
            if (hasOwnProperty.call(synonymsHash, prop)) {
                result.push(prop);
            }
        }

        return result;
    }
};

module.exports = synonyms;<commit_msg>Change redis host url and add wordnik key from env.<commit_after>var apikeys = {
    "thesaurus": process.env.THESAURUS_API_KEY,
    "wordnik": process.env.WORDNIK_API_KEY
};
var Promise 	  = require('bluebird'),
	request 	  = require('request'),
	//failed to promisify xml2js (not sure if by own error or by bug in either lib)
	parseXML 	  = require('xml2js').parseString;

var redis         = require("redis"),
    redisClient   = redis.createClient({
        host: "redis"
    });

redisClient.on("error", function (err) {
    console.log("Redis Error: ", err);
});

var synonyms = {
    getSynonyms : function(word) {
        var self = this;
        return new Promise(function(resolve, reject) {

            var synonymsList = [];

            //check if synonym exists in cache
            var getCachedSynonymsPromise = self._getCachedSynonyms(word);

            //Handle cache repsonse
            var apiPromise = getCachedSynonymsPromise.then(function(result) {
                    if(!result) {
                        //No cached synonyms found so make api request
                        return Promise.all([self._makeApiRequest(word), self._makeWordnikApiRequest(word)]);
                    }
                    //Cached synonyms found
                    resolve(result);
                })
                .error(function(err) {
                    reject(err);
                })
                //handle the all or continue passing along the empty result
                .then(function(result) {
                    if(result) {
                        //console.log('wordnik api request', result);
                        var joinedSynonyms = result[0]
                            .concat(result[1])
                            .sort()
                            .filter(function(item, pos, arr) {
                                return !pos || item !== arr[pos - 1];
                            });
                        return joinedSynonyms;
                    }
                }, function(err) {
                    reject(err);
                });

            //Handle Api Response
            var setCachedSynonymsPromise = apiPromise.then(function(result) {
                    //check for result to see if apiRequest was even made. this fix works, but seems hacky
                    if(result) {
                        synonymsList = result;
                        return self._setCachedSynonyms(word, result);
                    }
                })
                .error(function(err) {
                    reject(err);
                });

            //Handle setting cache response
            setCachedSynonymsPromise.then(function(result) {
                    if(result) {
                        //console.log('success setting cached synonyms: ', result);
                    } else {
                        //console.log('results already cached');
                    }
                })
                .error(function(err) {
                    //console.log('error setting cached synonyms: ', err);
                })
                .finally(function() {
                    resolve(synonymsList);
                });

        });
    },

    /*
     * Utility Methods
     * These methods below are exposed for unit testing purposes only
     */

    _makeApiRequest : function(word) {

        var self = this;

        return new Promise(function(resolve, reject) {
            //word was not in cache
            var url =
                'http://www.dictionaryapi.com/api/v1/references/thesaurus/xml/' +
                word +
                '?key=' +
                apikeys.thesaurus;

            request(url, function(error, response, body) {
                if(error) {
                    resolve([]);
                    return;
                }
                parseXML(body, function(error, result) {
                    if(error) {
                        resolve([]);
                        return;
                    } else {
                        if(result.suggestion || result.entry_list.suggestion) {
                            resolve([]);
                            return;
                        }
                        var parsedSynonyms = self._parseSynonymsXML(result);
                        if(parsedSynonyms.error) {
                            resolve([]);
                            return;
                        }

                        var synonymsList = self._synonymsList(parsedSynonyms);
                        resolve(synonymsList);
                    }
                })
            });
        });
    },

    _makeWordnikApiRequest : function(word) {

        var self = this;

        return new Promise(function(resolve, reject) {
            //word was not in cache
            var url =
                'http://api.wordnik.com:80/v4/word.json/' +
                word +
                '/relatedWords?useCanonical=false&limitPerRelationshipType=100&api_key=' +
                apikeys.wordnik;

            request(url, function(error, response, body) {
                if(error) {
                    //console.log('Wordnik api error: ', error);
                    resolve([]);
                    return;
                }
                //parsing the json from wordnik is so much easier
                //console.log('Wordnik Response: ', body);
                resolve(self._parseWordnikResults(self._parseRawWordnikBody(body)));
            });
        });
    },

    _parseRawWordnikBody : function(body) {
        try {
            return JSON.parse(body);
        } catch(e) {
            //console.log('Error parsing raw wordnik body');
            return [];
        }
    },

    _parseWordnikResults : function(results) {
        // hash table for fast lookups
        var wantedRelationshipTypes = {
            "verb-form": true,
            "hypernym": true,
            "etymologically-related-term": true,
            "variant": true,
            "same-context": true,
            "synonym": true
        };
        var words = [];
        for(var i = 0; i < results.length; i++) {
            if(wantedRelationshipTypes[results[i].relationshipType] === true) {
                words = words.concat(results[i].words);
            }
        }
        return words;
    },

    _getCachedSynonyms : function(word) {
        return new Promise(function(resolve, reject) {
            redisClient.get('synonyms:' + word, function(err, result) {
                if(err) {
                    reject(err);
                } else if(result) {
                    //results are stored in redis as a string. Need to blow it up into array
                    var resultArray = result.split(',');
                    resolve(resultArray);
                } else {
                    resolve(false);
                }
            });
        });
    },

    _setCachedSynonyms : function(word, synonyms) {
        return new Promise(function(resolve, reject) {
            //cache synonyms results
            redisClient.set('synonyms:' + word, synonyms.join(), function(err, result) {
                if(err) {
                    reject(err);
                } else if(result) {
                    var secondsPerDay = 60 * 60 * 24;
                    var ttlDays = 180;
                    var ttl = ttlDays * secondsPerDay;
                    redisClient.expire('synonyms:' + word, ttl, function(err, res) {
                        //console.log('expire err:', err);
                        //console.log('expire result: ', res);
                    });
                    resolve(result);
                } else {
                    reject('Redis: Unkown error caching synonymsList');
                }
            });
        });
    },

    //this method returns an object of mapped data, or an object with an error property
    _parseSynonymsXML : function(rawSynonyms) {
        //check for needed properties
        if( !rawSynonyms ||
            !rawSynonyms.entry_list ||
            !rawSynonyms.entry_list.entry ||
            rawSynonyms.entry_list.entry.length == 0 ) {

                return {error: '_mapRawSynonyms: rawSynonyms fails validation'};
        }

        //an entry differentiates between words by type like noun or verb
        var entries = [];
        var entryCount = rawSynonyms.entry_list.entry.length;
        for(var i = 0; i < entryCount; i++) {
            var rawEntry = rawSynonyms.entry_list.entry[i];

            var entry = {};
            entry.wordType = rawEntry.fl;

            var senses = [];
            var senseCount  = rawEntry.sens.length;
            for(var j = 0; j < senseCount; j++) {
                var rawSense = rawEntry.sens[j];

                //Checking for valid Sense.
                //  The API sometimes returns invalid Senses mixed with valid ones (investigate why)
                if( !rawSense ||
                    !rawSense.mc ||
                    !rawSense.mc.length ||
                    !rawSense.rel ||
                    !rawSense.rel.length ||
                    !rawSense.syn ||
                    !rawSense.syn.length) {
                    //console.log('rawSense is null. senseCount: ', senseCount, ' j: ', j, ' i: ', i);
                    continue;
                }

                var sense = {};
                sense.meaning = rawSense.mc[0];

                var rel = rawSense.rel[0];
                if(rel._) {
                    rel = rel._;
                }
                var syn = rawSense.syn[0];
                if(syn._) {
                    syn = syn._;
                }
                var relWords = this._extractKeywords(rel);
                var synWords = this._extractKeywords(syn);
                sense.words = synWords.concat(relWords);

                senses.push(sense);
            }
            entry.senses = senses;

            entries.push(entry);
        }

        return entries;
    },

    _extractKeywords : function(str) {
        return str
            //remove whitespace
            .replace(/(\s)/g, '')
            //remove parentheses and words in them
            .replace(/(\(.*\))/g, '')
            //replace semicolons with commas
            .replace(/([;])/g, ',')
            //remove square brackets (are they empty? I forget the format that caused this check to be necessary)
            .replace(/(\[\])/g, '')
            //remove hyphens
            .replace(/(-)/g, '')
            //use commas to split words into an array
            .split(',');
    },

    //Takes in a synonyms array.
    /*
        synonyms[] {
            wordType : ...
            senses[] : {
                meaning : ...
                words[] : ...
            }
        }
    */
    _synonymsList : function(synonyms) {
        //storing domains as a hash
        var synonymsHash = {};

        //so many loops. caching lengths to squeeze performance a bit.
        var synonymsCount = synonyms.length;
        for(var i = 0; i < synonymsCount; i++) {
            var synonymSet = synonyms[i]
            var senseCount = synonymSet.senses.length;
            for(var j = 0; j < senseCount; j++) {
                var sense = synonymSet.senses[j];

                var wordCount = sense.words.length;
                for(var k = 0; k < wordCount; k++) {
                    var synonym = sense.words[k];
                    synonymsHash[synonym] = true;
                }
            }
        }

        var result = [], prop, i;

        for (prop in synonymsHash) {
            if (hasOwnProperty.call(synonymsHash, prop)) {
                result.push(prop);
            }
        }

        return result;
    }
};

module.exports = synonyms;<|endoftext|>


<commit_before>{
    init: function(elevators, floors) {
        this.elevators = elevators;
        this.floors = floors;
        this.pickupQueue = [];
        var self = this;

        for( var i = 0; i<floors.length; i++){
            var floor = floors[i];

            floor.on('up_button_pressed', function(){

                if( self.pickupQueue.indexOf(this.floorNum() == -1 )){
                    for( var j = 0; j<elevators.length; j++){
                        var elevator = elevators[j];

                        if( elevator.loadFactor() < 1  ){

                            self.setIndicators(elevator, true);

                            self.goToFloor( elevator, this.floorNum());
                            self.pickupQueue.push(this.floorNum());
                            break;
                        }
                    }
                }
            });

            floor.on('down_button_pressed', function(){
                if( self.pickupQueue.indexOf(this.floorNum() == -1 )){
                    for( var j = 0; j<elevators.length; j++){

                        var elevator = elevators[elevators.length - j -1 ];

                        if( elevator.loadFactor() < 1 ){

                            self.setIndicators(elevator, false);

                            self.goToFloor( elevator, this.floorNum() );
                            self.pickupQueue.push(this.floorNum());
                            break;
                        }
                    }
                }
            });

        }

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

            var elevator = elevators[i];
            elevator.requestQueue = [];

            elevator.on('floor_button_pressed ', function(floorNum){
               if( this.destinationQueue.indexOf(floorNum) == -1 && this.destinationQueue.length < floors.length ){

                   self.removeDestinationFromOthers(floorNum);
                   this.requestQueue.push(floorNum);

                     self.goToFloor(this, floorNum);
               }

           });

            elevator.on("idle", function() {
                // The elevator is idle, so let's go to all the floors (or did we forget one?)

                elevator.goToFloor(i);
            });

       /*    elevator.on('passing_floor', function(floorNum, direction){
                if( this.loadFactor() < 1 && this.destinationQueue.indexOf(floorNum) != -1 ){
                    this.goToFloor(floorNum, true);
                }
            });
*/

            elevator.on('stopped_at_floor', function(floorNum){
                self.removeDestination(this, floorNum, true);

                 var index =self.pickupQueue.indexOf(floorNum);

                if( index > -1 ){
                    self.pickupQueue.splice(index, 1);
                }

                if( floorNum == 0 ){
                    self.setIndicators(elevator, true);
                }else if ( floorNum == floors.length ){
                    self.setIndicators(elevator, false);
                }

            });

        }

    },
    setIndicators: function(elevator, up){
        console.log('indicate', up);
         elevator.goingUpIndicator(up);
        elevator.goingDownIndicator(!up);
    },
    update: function(dt, elevators, floors) {
        // We normally don't need to do anything here
    },
    goToFloor: function( elevator, floorNum ){
        elevator.destinationQueue.push(floorNum);
        var above = [];
        var below = [];

        var aboveIndex = 0; // the index in the queue where the floors are above the current floor
        for( var i = 0; i< elevator.destinationQueue.length; i++ ){
            if( elevator.destinationQueue[i] > elevator.currentFloor() ){
                above.push(elevator.destinationQueue[i]);
            }else if ( elevator.destinationQueue[i] < elevator.currentFloor() ){
                below.push(elevator.destinationQueue[i]);
            }
        }

        above.sort(); // Sort ascending
        below.sort(); // Sort descending
        below.reverse();

        var newQueue;

        if ( below.length > above.length ){
            // More floors are below the current floor than above, go down first
            console.log('down',  elevator.currentFloor(), above, below);

           newQueue = below.concat(above);
        }else{
            console.log('up', elevator.currentFloor(), above, below);

            newQueue= above.concat(below);
        }

        elevator.destinationQueue = newQueue;
        elevator.checkDestinationQueue();
    },
    notInQueue: function(floorNum){
        for( var i =0;i<this.elevators.length; i++){
            var elevator = this.elevators[i];

            console.log('q', elevator);

            if( elevator.loadFactor < 1 && elevator.destinationQueue.indexOf(floorNum) != -1 ){
                console.log(queue, elevator.loadFactor);
                return false;
            }

        }

        return true;
    },
    removeDestinationFromOthers: function(elevator, floorNum){
         for( var i =0;i<this.elevators.length; i++){
            if( elevator != this.elevators[i]){
                this.removeDestination( this.elevators[i], floorNum );
            }

        }
    },
    floorRequested: function(elevator, floorNum ){
        return elevator.requestQueue.indexOf(floorNum);
    },
    removeDestination: function(elevator, floorNum, force ){
        if( force || !this.floorRequested( elevator, floorNum) ){
            var index = elevator.destinationQueue.indexOf(floorNum);

            if( index > -1 ){

                elevator.destinationQueue.splice(index, 1);
                elevator.checkDestinationQueue();
            }

            var index2 = elevator.requestQueue.indexOf(floorNum);

            if( index2 > -1 ){

                elevator.requestQueue.splice(index2, 1);
            }
        }
    }
}<commit_msg>Cleanup<commit_after>{
    init: function(elevators, floors) {
        this.elevators = elevators;
        this.floors = floors;
        this.pickupQueue = [];
        var self = this;

        for( var i = 0; i<floors.length; i++){
            var floor = floors[i];

            floor.on('up_button_pressed down_button_pressed', function(){

              //  if( self.pickupQueue.indexOf(this.floorNum() == -1 )){
                    var elevator = self.pickPickupElevator(this.floorNum());

                     self.setIndicators(elevator, true);

                    self.goToFloor( elevator, this.floorNum());
                    self.pickupQueue.push(this.floorNum());
               // }
            });
        }

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

            var elevator = elevators[i];
            elevator.requestQueue = [];

            elevator.on('floor_button_pressed', function(floorNum){
               if( this.destinationQueue.indexOf(floorNum) == -1 && this.destinationQueue.length < floors.length ){

                   self.removeDestinationFromOthers(floorNum);
                   this.requestQueue.push(floorNum);

                     self.goToFloor(this, floorNum);
               }

           });

           /*
            elevator.on("idle", function() {
                // The elevator is idle, so let's go to all the floors (or did we forget one?)

                elevator.goToFloor(i);
            });
*/

       /*    elevator.on('passing_floor', function(floorNum, direction){
                if( this.loadFactor() < 1 && this.destinationQueue.indexOf(floorNum) != -1 ){
                    this.goToFloor(floorNum, true);
                }
            });
*/

            elevator.on('stopped_at_floor', function(floorNum){
                self.removeDestination(this, floorNum, true);

                 var index =self.pickupQueue.indexOf(floorNum);

                if( index > -1 ){
                    self.pickupQueue.splice(index, 1);
                }

                if( floorNum == 0 ){
                    self.setIndicators(elevator, true);
                }else if ( floorNum == floors.length ){
                    self.setIndicators(elevator, false);
                }

            });

        }

    },
    setIndicators: function(elevator, up){
 /*       console.log('indicate', up);
         elevator.goingUpIndicator(up);
        elevator.goingDownIndicator(!up);
        */
    },
    update: function(dt, elevators, floors) {
        // We normally don't need to do anything here
    },
    goToFloor: function( elevator, floorNum ){
        elevator.destinationQueue.push(floorNum);
        var above = [];
        var below = [];

        var aboveIndex = 0; // the index in the queue where the floors are above the current floor
        for( var i = 0; i< elevator.destinationQueue.length; i++ ){
            if( elevator.destinationQueue[i] > elevator.currentFloor() ){
                above.push(elevator.destinationQueue[i]);
            }else if ( elevator.destinationQueue[i] < elevator.currentFloor() ){
                below.push(elevator.destinationQueue[i]);
            }
        }

        above.sort(); // Sort ascending
        below.sort(); // Sort descending
        below.reverse();

        var newQueue;

        if ( below.length > above.length ){
            // More floors are below the current floor than above, go down first
            console.log('down',  elevator.currentFloor(), above, below);

           newQueue = below.concat(above);
        }else{
            console.log('up', elevator.currentFloor(), above, below);

            newQueue= above.concat(below);
        }

        elevator.destinationQueue = newQueue;
        elevator.checkDestinationQueue();

          console.log('destination', elevator.destinationQueue);
    },
    notInQueue: function(floorNum){
        for( var i =0;i<this.elevators.length; i++){
            var elevator = this.elevators[i];

            console.log('q', elevator);

            if( elevator.loadFactor < 1 && elevator.destinationQueue.indexOf(floorNum) != -1 ){
                console.log(queue, elevator.loadFactor);
                return false;
            }

        }

        return true;
    },
    removeDestinationFromOthers: function(elevator, floorNum){
         for( var i =0;i<this.elevators.length; i++){
            if( elevator != this.elevators[i]){
                this.removeDestination( this.elevators[i], floorNum );
            }

        }
    },
    floorRequested: function(elevator, floorNum ){
        return elevator.requestQueue.indexOf(floorNum);
    },
    removeDestination: function(elevator, floorNum, force ){
        if( force || !this.floorRequested( elevator, floorNum) ){
            var index = elevator.destinationQueue.indexOf(floorNum);

            if( index > -1 ){

                elevator.destinationQueue.splice(index, 1);
                elevator.checkDestinationQueue();
            }

            var index2 = elevator.requestQueue.indexOf(floorNum);

            if( index2 > -1 ){

                elevator.requestQueue.splice(index2, 1);
            }
        }
    },
    pickPickupElevator: function( floorNum ){

        for( var j = 0; j< this.elevators.length; j++){
            var elevator = this.elevators[j];

            if( elevator.loadFactor() < 1  ){
                return elevator;

                break;
            }
        }

        // All elevators are full
        return this.elevators[0];
    }
}<|endoftext|>


<commit_before>/*
 * Copyright (c) 2014 airbug inc. http://airbug.com
 *
 * bugcore may be freely distributed under the MIT license.
 */

//-------------------------------------------------------------------------------
// Common Modules
//-------------------------------------------------------------------------------

var bugcore             = require('bugcore');
var bugflow             = require('bugflow');
var bugfs               = require('bugfs');
var buildbug            = require('buildbug');

//-------------------------------------------------------------------------------
// Simplify References
//-------------------------------------------------------------------------------

var enableModule        = buildbug.enableModule;
var TypeUtil            = bugcore.TypeUtil;
var $series             = bugflow.$series;
var $task               = bugflow.$task;

//-------------------------------------------------------------------------------
// Enable Modules
//-------------------------------------------------------------------------------

var lintbug             = enableModule("lintbug");

//-------------------------------------------------------------------------------
// Lint Tasks
//-------------------------------------------------------------------------------

//NOTE BRN: This only works for JS files
lintbug.lintTask("updateCopyright", function(lintFile, callback) {
    var fileContents    = lintFile.getFileContents();
    var copyright       = getCopyright();
    var copyrightRegex  = /^(\s*)\/\*(([^.]|[.])+?)Copyright \(c\)(([^.]|[.])+?)\*\/(\s*)/;
    if (copyrightRegex.test(fileContents)) {
        fileContents = fileContents.replace(copyrightRegex, copyright + "\n\n");
    } else {
        fileContents = copyright + "\n\n" + fileContents;
    }
    lintFile.setFileContents(fileContents);
    callback();
});

lintbug.lintTask("orderRequireAnnotations", function(lintFile, callback) {
    var fileContents    = lintFile.getFileContents();
    fileContents = sortRequireAnnotations(fileContents);
    lintFile.setFileContents(fileContents);
    callback();
});

lintbug.lintTask("orderBugPackRequiresInFile", function(jsFilePath, callback) {
    //var Class                   = bugpack.require('Class');
});

//-------------------------------------------------------------------------------
// Helper Methods
//-------------------------------------------------------------------------------

var copyright = null;

/**
 * @private
 * @return {string}
 */
var getCopyright = function() {
    if (copyright === null) {
        var copyrightText   = bugcore.StringUtil.trim(bugfs.readFileSync(__dirname + "/COPYRIGHT", 'utf8'));
        var copyrightLines  = copyrightText.split("\n");
        copyright = "/*\n";
        copyrightLines.forEach(function(copyrightLine) {
            copyrightLine = bugcore.StringUtil.trim(copyrightLine);
            if (copyrightLine !== "") {
                copyright += " * " + copyrightLine + "\n";
            } else {
                copyright += " *\n";
            }
        });
        copyright += " */\n";
    }
    return copyright;
};

/**
 * @private
 * @param {string} argumentsString
 * @return {Array.<(string|number)>}
 */
var parseArguments = function(argumentsString) {
    var args = [];
    var parts = argumentsString.split(',');
    parts.forEach(function(part) {
        var results = part.match(/\s*('|")(.*?)\1\s*/);
        if (results) {
            args.push(results[2]);
        } else {
            var num = parseFloat(part);
            if (isNaN(num)) {
                throw new Error("Could not parse parameter '" + part + "'");
            }
            args.push(num);
        }
    });
    return args;
};

/**
 * @private
 * @param {string} text
 * @return {string}
 */
var parseString = function(text) {
    var results = text.match(/\s*('|")(.*?)\1\s*/);
    if (results) {
        return results[2];
    }
    return null;
};

/**
 * @private
 * @param {string} fileContents
 * @return {string}
 */
var sortRequireAnnotations = function(fileContents) {
    var lines   = bugcore.StringUtil.split(fileContents, "\n", function(line, index) {
        return {
            index: index,
            line: line
        };
    });
    lines.sort(function(a, b) {
        var resultsA = a.line.match(/^\s*\/\/\s*@Require\(('|")((?:\w|\.)*)\1\)\s*$/);
        var resultsB = b.line.match(/^\s*\/\/\s*@Require\(('|")((?:\w|\.)*)\1\)\s*$/);

        if (resultsA && resultsB) {
            var partsA = resultsA[2].split(".");
            var partsB = resultsB[2].split(".");
            var classNameA = partsA.pop();
            var classNameB = partsB.pop();
            var packageNameA = partsA.join(".");
            var packageNameB = partsB.join(".");
            if (packageNameA < packageNameB) {
                return -1;
            }
            if (packageNameA > packageNameB) {
                return 1;
            }
            if (classNameA < classNameB) {
                return -1;
            }
            if (classNameB > classNameA) {
                return 1;
            }
        } else {
            if (a.index < b.index) {
                return -1;
            }
            return 1;
        }
    });
    var result = "";
    var first = true;
    lines.forEach(function(line) {
        if (first) {
            result += line.line;
        } else {
            result += "\n" + line.line;
        }
    });
    return result;
};<commit_msg>Fix bug in lintbug script<commit_after>/*
 * Copyright (c) 2014 airbug inc. http://airbug.com
 *
 * bugcore may be freely distributed under the MIT license.
 */

//-------------------------------------------------------------------------------
// Common Modules
//-------------------------------------------------------------------------------

var bugcore             = require('bugcore');
var bugflow             = require('bugflow');
var bugfs               = require('bugfs');
var buildbug            = require('buildbug');

//-------------------------------------------------------------------------------
// Simplify References
//-------------------------------------------------------------------------------

var enableModule        = buildbug.enableModule;
var TypeUtil            = bugcore.TypeUtil;
var $series             = bugflow.$series;
var $task               = bugflow.$task;

//-------------------------------------------------------------------------------
// Enable Modules
//-------------------------------------------------------------------------------

var lintbug             = enableModule("lintbug");

//-------------------------------------------------------------------------------
// Lint Tasks
//-------------------------------------------------------------------------------

//NOTE BRN: This only works for JS files
lintbug.lintTask("updateCopyright", function(lintFile, callback) {
    var fileContents    = lintFile.getFileContents();
    var copyright       = getCopyright();
    var copyrightRegex  = /^(\s*)\/\*(([^.]|[.])+?)Copyright \(c\)(([^.]|[.])+?)\*\/(\s*)/;
    if (copyrightRegex.test(fileContents)) {
        fileContents = fileContents.replace(copyrightRegex, copyright + "\n\n");
    } else {
        fileContents = copyright + "\n\n" + fileContents;
    }
    lintFile.setFileContents(fileContents);
    callback();
});

lintbug.lintTask("orderRequireAnnotations", function(lintFile, callback) {
    var fileContents    = lintFile.getFileContents();
    fileContents = sortRequireAnnotations(fileContents);
    lintFile.setFileContents(fileContents);
    callback();
});

lintbug.lintTask("orderBugPackRequiresInFile", function(jsFilePath, callback) {
    //var Class                   = bugpack.require('Class');
});

//-------------------------------------------------------------------------------
// Helper Methods
//-------------------------------------------------------------------------------

var copyright = null;

/**
 * @private
 * @return {string}
 */
var getCopyright = function() {
    if (copyright === null) {
        var copyrightText   = bugcore.StringUtil.trim(bugfs.readFileSync(__dirname + "/COPYRIGHT", 'utf8'));
        var copyrightLines  = copyrightText.split("\n");
        copyright = "/*\n";
        copyrightLines.forEach(function(copyrightLine) {
            copyrightLine = bugcore.StringUtil.trim(copyrightLine);
            if (copyrightLine !== "") {
                copyright += " * " + copyrightLine + "\n";
            } else {
                copyright += " *\n";
            }
        });
        copyright += " */\n";
    }
    return copyright;
};

/**
 * @private
 * @param {string} argumentsString
 * @return {Array.<(string|number)>}
 */
var parseArguments = function(argumentsString) {
    var args = [];
    var parts = argumentsString.split(',');
    parts.forEach(function(part) {
        var results = part.match(/\s*('|")(.*?)\1\s*/);
        if (results) {
            args.push(results[2]);
        } else {
            var num = parseFloat(part);
            if (isNaN(num)) {
                throw new Error("Could not parse parameter '" + part + "'");
            }
            args.push(num);
        }
    });
    return args;
};

/**
 * @private
 * @param {string} text
 * @return {string}
 */
var parseString = function(text) {
    var results = text.match(/\s*('|")(.*?)\1\s*/);
    if (results) {
        return results[2];
    }
    return null;
};

/**
 * @private
 * @param {string} fileContents
 * @return {string}
 */
var sortRequireAnnotations = function(fileContents) {
    var lines   = bugcore.StringUtil.split(fileContents, "\n", function(line, index) {
        return {
            index: index,
            line: line
        };
    });
    lines.sort(function(a, b) {
        var resultsA = a.line.match(/^\s*\/\/\s*@Require\(('|")((?:\w|\.)*)\1\)\s*$/);
        var resultsB = b.line.match(/^\s*\/\/\s*@Require\(('|")((?:\w|\.)*)\1\)\s*$/);

        if (resultsA && resultsB) {
            var partsA = resultsA[2].split(".");
            var partsB = resultsB[2].split(".");
            var classNameA = partsA.pop();
            var classNameB = partsB.pop();
            var packageNameA = partsA.join(".");
            var packageNameB = partsB.join(".");
            if (packageNameA < packageNameB) {
                return -1;
            }
            if (packageNameA > packageNameB) {
                return 1;
            }
            if (classNameA < classNameB) {
                return -1;
            }
            if (classNameB > classNameA) {
                return 1;
            }
        } else {
            if (a.index < b.index) {
                return -1;
            }
            return 1;
        }
    });
    var result = "";
    var first = true;
    lines.forEach(function(line) {
        if (first) {
            result += line.line;
            first = false;
        } else {
            result += "\n" + line.line;
        }
    });
    return result;
};<|endoftext|>


<commit_before>
;(function (root) {
  'use strict'

  var NS,

      cards = [
        '2C', '3C', '4C', '5C', '6C', '7C', '8C', '9C', 'TC', 'JC', 'QC', 'KC', 'AC',
        '2D', '3D', '4D', '5D', '6D', '7D', '8D', '9D', 'TD', 'JD', 'QD', 'KD', 'AD',
        '2H', '3H', '4H', '5H', '6H', '7H', '8H', '9H', 'TH', 'JH', 'QH', 'KH', 'AH',
        '2S', '3S', '4S', '5S', '6S', '7S', '8S', '9S', 'TS', 'JS', 'QS', 'KS', 'AS'
      ],

      SIZE = cards.length,

      MAX_JOKERS = 4

  // @namespace
  root.DISBRANDED = (typeof root.DISBRANDED === 'undefined') ? {} : root.DISBRANDED
  root.DISBRANDED.poker = root.DISBRANDED.poker || {}
  NS = root.DISBRANDED.poker

  NS.Deck = function (options) {
    var i, len

    options = options || {}
    this._cards = []
    this._in = []
    this._out = []

    // add cards
    for (i = 0, len = cards.length; i < len; i++) {
      this._cards[i] = cards[i]
    }

    // add jokers
    this._jokers = options.jokers ? Math.max(0, Math.min(MAX_JOKERS, parseInt(options.jokers, 10))) : 0
    for (i = 0, len = this._jokers; i < len; i++) {
      this._cards[i + SIZE] = 'J' + (i + 1)
    }

    this.reset(true)
  }

  NS.Deck.prototype = {

    /**
     * @returns undealt cards remaining in deck.
     */
    cards: function () {
      return this._in
    },

    /**
     * @returns number of cards in the deck.
     */
    count: function () {
      return SIZE + this.jokers()
    },

    /**
     * @returns number of jokers in the deck.
     */
    jokers: function () {
      return this._jokers
    },

    /**
     * Remove all jokers from the deck.
     */
    removeJokers: function () {
      this.reset()
      this._cards.splice(SIZE, this._jokers)
      this._jokers = 0
      this.reset(true)
      return this
    },

    /**
     * Add up to 4 jokers to the deck.
     * @param {number} num Number of jokers to add.
     */
    addJokers: function (num) {
      var jokers = this.jokers(),
          num = Math.max(0, Math.min(MAX_JOKERS, parseInt(num, 10))),
          index = jokers + 1
      while (jokers < MAX_JOKERS && num > 0) {
        this._cards.push('J' + index++)
        num--
        jokers ++
      }
      this._jokers = jokers
      this.reset(true)
      return this
    },

    /**
     * Reset the deck to its original unshuffled order.
     */
    reset: function (override) {
      var i, len
      if (!!override || !this._reset) {
        this._in = []
        this._out = []
        for (i = 0, len = this._cards.length; i < len; i++) {
          this._in[i] = this._cards[i]
        }
        this._reset = true
      }
      return this
    },

    /**
     * Shuffle/randomize the deck.
     */
    shuffle: function () {
      var len,
          temp = []
      this.reset()
      while ((len = this._in.length) > 0) {
        temp.push(this._in.splice(Math.floor(Math.random() * len), 1)[0])
      }
      this._in = temp
      this._reset = false
      return this
    },

    /**
     * Deal next card in the deck.
     * @returns the card dealt.
     */
    deal: function () {
      var card = null
      if (this._in.length === 0) {
        return card
      }
      card = this._in.shift()
      this._out.unshift(card)
      this._reset = false
      return card
    }
  }
}(this));<commit_msg>changed J for joker to W for wild to avoid confusion with J for jack<commit_after>
;(function (root) {
  'use strict'

  var NS,

      cards = [
        '2C', '3C', '4C', '5C', '6C', '7C', '8C', '9C', 'TC', 'JC', 'QC', 'KC', 'AC',
        '2D', '3D', '4D', '5D', '6D', '7D', '8D', '9D', 'TD', 'JD', 'QD', 'KD', 'AD',
        '2H', '3H', '4H', '5H', '6H', '7H', '8H', '9H', 'TH', 'JH', 'QH', 'KH', 'AH',
        '2S', '3S', '4S', '5S', '6S', '7S', '8S', '9S', 'TS', 'JS', 'QS', 'KS', 'AS'
      ],

      SIZE = cards.length,

      JOKER = 'W',
      MAX_JOKERS = 4

  // @namespace
  root.DISBRANDED = (typeof root.DISBRANDED === 'undefined') ? {} : root.DISBRANDED
  root.DISBRANDED.poker = root.DISBRANDED.poker || {}
  NS = root.DISBRANDED.poker

  NS.Deck = function (options) {
    var i, len

    options = options || {}
    this._cards = []
    this._in = []
    this._out = []

    // add cards
    for (i = 0, len = cards.length; i < len; i++) {
      this._cards[i] = cards[i]
    }

    // add jokers
    this._jokers = options.jokers ? Math.max(0, Math.min(MAX_JOKERS, parseInt(options.jokers, 10))) : 0
    for (i = 0, len = this._jokers; i < len; i++) {
      this._cards[i + SIZE] = 'JOKER' + (i + 1)
    }

    this.reset(true)
  }

  NS.Deck.prototype = {

    /**
     * @returns undealt cards remaining in deck.
     */
    cards: function () {
      return this._in
    },

    /**
     * @returns number of cards in the deck.
     */
    count: function () {
      return SIZE + this.jokers()
    },

    /**
     * @returns number of jokers in the deck.
     */
    jokers: function () {
      return this._jokers
    },

    /**
     * Remove all jokers from the deck.
     */
    removeJokers: function () {
      this.reset()
      this._cards.splice(SIZE, this._jokers)
      this._jokers = 0
      this.reset(true)
      return this
    },

    /**
     * Add up to 4 jokers to the deck.
     * @param {number} num Number of jokers to add.
     */
    addJokers: function (num) {
      var jokers = this.jokers(),
          num = Math.max(0, Math.min(MAX_JOKERS, parseInt(num, 10))),
          index = jokers + 1
      while (jokers < MAX_JOKERS && num > 0) {
        this._cards.push(JOKER + index++)
        num--
        jokers ++
      }
      this._jokers = jokers
      this.reset(true)
      return this
    },

    /**
     * Reset the deck to its original unshuffled order.
     */
    reset: function (override) {
      var i, len
      if (!!override || !this._reset) {
        this._in = []
        this._out = []
        for (i = 0, len = this._cards.length; i < len; i++) {
          this._in[i] = this._cards[i]
        }
        this._reset = true
      }
      return this
    },

    /**
     * Shuffle/randomize the deck.
     */
    shuffle: function () {
      var len,
          temp = []
      this.reset()
      while ((len = this._in.length) > 0) {
        temp.push(this._in.splice(Math.floor(Math.random() * len), 1)[0])
      }
      this._in = temp
      this._reset = false
      return this
    },

    /**
     * Deal next card in the deck.
     * @returns the card dealt.
     */
    deal: function () {
      var card = null
      if (this._in.length === 0) {
        return card
      }
      card = this._in.shift()
      this._out.unshift(card)
      this._reset = false
      return card
    }
  }
}(this));<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";

var EXPORTED_SYMBOLS = ["BASE_PATH", "require", "unload", "_setupLoader"];

const {
  classes: Cc,
  interfaces: Ci,
  utils: Cu,
  Constructor: ctor
} = Components;
const {
  getWeakReference: weak,
  reportError: reportError
} = Cu;

Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");

const lazy = XPCOMUtils.defineLazyGetter;

// hide our internals
// Since require() uses .scriptloader, the loaded require scopes will have
// access to the named stuff within this module scope, but we actually want
// them to have access to certain stuff.
(function setup_scope(exports) {
  Services = exports.Services = Object.create(Services);
  let dlsg = XPCOMUtils.defineLazyServiceGetter.bind(XPCOMUtils, Services);
  dlsg("catman", "@mozilla.org/categorymanager;1", "nsICategoryManager");
  dlsg("clipbrd", "@mozilla.org/widget/clipboard;1", "nsIClipboard");
  dlsg("drags", "@mozilla.org/widget/dragservice;1", "nsIDragService");
  dlsg("eps", "@mozilla.org/uriloader/external-protocol-service;1", "nsIExternalProtocolService");
  dlsg("fixups", "@mozilla.org/docshell/urifixup;1", "nsIURIFixup");
  dlsg("memrm", "@mozilla.org/memory-reporter-manager;1", "nsIMemoryReporterManager");
  dlsg("mime", "@mozilla.org/uriloader/external-helper-app-service;1", "nsIMIMEService");
  dlsg("mimeheader", "@mozilla.org/network/mime-hdrparam;1", "nsIMIMEHeaderParam");
  dlsg("uuid", "@mozilla.org/uuid-generator;1", "nsIUUIDGenerator");

  const Instances = exports.Instances = {
    get: function I_get(symbol, contract, iface, initializer) {
      if (!(symbol in this)) {
        this.register(symbol, contract, iface, initializer);
      }
      return this[symbol];
    },
    register: function I_register(symbol, contract, iface, initializer) {
      if (initializer) {
        lazy(this, symbol, function() ctor(contract, iface, initializer));
        lazy(this, symbol + "_p", function() ctor(contract, iface));
      }
      else {
        lazy(this, symbol, function() ctor(contract, iface));
        lazy(this, symbol.toLowerCase(), function() new (ctor(contract, iface))());
      }
    }
  };

  const {SELF_PATH, BASE_PATH} = (function() {
    let rv;
    try { throw new Error("narf"); }
    catch (ex) {
      rv = {
        SELF_PATH: ex.fileName,
        BASE_PATH: /^(.+\/).*?$/.exec(ex.fileName)[1]
      };
    }
    return rv;
  })();
  exports.BASE_PATH = BASE_PATH;

  // logging stubs
  var log = function() {} // stub
  var LOG_DEBUG = 0, LOG_INFO = 0, LOG_ERROR = 0;

  var _unloaders = [];
  let _runUnloader = function _runUnloader(fn, args) {
      try {
        fn.apply(null, args);
      }
      catch (ex) {
        log(LOG_ERROR, "unloader failed", ex);
      }
  }
  exports.unload = function unload(fn) {
    if (fn == "shutdown") {
      log(LOG_INFO, "shutdown");
      for (let i = _unloaders.length; ~(--i);) {
        _runUnloader(_unloaders[i]);
      }
      _unloaders.splice(0);
      return;
    }

    // add an unloader
    if (typeof(fn) != "function") {
      throw new Error("unloader is not a function");
    }
    _unloaders.push(fn);
    return function() {
      _runUnloader(fn, arguments);
      _unloaders = _unloaders.filter(function(c) c != fn);
    };
  }

  const _registry = Object.create(null);
  exports.require = function require(module) {
    module = BASE_PATH + module + ".js";

    // already loaded?
    if (module in _registry) {
      return _registry[module];
    }

    // try to load the module
    log(LOG_DEBUG, "going to load: " + module);
    let scope = {exports: Object.create(null)};
    try {
      Services.scriptloader.loadSubScript(module, scope);
    }
    catch (ex) {
      log(LOG_ERROR, "failed to load " + module, ex);
      throw ex;
    }

    _registry[module] = scope.exports;
    log(LOG_DEBUG, "loaded module: " + module);

    return scope.exports;
  }
  exports.lazyRequire = function lazyRequire(module) {
    function lazyBind(props, prop) {
      log(LOG_DEBUG, "lazily binding " + props + " for module " + module);
      let m = require(module);
      for (let [,p] in Iterator(props)) {
        delete this[p];
        this[prop] = m[p];
      }
      return this[prop];
    }

    // Already loaded?
    if (module in _registry) {
      log(LOG_DEBUG, "not lazily binding " + module + "; already loaded");
      return _registry[module];
    }

    let props = Array.slice(arguments, 1);
    let rv = {};
    let binder = lazyBind.bind(rv, props);
    for (let [,p] in Iterator(props)) {
      let _p = p;
      lazy(rv, _p, function() binder(_p));
    }
    return rv;
  }

  unload(function() {
    let keys = Object.keys(_registry);
    for (let i = keys.length; ~(--i);) {
      delete _registry[keys[i]];
    }
    // unload ourselves
    Cu.unload(SELF_PATH);
  });

  exports._setupLoader = function _setupLoader(data, callback) {
    delete exports._setupLoader;

    let _am = {};
    Cu.import("resource://gre/modules/AddonManager.jsm", _am);
    _am.AddonManager.getAddonByID(data.id, function loader_startup(addon) {
      let logging;
      try {
        logging = require("logging");
        for (let [k,v] in Iterator(logging)) {
          exports[k] = v;
        }
        logging.init(addon.name);
        delete logging.init;

        let prefs = require("preferences");
        prefs.init(addon.id);
        delete prefs.init;
        exports.prefs = prefs.prefs;
        exports.globalPrefs = prefs.globalPrefs;

        try {
          prefs.prefs.observe("loglevel", function(p,v) logging.setLogLevel(v));
        }
        catch (ex) {
          logging.log(logging.LOG_ERROR, "failed to set log level", ex);
        }
      }
      catch (ex) {
        // probably do not have a working log() yet
        reportError(ex);
        return;
      }

      try {
        if (callback) {
          logging.log(logging.LOG_DEBUG, "loader: running callback");
          callback();
        }
      }
      catch (ex) {
        logging.log(logging.LOG_ERROR, "callback failed!", ex);
      }
      logging.log(logging.LOG_DEBUG, "loader: done");
    });
  }

})(this);

/* vim: set et ts=2 sw=2 : */
<commit_msg>Fix lazyRequire to properly bind all properties<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";

var EXPORTED_SYMBOLS = ["BASE_PATH", "require", "unload", "_setupLoader"];

const {
  classes: Cc,
  interfaces: Ci,
  utils: Cu,
  Constructor: ctor
} = Components;
const {
  getWeakReference: weak,
  reportError: reportError
} = Cu;

Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");

const lazy = XPCOMUtils.defineLazyGetter;

// hide our internals
// Since require() uses .scriptloader, the loaded require scopes will have
// access to the named stuff within this module scope, but we actually want
// them to have access to certain stuff.
(function setup_scope(exports) {
  Services = exports.Services = Object.create(Services);
  let dlsg = XPCOMUtils.defineLazyServiceGetter.bind(XPCOMUtils, Services);
  dlsg("catman", "@mozilla.org/categorymanager;1", "nsICategoryManager");
  dlsg("clipbrd", "@mozilla.org/widget/clipboard;1", "nsIClipboard");
  dlsg("drags", "@mozilla.org/widget/dragservice;1", "nsIDragService");
  dlsg("eps", "@mozilla.org/uriloader/external-protocol-service;1", "nsIExternalProtocolService");
  dlsg("fixups", "@mozilla.org/docshell/urifixup;1", "nsIURIFixup");
  dlsg("memrm", "@mozilla.org/memory-reporter-manager;1", "nsIMemoryReporterManager");
  dlsg("mime", "@mozilla.org/uriloader/external-helper-app-service;1", "nsIMIMEService");
  dlsg("mimeheader", "@mozilla.org/network/mime-hdrparam;1", "nsIMIMEHeaderParam");
  dlsg("uuid", "@mozilla.org/uuid-generator;1", "nsIUUIDGenerator");

  const Instances = exports.Instances = {
    get: function I_get(symbol, contract, iface, initializer) {
      if (!(symbol in this)) {
        this.register(symbol, contract, iface, initializer);
      }
      return this[symbol];
    },
    register: function I_register(symbol, contract, iface, initializer) {
      if (initializer) {
        lazy(this, symbol, function() ctor(contract, iface, initializer));
        lazy(this, symbol + "_p", function() ctor(contract, iface));
      }
      else {
        lazy(this, symbol, function() ctor(contract, iface));
        lazy(this, symbol.toLowerCase(), function() new (ctor(contract, iface))());
      }
    }
  };

  const {SELF_PATH, BASE_PATH} = (function() {
    let rv;
    try { throw new Error("narf"); }
    catch (ex) {
      rv = {
        SELF_PATH: ex.fileName,
        BASE_PATH: /^(.+\/).*?$/.exec(ex.fileName)[1]
      };
    }
    return rv;
  })();
  exports.BASE_PATH = BASE_PATH;

  // logging stubs
  var log = function() {} // stub
  var LOG_DEBUG = 0, LOG_INFO = 0, LOG_ERROR = 0;

  var _unloaders = [];
  let _runUnloader = function _runUnloader(fn, args) {
      try {
        fn.apply(null, args);
      }
      catch (ex) {
        log(LOG_ERROR, "unloader failed", ex);
      }
  }
  exports.unload = function unload(fn) {
    if (fn == "shutdown") {
      log(LOG_INFO, "shutdown");
      for (let i = _unloaders.length; ~(--i);) {
        _runUnloader(_unloaders[i]);
      }
      _unloaders.splice(0);
      return;
    }

    // add an unloader
    if (typeof(fn) != "function") {
      throw new Error("unloader is not a function");
    }
    _unloaders.push(fn);
    return function() {
      _runUnloader(fn, arguments);
      _unloaders = _unloaders.filter(function(c) c != fn);
    };
  }

  const _registry = Object.create(null);
  exports.require = function require(module) {
    module = BASE_PATH + module + ".js";

    // already loaded?
    if (module in _registry) {
      return _registry[module];
    }

    // try to load the module
    log(LOG_DEBUG, "going to load: " + module);
    let scope = {exports: Object.create(null)};
    try {
      Services.scriptloader.loadSubScript(module, scope);
    }
    catch (ex) {
      log(LOG_ERROR, "failed to load " + module, ex);
      throw ex;
    }

    _registry[module] = scope.exports;
    log(LOG_DEBUG, "loaded module: " + module);

    return scope.exports;
  }
  exports.lazyRequire = function lazyRequire(module) {
    function lazyBind(props, prop) {
      log(LOG_DEBUG, "lazily binding " + props + " for module " + module);
      let m = require(module);
      for (let [,p] in Iterator(props)) {
        delete this[p];
        this[p] = m[p];
      }
      return this[prop];
    }

    // Already loaded?
    if (module in _registry) {
      log(LOG_DEBUG, "not lazily binding " + module + "; already loaded");
      return _registry[module];
    }

    let props = Array.slice(arguments, 1);
    let rv = {};
    let binder = lazyBind.bind(rv, props);
    for (let [,p] in Iterator(props)) {
      let _p = p;
      lazy(rv, _p, function() binder(_p));
    }
    return rv;
  }

  unload(function() {
    let keys = Object.keys(_registry);
    for (let i = keys.length; ~(--i);) {
      delete _registry[keys[i]];
    }
    // unload ourselves
    Cu.unload(SELF_PATH);
  });

  exports._setupLoader = function _setupLoader(data, callback) {
    delete exports._setupLoader;

    let _am = {};
    Cu.import("resource://gre/modules/AddonManager.jsm", _am);
    _am.AddonManager.getAddonByID(data.id, function loader_startup(addon) {
      let logging;
      try {
        logging = require("logging");
        for (let [k,v] in Iterator(logging)) {
          exports[k] = v;
        }
        logging.init(addon.name);
        delete logging.init;

        let prefs = require("preferences");
        prefs.init(addon.id);
        delete prefs.init;
        exports.prefs = prefs.prefs;
        exports.globalPrefs = prefs.globalPrefs;

        try {
          prefs.prefs.observe("loglevel", function(p,v) logging.setLogLevel(v));
        }
        catch (ex) {
          logging.log(logging.LOG_ERROR, "failed to set log level", ex);
        }
      }
      catch (ex) {
        // probably do not have a working log() yet
        reportError(ex);
        return;
      }

      try {
        if (callback) {
          logging.log(logging.LOG_DEBUG, "loader: running callback");
          callback();
        }
      }
      catch (ex) {
        logging.log(logging.LOG_ERROR, "callback failed!", ex);
      }
      logging.log(logging.LOG_DEBUG, "loader: done");
    });
  }

})(this);

/* vim: set et ts=2 sw=2 : */
<|endoftext|>


<commit_before>// ==UserScript==
// @name         TruyenCV downloader
// @namespace    http://devs.forumvi.com/
// @description  Tải truyện từ truyencv.com định dạng html. Sau đó, bạn có thể dùng Mobipocket Creator để tạo ebook prc
// @version      2.0.0
// @icon         http://i.imgur.com/o5cmtkU.png
// @author       Zzbaivong
// @license      MIT
// @match        http://truyencv.com/*/
// @require      https://code.jquery.com/jquery-3.2.0.min.js
// @require      https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/4.0.2/handlebars.min.js
// @require      https://greasyfork.org/scripts/20307-jszip-v2/code/jszip-v2.js?version=196156
// @require      https://greasyfork.org/scripts/29904-jszip-utils/code/jszip-utils.js?version=196137
// @require      https://greasyfork.org/scripts/18532-filesaver/code/FileSaver.js?version=164030
// @require      https://greasyfork.org/scripts/29905-js-epub-maker/code/js-epub-maker.js?version=196139
// @noframes
// @connect      self
// @supportURL   https://github.com/baivong/Userscript/issues
// @run-at       document-idle
// @grant        GM_xmlhttpRequest
// ==/UserScript==

/* global EpubMaker */
(function ($, window, document) {
    'use strict';

    /**
     * Hiển thị liên kết xem online cuối chương
     * [>]
     * @type {Boolean} true
     *                 false
     */
    var readOnline = true;

    function cleanHtml(str) {
        str = str.replace(/&nbsp\;/gm, ' ');
        str = str.replace(/<(br|hr|img)([^>]+)?>/gm, '<$1$2 />');
        return '
' + str + '
';
    }

    function downloadError(err) {
        $downloadStatus('danger');
        titleError.push(chapTitle);
        console.error(err);
    }

    function saveEbook() {
        if (endDownload) return;
        endDownload = true;

        if (titleError.length) {
            titleError = '
Các chương lỗi: ' + titleError.join(', ') + '
';
        } else {
            titleError = '';
        }
        beginEnd = '
Nội dung từ ' + begin + ' đến ' + end + '
';

        epubMaker.withSection(new EpubMaker.Section('note', 'note', {
            content: beginEnd + titleError + '

' + credits,
            title: 'Ghi chú'
        }, false, true));

        epubMaker.downloadEpub(function (epubZipContent, filename) {
            document.title = '[⇓] ' + ebookTitle;
            $win.off('beforeunload');
            $download.attr({
                href: window.URL.createObjectURL(epubZipContent),
                download: filename
            }).text('Tải xong').off('click');
            $downloadStatus('success');
        });
    }

    function getContent() {
        if (endDownload) return;
        chapId = chapList[count];

        GM_xmlhttpRequest({
            method: 'GET',
            url: pathname + chapId + '/',
            onload: function (response) {
                var $data = $(response.responseText),
                    $chapter = $data.find('#js-truyencv-content'),
                    $notContent = $chapter.find('script, style, a, div, p:last'),
                    $referrer = $chapter.find('[style]').filter(function () {
                        return (this.style.fontSize === '1px' || this.style.fontSize === '0px' || this.style.color === 'white');
                    });

                if (endDownload) return;

                chapTitle = $data.find('#js-truyencv-read-content .title').text().trim();

                if (!$chapter.length) {
                    downloadError('Missing content.');
                } else {
                    $downloadStatus('warning');

                    if ($notContent.length) $notContent.remove();
                    if ($referrer.length) $referrer.remove();

                    var $img = $chapter.find('img');
                    if ($img.length) $img.replaceWith(function () {
                        return '
Click để xem ảnh
';
                    });

                    epubMaker.withSection(new EpubMaker.Section('chapter', chapId, {
                        content: cleanHtml($chapter.html()) + chapRef(referrer + chapId),
                        title: chapTitle
                    }, true, false));

                    if (count === 0) begin = chapTitle;
                    end = chapTitle;

                    $download.html('Đang tải: ' + Math.floor((count / chapListSize) * 100) + '%');

                    count++;
                    document.title = '[' + count + '] ' + pageName;
                    if (count >= chapListSize) {
                        saveEbook();
                    } else {
                        getContent();
                    }
                }
            },
            onerror: function (err) {
                downloadError(err);
                saveEbook();
            }
        });
    }

    function downloadEbook() {
        var $infoBlock = $('.truyencv-detail-info-block'),
            $ebookType = $infoBlock.find('.categories a');

        ebookTitle = $infoBlock.find('h1').text().trim();
        ebookAuthor = $infoBlock.find('.author').text().trim();
        ebookCover = $infoBlock.find('.img-responsive').attr('src');

        if ($ebookType.length) {
            $ebookType.each(function () {
                ebookType.push($(this).text().trim());
            });
            ebookType = ebookType.join(', ');
            ebookType = '
Thể loại: ' + ebookType + '
';
        } else {
            ebookType = '';
        }

        epubMaker = new EpubMaker()
            .withUuid('github.com/baivong/Userscript::truyencv::' + pathname.slice(1, -1))
            .withTemplate('idpf-wasteland')
            .withAuthor(ebookAuthor)
            .withLanguage('vi')
            .withModificationDate(new Date)
            .withCover(ebookCover)
            .withTitle(ebookTitle);

        epubMaker.withSection(new EpubMaker.Section('introduction', 'info', {
            content: '
' + ebookTitle + '

Tác giả: ' + ebookAuthor + '
' + ebookType,
            title: 'Giới thiệu'
        }, false, true));

        epubMaker.withSection(new EpubMaker.Section('preamble', 'content', {
            content: cleanHtml($('#truyencv-detail-introduction .brief').html()),
            title: 'Nội dung'
        }, false, true));

        $win.on('beforeunload', function () {
            return 'Truyện đang được tải xuống...';
        });

        $download.one('click', function (e) {
            e.preventDefault();
            saveEbook();
        });

        getContent();
    }

    var pageName = document.title,
        $win = $(window),
        $download = $('', {
            class: 'btn btn-truyencv',
            href: '#download',
            text: 'Tải xuống'
        }),
        $downloadStatus = function (status) {
            $download.removeClass('btn-truyencv btn-primary btn-success btn-info btn-warning btn-danger').addClass('btn-' + status);
        },

        $novelId = $('.basic'),
        chapList = [],
        chapListSize = 0,
        chapId = '',
        chapTitle = '',
        pathname = location.pathname,
        count = 0,
        begin = '',
        end = '',
        endDownload = false,

        ebookTitle = '',
        ebookAuthor = '',
        ebookCover = '',
        ebookType = [],
        beginEnd = '',
        titleError = [],
        referrer = location.origin + pathname,
        credits = '
Truyện được tải từ TruyenCV

Userscript được viết bởi: Zzbaivong
',
        chapRef = function (ref) {
            return readOnline ? '
[>]
' : '';
        },

        epubMaker;

    if (!$novelId.length) return;

    $download.appendTo('.info .buttons');
    $download.one('click contextmenu', function (e) {
        e.preventDefault();
        var showChapList = $('.truyencv-detail-block a[href="#truyencv-detail-chap"]');

        document.title = '[...] Vui lòng chờ trong giây lát';

        showChapList = showChapList.attr('onclick');
        showChapList = showChapList.match(/\(([^\(\)]+)\)/)[1];
        showChapList = showChapList.match(/[^',]+/g);

        $.post('/index.php', {
            showChapter: 1,
            media_id: showChapList[0],
            number: showChapList[1],
            page: showChapList[2],
            type: showChapList[3]
        }).done(function (data) {
            chapList = data.match(/(?:href\=")[^"\)]+(?=")/g);
            chapList = chapList.reverse();
            chapList = chapList.map(function (val) {
                val = val.slice(6, -1);
                val = val.replace(referrer, '');
                return val;
            });

            if (e.type === 'contextmenu') {
                var startFrom = prompt('Nhập ID chương truyện bắt đầu tải:', chapList[0]);
                startFrom = chapList.indexOf(startFrom);
                if (startFrom !== -1) chapList = chapList.slice(startFrom);
            }

            chapListSize = chapList.length;
            if (chapListSize > 0) downloadEbook();
        }).fail(function (jqXHR, textStatus) {
            downloadError(textStatus);
        });
    });

})(jQuery, window, document);
<commit_msg>Update truyenCV_downloader.user.js<commit_after>// ==UserScript==
// @name         TruyenCV downloader
// @namespace    http://devs.forumvi.com/
// @description  Tải truyện từ truyencv.com định dạng html. Sau đó, bạn có thể dùng Mobipocket Creator để tạo ebook prc
// @version      2.0.0
// @icon         http://i.imgur.com/o5cmtkU.png
// @author       Zzbaivong
// @license      MIT
// @match        http://truyencv.com/*/
// @require      https://code.jquery.com/jquery-3.2.0.min.js
// @require      https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/4.0.2/handlebars.min.js
// @require      https://greasyfork.org/scripts/20307-jszip-v2/code/jszip-v2.js?version=196156
// @require      https://greasyfork.org/scripts/29904-jszip-utils/code/jszip-utils.js?version=196137
// @require      https://greasyfork.org/scripts/18532-filesaver/code/FileSaver.js?version=164030
// @require      https://greasyfork.org/scripts/29905-js-epub-maker/code/js-epub-maker.js?version=196139
// @noframes
// @connect      self
// @supportURL   https://github.com/baivong/Userscript/issues
// @run-at       document-idle
// @grant        GM_xmlhttpRequest
// ==/UserScript==

/* global EpubMaker */
(function ($, window, document) {
    'use strict';

    /**
     * Hiển thị liên kết xem online cuối chương
     * [>]
     * @type {Boolean} true
     *                 false
     */
    var readOnline = true;

    function cleanHtml(str) {
        str = str.replace(/&nbsp\;/gm, ' ');
        str = str.replace(/<(br|hr|img)([^>]+)?>/gm, '<$1$2 />');
        return '
' + str + '
';
    }

    function downloadError(err) {
        $downloadStatus('danger');
        titleError.push(chapTitle);
        console.error(err);
    }

    function saveEbook() {
        if (endDownload) return;
        endDownload = true;

        if (titleError.length) {
            titleError = '
Các chương lỗi: ' + titleError.join(', ') + '
';
        } else {
            titleError = '';
        }
        beginEnd = '
Nội dung từ ' + begin + ' đến ' + end + '
';

        epubMaker.withSection(new EpubMaker.Section('note', 'note', {
            content: beginEnd + titleError + '

' + credits,
            title: 'Ghi chú'
        }, false, true));

        epubMaker.downloadEpub(function (epubZipContent, filename) {
            document.title = '[⇓] ' + ebookTitle;
            $win.off('beforeunload');
            $download.attr({
                href: window.URL.createObjectURL(epubZipContent),
                download: filename
            }).text('Tải xong').off('click');
            $downloadStatus('success');
        });
    }

    function getContent() {
        if (endDownload) return;
        chapId = chapList[count];

        GM_xmlhttpRequest({
            method: 'GET',
            url: pathname + chapId + '/',
            onload: function (response) {
                var $data = $(response.responseText),
                    $chapter = $data.find('#js-truyencv-content'),
                    $notContent = $chapter.find('script, style, a, div, p:last'),
                    $referrer = $chapter.find('[style]').filter(function () {
                        return (this.style.fontSize === '1px' || this.style.fontSize === '0px' || this.style.color === 'white');
                    });

                if (endDownload) return;

                chapTitle = $data.find('#js-truyencv-read-content .title').text().trim();

                if (!$chapter.length) {
                    downloadError('Missing content.');
                } else {
                    $downloadStatus('warning');

                    if ($notContent.length) $notContent.remove();
                    if ($referrer.length) $referrer.remove();

                    var $img = $chapter.find('img');
                    if ($img.length) $img.replaceWith(function () {
                        return '
Click để xem ảnh
';
                    });

                    epubMaker.withSection(new EpubMaker.Section('chapter', chapId, {
                        content: cleanHtml($chapter.html()) + chapRef(referrer + chapId)),
                        title: chapTitle
                    }, true, false));

                    if (count === 0) begin = chapTitle;
                    end = chapTitle;

                    $download.html('Đang tải: ' + Math.floor((count / chapListSize) * 100) + '%');

                    count++;
                    document.title = '[' + count + '] ' + pageName;
                    if (count >= chapListSize) {
                        saveEbook();
                    } else {
                        getContent();
                    }
                }
            },
            onerror: function (err) {
                downloadError(err);
                saveEbook();
            }
        });
    }

    function downloadEbook() {
        var $infoBlock = $('.truyencv-detail-info-block'),
            $ebookType = $infoBlock.find('.categories a');

        ebookTitle = $infoBlock.find('h1').text().trim();
        ebookAuthor = $infoBlock.find('.author').text().trim();
        ebookCover = $infoBlock.find('.img-responsive').attr('src');

        if ($ebookType.length) {
            $ebookType.each(function () {
                ebookType.push($(this).text().trim());
            });
            ebookType = ebookType.join(', ');
            ebookType = '
Thể loại: ' + ebookType + '
';
        } else {
            ebookType = '';
        }

        epubMaker = new EpubMaker()
            .withUuid('github.com/baivong/Userscript::truyencv::' + pathname.slice(1, -1))
            .withTemplate('idpf-wasteland')
            .withAuthor(ebookAuthor)
            .withLanguage('vi')
            .withModificationDate(new Date)
            .withCover(ebookCover)
            .withTitle(ebookTitle);

        epubMaker.withSection(new EpubMaker.Section('introduction', 'info', {
            content: '
' + ebookTitle + '

Tác giả: ' + ebookAuthor + '
' + ebookType,
            title: 'Giới thiệu'
        }, false, true));

        epubMaker.withSection(new EpubMaker.Section('preamble', 'content', {
            content: cleanHtml($('#truyencv-detail-introduction .brief').html()),
            title: 'Nội dung'
        }, false, true));

        $win.on('beforeunload', function () {
            return 'Truyện đang được tải xuống...';
        });

        $download.one('click', function (e) {
            e.preventDefault();
            saveEbook();
        });

        getContent();
    }

    var pageName = document.title,
        $win = $(window),
        $download = $('', {
            class: 'btn btn-truyencv',
            href: '#download',
            text: 'Tải xuống'
        }),
        $downloadStatus = function (status) {
            $download.removeClass('btn-truyencv btn-primary btn-success btn-info btn-warning btn-danger').addClass('btn-' + status);
        },

        $novelId = $('.basic'),
        chapList = [],
        chapListSize = 0,
        chapId = '',
        chapTitle = '',
        pathname = location.pathname,
        count = 0,
        begin = '',
        end = '',
        endDownload = false,

        ebookTitle = '',
        ebookAuthor = '',
        ebookCover = '',
        ebookType = [],
        beginEnd = '',
        titleError = [],
        referrer = location.origin + pathname,
        credits = '
Truyện được tải từ TruyenCV

Userscript được viết bởi: Zzbaivong
',
        chapRef = function (ref) {
            return readOnline ? '
[>]
' : '';
        },

        epubMaker;

    if (!$novelId.length) return;

    $download.appendTo('.info .buttons');
    $download.one('click contextmenu', function (e) {
        e.preventDefault();
        var showChapList = $('.truyencv-detail-block a[href="#truyencv-detail-chap"]');

        document.title = '[...] Vui lòng chờ trong giây lát';

        showChapList = showChapList.attr('onclick');
        showChapList = showChapList.match(/\(([^\(\)]+)\)/)[1];
        showChapList = showChapList.match(/[^',]+/g);

        $.post('/index.php', {
            showChapter: 1,
            media_id: showChapList[0],
            number: showChapList[1],
            page: showChapList[2],
            type: showChapList[3]
        }).done(function (data) {
            chapList = data.match(/(?:href\=")[^"\)]+(?=")/g);
            chapList = chapList.reverse();
            chapList = chapList.map(function (val) {
                val = val.slice(6, -1);
                val = val.replace(referrer, '');
                return val;
            });

            if (e.type === 'contextmenu') {
                var startFrom = prompt('Nhập ID chương truyện bắt đầu tải:', chapList[0]);
                startFrom = chapList.indexOf(startFrom);
                if (startFrom !== -1) chapList = chapList.slice(startFrom);
            }

            chapListSize = chapList.length;
            if (chapListSize > 0) downloadEbook();
        }).fail(function (jqXHR, textStatus) {
            downloadError(textStatus);
        });
    });

})(jQuery, window, document);
<|endoftext|>


<commit_before>(function(){

    var root = this;

    var locache = {};
    root.locache = locache;

    locache.supportsLocalStorage = (function() {

        // Based on the Modernizr implementation;
        // https://github.com/Modernizr/Modernizr/blob/c56fb8b09515f629806ca44742932902ac145302/modernizr.js#L696-731

        try {
            var test_val = "___locache___";
            localStorage.setItem(test_val, test_val);
            localStorage.getItem(test_val);
            localStorage.removeItem(test_val);
            return true;
        } catch(e) {
            return false;
        }

    })();

    locache.supportsNativeJSON = !!window.JSON;

    locache.cachePrefix = '___locache___';
    locache.expirePrefix = '___locacheExpire___';

    locache.storage = {

        set : function(key, value){
            // Due to a strange bug (seen usually on the iPad) removing the
            // key before setting it can avoid QUOTA_EXCEEDED_ERR's
            localStorage.removeItem(key);
            return localStorage.setItem(key, value);
        },

        get : function(key, value){
            return localStorage.getItem(key);
        },

        remove : function(key){
            return localStorage.removeItem(key);
        },

        length : function(key){
            return localStorage.length;
        },

        key : function(index){
            return localStorage.key(index);
        }
    };

    var _currentTime = function(){
        return new Date().getTime();
    };

    locache.key = function(key){
        return this.cachePrefix + key;
    };

    locache.expirekey = function(key){
        return this.expirePrefix + key;
    };

    locache.hasExpired = function(key){

        var expireKey = this.expirekey(key);
        var expireValue = parseInt(this.storage.get(expireKey), 10);

        if (expireValue && expireValue < _currentTime()){
            return true;
        }

        return false;

    };

    locache.set = function(key, value, seconds){

        if (!this.supportsLocalStorage || !key) return;

        var expireKey = this.expirekey(key);
        var valueKey = this.key(key);

        if(seconds){
            ms = seconds * 1000;
            this.storage.set(expireKey, _currentTime() + ms);
        }

        value = JSON.stringify(value);
        this.storage.set(valueKey, value);

    };

    locache.get = function(key){

        if (!this.supportsLocalStorage) return null;

        if (this.hasExpired(key)){
            this.remove(this.key(key));
            return null;
        }

        var valueKey = this.key(key);
        var value = this.storage.get(valueKey);

        if (value){
            try{
                return JSON.parse(value);
            } catch(err){
                return null;
            }
        }

        return value;

    };

    locache.remove = function(key){

        if (!this.supportsLocalStorage) return;

        var expireKey = this.expirekey(key);
        var valueKey = this.key(key);

        this.storage.remove(expireKey);
        this.storage.remove(valueKey);

    };

    locache.incr = function(key){
        if (!this.supportsLocalStorage) return;

        var current = parseInt(this.get(key), 10);
        if (!current){
            current = 0;
        }
        var new_number = current + 1;
        this.set(key, new_number);
        return new_number;

    };

    locache.decr = function(key){
        if (!this.supportsLocalStorage) return;

        var current = parseInt(this.get(key), 10);
        if (!current){
            current = 0;
        }
        var new_number = current - 1;
        this.set(key, new_number);
        return new_number;

    };

    locache.setMany = function(properties){
        if (!this.supportsLocalStorage) return;

        for (var key in properties) {
            if (properties.hasOwnProperty(key)) {
                locache.set(key, properties[key]);
            }
        }

    };

    locache.getMany = function(keys){

        var results = [];

        for (var i=0; i < keys.length; i++){
            if (this.supportsLocalStorage){
                results.push(this.get(keys[i]));
            } else {
                results.push(null);
            }
        }

        return results;

    };

    locache.removeMany = function(keys){
        if (!this.supportsLocalStorage) return;

        for (var i=0; i < keys.length; i++){
            this.remove(keys[i]);
        }

    };

    locache.flush = function(){

        if (!this.supportsLocalStorage) return;

        var length = this.storage.length();
        var prefix = this.cachePrefix;

        for (var i=0; i < length; i++) {
            var key = this.storage.key(i);
            if (key.indexOf(prefix) === 0) this.storage.remove(key);
        }

    };

    locache.length = function(){

        if (!this.supportsLocalStorage) return 0;

        var c = 0;
        var length = this.storage.length();
        var prefix = this.cachePrefix;

        for (var i=0; i < length; i++) {
            if (this.storage.key(i).indexOf(prefix) === 0) c++;
        }

        return c;

    };

    locache.cleanup = function(){

        if (!this.supportsLocalStorage) return;

        var length = this.storage.length();
        var prefix = this.cachePrefix;

        for (var i=0; i < length; i++) {
            var key = this.storage.key(i);
            if (key.indexOf(prefix) === 0) this.storage.remove(key);
        }

    };

}).call(this);<commit_msg>Make sure the key index is valid.<commit_after>(function(){

    var root = this;

    var locache = {};
    root.locache = locache;

    locache.supportsLocalStorage = (function() {

        // Based on the Modernizr implementation;
        // https://github.com/Modernizr/Modernizr/blob/c56fb8b09515f629806ca44742932902ac145302/modernizr.js#L696-731

        try {
            var test_val = "___locache___";
            localStorage.setItem(test_val, test_val);
            localStorage.getItem(test_val);
            localStorage.removeItem(test_val);
            return true;
        } catch(e) {
            return false;
        }

    })();

    locache.supportsNativeJSON = !!window.JSON;

    locache.cachePrefix = '___locache___';
    locache.expirePrefix = '___locacheExpire___';

    locache.storage = {

        set : function(key, value){
            // Due to a strange bug (seen usually on the iPad) removing the
            // key before setting it can avoid QUOTA_EXCEEDED_ERR's
            localStorage.removeItem(key);
            return localStorage.setItem(key, value);
        },

        get : function(key, value){
            return localStorage.getItem(key);
        },

        remove : function(key){
            return localStorage.removeItem(key);
        },

        length : function(key){
            return localStorage.length;
        },

        key : function(index){
            if (index < 0 || index >= this.length()){
                return;
            }
            return localStorage.key(index);
        }
    };

    var _currentTime = function(){
        return new Date().getTime();
    };

    locache.key = function(key){
        return this.cachePrefix + key;
    };

    locache.expirekey = function(key){
        return this.expirePrefix + key;
    };

    locache.hasExpired = function(key){

        var expireKey = this.expirekey(key);
        var expireValue = parseInt(this.storage.get(expireKey), 10);

        if (expireValue && expireValue < _currentTime()){
            return true;
        }

        return false;

    };

    locache.set = function(key, value, seconds){

        if (!this.supportsLocalStorage || !key) return;

        var expireKey = this.expirekey(key);
        var valueKey = this.key(key);

        if(seconds){
            ms = seconds * 1000;
            this.storage.set(expireKey, _currentTime() + ms);
        }

        value = JSON.stringify(value);
        this.storage.set(valueKey, value);

    };

    locache.get = function(key){

        if (!this.supportsLocalStorage) return null;

        if (this.hasExpired(key)){
            this.remove(this.key(key));
            return null;
        }

        var valueKey = this.key(key);
        var value = this.storage.get(valueKey);

        if (value){
            try{
                return JSON.parse(value);
            } catch(err){
                return null;
            }
        }

        return value;

    };

    locache.remove = function(key){

        if (!this.supportsLocalStorage) return;

        var expireKey = this.expirekey(key);
        var valueKey = this.key(key);

        this.storage.remove(expireKey);
        this.storage.remove(valueKey);

    };

    locache.incr = function(key){
        if (!this.supportsLocalStorage) return;

        var current = parseInt(this.get(key), 10);
        if (!current){
            current = 0;
        }
        var new_number = current + 1;
        this.set(key, new_number);
        return new_number;

    };

    locache.decr = function(key){
        if (!this.supportsLocalStorage) return;

        var current = parseInt(this.get(key), 10);
        if (!current){
            current = 0;
        }
        var new_number = current - 1;
        this.set(key, new_number);
        return new_number;

    };

    locache.setMany = function(properties){
        if (!this.supportsLocalStorage) return;

        for (var key in properties) {
            if (properties.hasOwnProperty(key)) {
                locache.set(key, properties[key]);
            }
        }

    };

    locache.getMany = function(keys){

        var results = [];

        for (var i=0; i < keys.length; i++){
            if (this.supportsLocalStorage){
                results.push(this.get(keys[i]));
            } else {
                results.push(null);
            }
        }

        return results;

    };

    locache.removeMany = function(keys){
        if (!this.supportsLocalStorage) return;

        for (var i=0; i < keys.length; i++){
            this.remove(keys[i]);
        }

    };

    locache.flush = function(){

        if (!this.supportsLocalStorage) return;

        var length = this.storage.length();
        var prefix = this.cachePrefix;

        for (var i=0; i < length; i++) {
            var key = this.storage.key(i);
            if (key && key.indexOf(prefix) === 0) this.storage.remove(key);
        }

    };

    locache.length = function(){

        if (!this.supportsLocalStorage) return 0;

        var c = 0;
        var length = this.storage.length();
        var prefix = this.cachePrefix;

        for (var i=0; i < length; i++) {
            if (this.storage.key(i).indexOf(prefix) === 0) c++;
        }

        return c;

    };

    locache.cleanup = function(){

        if (!this.supportsLocalStorage) return;

        var length = this.storage.length();
        var prefix = this.cachePrefix;

        for (var i=0; i < length; i++) {
            var key = this.storage.key(i);
            if (key.indexOf(prefix) === 0) this.storage.remove(key);
        }

    };

}).call(this);<|endoftext|>