<commit_before>// @author Sufflavus https://github.com/Sufflavus
// @version 1.1.0

var jL = (function() {
    function JL(source){
        this.__source = source.slice();
    }

    JL.prototype.all = function(predicate) {
        // TODO: check if predicate is a function

        return this.__source.every(predicate);
    };

    JL.prototype.any = function(predicate) {
        // TODO: check if predicate is a function
        if(!predicate) {
            return this.__source.length > 0;
        }
        return this.__source.some(predicate);
    };

    JL.prototype.average = function(predicate) {
        // TODO: check if predicate is a function
        if(!this.__source.length) {
            return 0;
        }

        // TODO: check if all items in array are numbers

        var averageNum = this.__source.reduce(function(sum, number) {
            return sum + (predicate ? predicate(number) : number);
        }, 0) / this.__source.length;

        return averageNum;
    };

    JL.prototype.concat = function(secondArray) {
        // TODO: check if secondArray is an array
        if(secondArray) {
            this.__source = this.__source.concat(secondArray);
        }
        return this;
    };

    JL.prototype.contains = function(item, comparer) {
        if(!comparer) {
            return this.__source.indexOf(item) >= 0;
        }

        // TODO: check if comparer is a function
        for(var i = 0, length = this.__source.length; i < l; i++) {
            if(comparer(this.__source[i]) === comparer(item)) {
                return true;
            }
        }

        return false;
    };

    JL.prototype.count = function(predicate) {
        // TODO: check if predicate is a function
        if(!predicate) {
            return this.__source.length;
        }
        return this.__source.filter(predicate).length;
    };

    JL.prototype.defaultIfEmpty = function(defaultValue) {
        if(!this.__source.length) {
            this.__source = [defaultValue];
        }

        return this;
    };

    JL.prototype.distinct = function(comparer) {
        if(!comparer) {
            this.__source = this.__source.filter(function(item, index, array) {
                return array.indexOf(item) === index;
            });
        } else {
            // TODO: check if comparer is a function
            this.__source = this.__source.map(comparer).filter(function(item, index, array) {
                return array.indexOf(item) === index;
            });
        }

        return this;
    };

    JL.prototype.elementAt = function(index) {
        // TODO: check if index is a number
        return this.__source[index];
    };

    JL.prototype.except = function(second, comparer) {
        // TODO: check if second is a array
        if(!second.length) {
            if(!comparer) {
                this.__source = this.__source.filter(function(n, index, array) {
                    return array.indexOf(n) === index;
                });
                return this;
            }
            // TODO
            return this;
        }

        // TODO: check if comparer is a function
        if(!comparer) {
            this.__source = this.__source.filter(function(n) {
                return second.indexOf(n) === -1;
            }).filter(function(n, index, array) {
                return array.indexOf(n) === index;
            });
            return this;
        }

        // TODO
        return this;
    };

    JL.prototype.first = function(predicate) {
        var length = this.__source.length;

        if(!length) {
            throw("Sourse should contain at least 1 element");
        }

        if(!predicate) {
            return this.__source[0];
        }

        // TODO: check if predicate is a function
        for(var i = 0; i < length; i++) {
            if(predicate(this.__source[i])) {
                return this.__source[i];
            }
        }
    };

    JL.prototype.groupBy = function() {

    };

    JL.prototype.groupJoin = function() {

    };

    JL.prototype.intersect = function() {

    };

    JL.prototype.last = function(predicate) {
        var length = this.__source.length;

        if(!this.__source.length) {
            throw("Sourse should contain at least 1 element");
        }

        if(!predicate) {
            return this.__source[length - 1];
        }

        // TODO: check if predicate is a function
        for(var i = length - 1; i >= 0; i--) {
            if(predicate(this.__source[i])) {
                return this.__source[i];
            }
        }
    };

    JL.prototype.max = function(selector) {
        if(!selector) {
            return Math.max.apply(null, this.__source);
        }

        // TODO: check if selector is a function
        return Math.max.apply(null, this.__source.map(selector));
    };

    JL.prototype.min = function(selector) {
        if(!selector) {
            return Math.min.apply(null, this.__source);
        }

        // TODO: check if selector is a function
        return Math.min.apply(null, this.__source.map(selector));
    };

    JL.prototype.orderBy = function(keySelector, comparer) {
        // TODO: check if keySelector is a function
        // TODO: check if comparer is a function
        this.__source = this.__source.map(keySelector).sort(comparer);
        return this;
    };

    JL.prototype.select = function(predicate) {
        // TODO: check if predicate is a function
        this.__source = this.__source.map(predicate);
        return this;
    };

    JL.prototype.selectMany = function(collectionSelector, resultSelector) {
        // TODO: check if collectionSelector and resultSelector are functions
        this.__source = this.__source.map(function (sourceItem, index) {
            return {
                key: sourceItem,
                values: collectionSelector(sourceItem, index)
            };
        }).reduce(function(result, item) {
            if(resultSelector){
                var resultCollection = item.values.map(function(value){
                    return resultSelector(item.key, value);
                });
                return result.concat(resultCollection);
            }
            return result.concat(item.values);
        },[]);

        return this;
    };

    JL.prototype.where = function(predicate) {
        // TODO: check if predicate is a function
        this.__source = this.__source.filter(predicate);
        return this;
    };

    JL.prototype.take = function(count) {
        // TODO: check if count is a integer

        if(count <= 0) {
            this.__source = [];
            return this;
        }

        this.__source = this.__source.slice(0, count);
        return this;
    };

    JL.prototype.takeWhile = function(predicate) {
        // TODO: check if predicate is a function

        var stopIndex = this.__source.length;
        this.__source.some(function(n, index) {
            return predicate(n, index) ? false : ((stopIndex = index), true);
        });

        this.__source = this.__source.slice(0, stopIndex);
        return this;
    };

    JL.prototype.skip = function(count) {
        // TODO: check if count is a integer

        if(count >= this.__source.length) {
            this.__source = [];
            return this;
        }

        if(count <= 0) {
            return this;
        }

        this.__source = this.__source.slice(count);
        return this;
    };

    JL.prototype.skipWhile = function(predicate) {
        // TODO: check if predicate is a function

        var startIndex = this.__source.length;
        this.__source.some(function(n, index) {
            return predicate(n, index) ? false : ((startIndex = index), true);
        });

        this.__source = this.__source.slice(startIndex);
        return this;
    };

    JL.prototype.toArray = function() {
        return this.__source.slice();
    };

    return {
        fromArray:  function(array) {
            // TODO: check if it's an array
            return new JL(array);
        }
    };
})();
<commit_msg>+ orderByDescending<commit_after>// @author Sufflavus https://github.com/Sufflavus
// @version 1.1.0

var jL = (function() {
    function JL(source){
        this.__source = source.slice();
    }

    JL.prototype.all = function(predicate) {
        // TODO: check if predicate is a function

        return this.__source.every(predicate);
    };

    JL.prototype.any = function(predicate) {
        // TODO: check if predicate is a function
        if(!predicate) {
            return this.__source.length > 0;
        }
        return this.__source.some(predicate);
    };

    JL.prototype.average = function(predicate) {
        // TODO: check if predicate is a function
        if(!this.__source.length) {
            return 0;
        }

        // TODO: check if all items in array are numbers

        var averageNum = this.__source.reduce(function(sum, number) {
            return sum + (predicate ? predicate(number) : number);
        }, 0) / this.__source.length;

        return averageNum;
    };

    JL.prototype.concat = function(secondArray) {
        // TODO: check if secondArray is an array
        if(secondArray) {
            this.__source = this.__source.concat(secondArray);
        }
        return this;
    };

    JL.prototype.contains = function(item, comparer) {
        if(!comparer) {
            return this.__source.indexOf(item) >= 0;
        }

        // TODO: check if comparer is a function
        for(var i = 0, length = this.__source.length; i < l; i++) {
            if(comparer(this.__source[i]) === comparer(item)) {
                return true;
            }
        }

        return false;
    };

    JL.prototype.count = function(predicate) {
        // TODO: check if predicate is a function
        if(!predicate) {
            return this.__source.length;
        }
        return this.__source.filter(predicate).length;
    };

    JL.prototype.defaultIfEmpty = function(defaultValue) {
        if(!this.__source.length) {
            this.__source = [defaultValue];
        }

        return this;
    };

    JL.prototype.distinct = function(comparer) {
        if(!comparer) {
            this.__source = this.__source.filter(function(item, index, array) {
                return array.indexOf(item) === index;
            });
        } else {
            // TODO: check if comparer is a function
            this.__source = this.__source.map(comparer).filter(function(item, index, array) {
                return array.indexOf(item) === index;
            });
        }

        return this;
    };

    JL.prototype.elementAt = function(index) {
        // TODO: check if index is a number
        return this.__source[index];
    };

    JL.prototype.except = function(second, comparer) {
        // TODO: check if second is a array
        if(!second.length) {
            if(!comparer) {
                this.__source = this.__source.filter(function(n, index, array) {
                    return array.indexOf(n) === index;
                });
                return this;
            }
            // TODO
            return this;
        }

        // TODO: check if comparer is a function
        if(!comparer) {
            this.__source = this.__source.filter(function(n) {
                return second.indexOf(n) === -1;
            }).filter(function(n, index, array) {
                return array.indexOf(n) === index;
            });
            return this;
        }

        // TODO
        return this;
    };

    JL.prototype.first = function(predicate) {
        var length = this.__source.length;

        if(!length) {
            throw("Sourse should contain at least 1 element");
        }

        if(!predicate) {
            return this.__source[0];
        }

        // TODO: check if predicate is a function
        for(var i = 0; i < length; i++) {
            if(predicate(this.__source[i])) {
                return this.__source[i];
            }
        }
    };

    JL.prototype.groupBy = function() {

    };

    JL.prototype.groupJoin = function() {

    };

    JL.prototype.intersect = function() {

    };

    JL.prototype.last = function(predicate) {
        var length = this.__source.length;

        if(!this.__source.length) {
            throw("Sourse should contain at least 1 element");
        }

        if(!predicate) {
            return this.__source[length - 1];
        }

        // TODO: check if predicate is a function
        for(var i = length - 1; i >= 0; i--) {
            if(predicate(this.__source[i])) {
                return this.__source[i];
            }
        }
    };

    JL.prototype.max = function(selector) {
        if(!selector) {
            return Math.max.apply(null, this.__source);
        }

        // TODO: check if selector is a function
        return Math.max.apply(null, this.__source.map(selector));
    };

    JL.prototype.min = function(selector) {
        if(!selector) {
            return Math.min.apply(null, this.__source);
        }

        // TODO: check if selector is a function
        return Math.min.apply(null, this.__source.map(selector));
    };

    JL.prototype.orderBy = function(keySelector, comparer) {
        // TODO: check if keySelector is a function
        // TODO: check if comparer is a function
        this.__source = this.__source.map(keySelector).sort(comparer);
        return this;
    };

    JL.prototype.orderByDescending = function(keySelector, comparer) {
        // TODO: check if keySelector is a function
        // TODO: check if comparer is a function
        this.__source = this.__source.map(keySelector).sort(comparer).reverse();
        return this;
    };

    JL.prototype.select = function(predicate) {
        // TODO: check if predicate is a function
        this.__source = this.__source.map(predicate);
        return this;
    };

    JL.prototype.selectMany = function(collectionSelector, resultSelector) {
        // TODO: check if collectionSelector and resultSelector are functions
        this.__source = this.__source.map(function (sourceItem, index) {
            return {
                key: sourceItem,
                values: collectionSelector(sourceItem, index)
            };
        }).reduce(function(result, item) {
            if(resultSelector){
                var resultCollection = item.values.map(function(value){
                    return resultSelector(item.key, value);
                });
                return result.concat(resultCollection);
            }
            return result.concat(item.values);
        },[]);

        return this;
    };

    JL.prototype.where = function(predicate) {
        // TODO: check if predicate is a function
        this.__source = this.__source.filter(predicate);
        return this;
    };

    JL.prototype.take = function(count) {
        // TODO: check if count is a integer

        if(count <= 0) {
            this.__source = [];
            return this;
        }

        this.__source = this.__source.slice(0, count);
        return this;
    };

    JL.prototype.takeWhile = function(predicate) {
        // TODO: check if predicate is a function

        var stopIndex = this.__source.length;
        this.__source.some(function(n, index) {
            return predicate(n, index) ? false : ((stopIndex = index), true);
        });

        this.__source = this.__source.slice(0, stopIndex);
        return this;
    };

    JL.prototype.skip = function(count) {
        // TODO: check if count is a integer

        if(count >= this.__source.length) {
            this.__source = [];
            return this;
        }

        if(count <= 0) {
            return this;
        }

        this.__source = this.__source.slice(count);
        return this;
    };

    JL.prototype.skipWhile = function(predicate) {
        // TODO: check if predicate is a function

        var startIndex = this.__source.length;
        this.__source.some(function(n, index) {
            return predicate(n, index) ? false : ((startIndex = index), true);
        });

        this.__source = this.__source.slice(startIndex);
        return this;
    };

    JL.prototype.toArray = function() {
        return this.__source.slice();
    };

    return {
        fromArray:  function(array) {
            // TODO: check if it's an array
            return new JL(array);
        }
    };
})();
<|endoftext|>


<commit_before>var assert = require("assert"),
    moment           = require('moment'),
    events   = require ('events'),
    util     = require ('util'),
    exec   = require('child_process').exec,
    mvcp_server      = require('../drivers/mvcp/mvcp-driver'),
    test_driver    = require('../drivers/playlists/test-driver'),
    mosto  = require('../mosto'),
    melted  = require('../api/Melted'),
    Playlist  = require('../api/Playlist'),
    Media  = require('../api/Media'),
    mosto_fetcher  = require('../fetch'),
    mosto_scheduler  = require('../scheduler'),
    mosto_synchronizer  = require('../sync'),
    mosto_player  = require('../play'),
    _                = require('underscore');

describe('Mosto [LOGIC/Scheduler] section tests', function(done) {
    var mosto_server = undefined;
    var server = undefined;

    var fetcher = undefined;
    var scheduler = undefined;
    var synchronizer = undefined;
    var player = undefined;

    this.timeout(15000);

    before(function(done) {
        melted.take(function() {
		    melted.stop(function(){
    	        done();
    	    });
	    });
    });

    describe('#[LOGIC] start mosto', function() {
	    it('-- starting mosto shouldnt throw error', function() {
	        mosto_server = silence(function(){ return new mosto(); });
	        assert.notEqual(mosto_server, undefined);
            mosto_server.driver = new test_driver();

            mosto_server.fetcher        = new mosto_fetcher( { mosto: mosto_server } );
            mosto_server.scheduler        = new mosto_scheduler( { mosto: mosto_server } );
            //mosto_server.synchronizer        = new mosto_synchronizer( { mosto: mosto_server } );
            //mosto_server.player        = new mosto_player( { mosto: mosto_server } );

            fetcher = mosto_server.fetcher;
            scheduler = mosto_server.scheduler;

            mosto_server.fetcher.init();
            mosto_server.scheduler.init();
            //mosto_server.synchronizer.init();
            //mosto_server.player.init();

            //synchronizer = mosto_server.synchronizer;
            //player = mosto_server.player;

	    });
	    it('-- mvcp server connected should return false', function() {
	        var r = silence(function(){ return mosto_server.server_started; });
	        assert.equal(r, false);
	    });
    });

    describe("#[LOGIC] Checking start with no playlists", function() {
        it("--should return no clips", function(done) {
            assert.equal( scheduler.scheduled_clips.length, 0 );
            done();
        });
    });

    describe("#[LOGIC] Adding playlist", function() {

        var playlist = undefined;

        before(function(done){
            mosto_server.driver.start();
            playlist = mosto_server.driver.TestPlaylist( 0 );
            done();
        });
        it("--scheduler should receive data and convert to scheduled_clips", function(done) {
            scheduler.once('datareceived', function (playlists) {
                console.log("mosto-logic-test: [SCHED TEST] Adding: fetch_downstream and datareceived ok!");
                console.log(playlists);
                (_.isEqual( playlists[0], playlist)) ? done() : done( new Error("playlists[0] and playlist doesn't match."));
            });
            fetcher.addPlaylist( playlist );
        });
        it("--scheduler should convert to scheduled_clips for the next upstreamCheck", function(done) {
            scheduler.once('sched_downstream', function (sched_clips) {
                console.log("mosto-logic-test: [SCHED TEST] Adding: sched_downstream ok! sched_clips: " + sched_clips.length + " vs " + playlist.medias.length);
                for( var i=0; i<playlist.medias.length; i++ ) {
                    if (!_.isEqual( playlist.medias[i], sched_clips[i].media )) {
                        return done(new Error( playlist.medias[i] + "!=" + sched_clips[i].media ));
                    }
                }
                done();
            });
            scheduler.emit('upstreamcheck');
        });

    });

    describe("#[LOGIC/SCHED] Adding playlists in the near future", function() {

        var playlist1 = undefined;
        var playlist2 = undefined;
        var splaylists = [];
        var start_playlist = undefined;

        before(function(done){

            mosto_server.driver.start();

            playlist1 = mosto_server.driver.TestPlaylist( 20000 );
            playlist2 = mosto_server.driver.TestPlaylist( 20000+90000 );

            splaylists.push(playlist1);
            splaylists.push(playlist2);

            start_playlist = moment( playlist1.startDate ).format("DD/MM/YYYY HH:mm:ss");

            done();
        });
        it("--scheduler should receive data", function(done) {
            scheduler.once('datareceived', function (playlists) {
                console.log("mosto-logic-test: [SCHED TEST] datareceived ok!");
                //console.log( playlists );
                (_.isEqual( splaylists[0], playlist1) && _.isEqual( splaylists[1], playlist2)) ? done() : done( new Error("playlists[0] and playlist doesn't match."));
            });
            scheduler.emit('datasend', splaylists);
        });

        it("--scheduler should convert to scheduled_clips", function(done) {
            scheduler.once('sched_downstream', function (sched_clips) {
                console.log("mosto-logic-test: [SCHED TEST] Adding: sched_downstream ok! sched_clips: " + sched_clips.length);
                console.log(sched_clips);
                assert.equal( sched_clips.length, 8 );//1 black at the start, 6 clips + 1 black at the end
                for( var i=1; i<sched_clips.length-1; i++ ) {
                    var pl_index = Math.floor( (i-1) / 3 );
                    var cl_index = Math.floor( (i-1) % 3 );
                    console.log("pl_index:"+ pl_index + " cl_index:" + cl_index);
                    if (!_.isEqual( sched_clips[i].media, splaylists[ pl_index ].medias[cl_index] )) {
                        return done(new Error( sched_clips[i].media.id+" not Equal to "+ splaylists[pl_index].medias[cl_index] ));
                    }
                }
                done();
            });
            scheduler.emit('upstreamcheck');
        });

    });

    describe("#[LOGIC] Updating playlist", function() {

        var playlist = undefined;

        before(function(done){
            playlist = mosto_server.driver.TestPlaylist();
            playlist.medias.splice( 2, 1 );
            done();
        });
        it("--should return the first clip of playlist", function(done) {
            scheduler.once( 'datareceived', function( playlists ) {
                console.log("mosto-logic-test: [SCHED TEST] Updating: fetch_downstream and datareceived ok!" + " rec_count:" + scheduler.rec_count + " ret_count:" + scheduler.ret_count );
                //console.log(playlists[0].id);
                scheduler.once( 'sched_downstream', function( sched_clips ) {
                    //assert.notEqual( sched_clips.length, 0 );
                    //assert.equal( sched_clips[0].media.id, playlist.medias[0].id );
                    console.log("mosto-logic-test: [SCHED TEST] Updating: sched_downstream ok! sched_clips: " + sched_clips.length + " vs " + playlist.medias.length);
                    for( var i=0; i<playlist.medias.length; i++ ) {
                        if (!_.isEqual( playlist.medias[i], sched_clips[i].media )) {
                            return done(new Error( playlist.medias[i] + "!=" + sched_clips[i].media ));
                        }
                    }
                    done();
                });
                scheduler.emit('upstreamcheck');
            });
            fetcher.updatePlaylist( playlist );
        });
    });

    describe("#[LOGIC] Remove playlist test", function() {
        before(function(done){
            done();
        });
        it("--should return only the blank black_id", function(done) {
            scheduler.once( 'datareceived', function( playlists ) {
                console.log("mosto-logic-test: [SCHED TEST] Removing: fetch_downstream and datareceived ok!" + " rec_count:" + scheduler.rec_count + " ret_count:" + scheduler.ret_count );
                scheduler.once( 'sched_downstream', function( sched_clips ) {
                    if (sched_clips.length==1 && ( sched_clips[0].media.id == "black_id" ) ) {
                        return done();
                    } else return done(new Error("No black id"));
                });
                scheduler.emit('upstreamcheck');
            });
            fetcher.removePlaylist( "test_playlist_1_id" );
        });

    });

    describe("#[LOGIC] Doing a checkoutPlaylists()", function() {
        var driver_playlists = undefined;

        before(function(done){
            mosto_server.driver.setCheckoutMode(true);
            fetcher.checkoutPlaylists( function(playlists) {
                driver_playlists = playlists;
                if (playlists.length==0) return done(new Error("test_driver->getPlaylists must return some playlists when called in co_mode:true"));
                done();
            });
        });
        it("--should return the same playlist", function(done) {
            scheduler.once( 'datareceived', function( data ) {
                scheduler.once( 'sched_downstream', function( sched_clips ) {
                    var co_playlist = driver_playlists[0];
                    for( var i=0; i<co_playlist.medias.length; i++ ) {
                        if (!_.isEqual( co_playlist.medias[i], sched_clips[i].media )) {
                            return done(new Error( co_playlist.medias[i] + "!=" + sched_clips[i].media ));
                        }
                    }
                    done();
                });
                scheduler.emit( 'upstreamcheck');
            });
            fetcher.checkoutPlaylists( function(playlists) {} );
        });

    });

    describe('#[LOGIC] leave melted', function() {
	    it('-- leave melted', function(done) {
		    //mosto_server.stop();
		    mosto_server = null;
		    melted.stop(function(pid) {
			    melted.leave();
			    done();
		    });
	    });
    });

    describe('#last [LOGIC] check ', function() {
        it("--should finish LOGIC", function(done) {
            done();
        });
    });

});
<commit_msg>Naming LOGIC/CHED<commit_after>var assert = require("assert"),
    moment           = require('moment'),
    events   = require ('events'),
    util     = require ('util'),
    exec   = require('child_process').exec,
    mvcp_server      = require('../drivers/mvcp/mvcp-driver'),
    test_driver    = require('../drivers/playlists/test-driver'),
    mosto  = require('../mosto'),
    melted  = require('../api/Melted'),
    Playlist  = require('../api/Playlist'),
    Media  = require('../api/Media'),
    mosto_fetcher  = require('../fetch'),
    mosto_scheduler  = require('../scheduler'),
    mosto_synchronizer  = require('../sync'),
    mosto_player  = require('../play'),
    _                = require('underscore');

describe('Mosto [LOGIC/Scheduler] section tests', function(done) {
    var mosto_server = undefined;
    var server = undefined;

    var fetcher = undefined;
    var scheduler = undefined;
    var synchronizer = undefined;
    var player = undefined;

    this.timeout(15000);

    before(function(done) {
        melted.take(function() {
		    melted.stop(function(){
    	        done();
    	    });
	    });
    });

    describe('#[LOGIC/SCHED] start mosto', function() {
	    it('-- starting mosto shouldnt throw error', function() {
	        mosto_server = silence(function(){ return new mosto(); });
	        assert.notEqual(mosto_server, undefined);
            mosto_server.driver = new test_driver();

            mosto_server.fetcher        = new mosto_fetcher( { mosto: mosto_server } );
            mosto_server.scheduler        = new mosto_scheduler( { mosto: mosto_server } );
            //mosto_server.synchronizer        = new mosto_synchronizer( { mosto: mosto_server } );
            //mosto_server.player        = new mosto_player( { mosto: mosto_server } );

            fetcher = mosto_server.fetcher;
            scheduler = mosto_server.scheduler;

            mosto_server.fetcher.init();
            mosto_server.scheduler.init();
            //mosto_server.synchronizer.init();
            //mosto_server.player.init();

            //synchronizer = mosto_server.synchronizer;
            //player = mosto_server.player;

	    });
	    it('-- mvcp server connected should return false', function() {
	        var r = silence(function(){ return mosto_server.server_started; });
	        assert.equal(r, false);
	    });
    });

    describe("#[LOGIC/SCHED] Checking start with no playlists", function() {
        it("--should return no clips", function(done) {
            assert.equal( scheduler.scheduled_clips.length, 0 );
            done();
        });
    });

    describe("#[LOGIC/SCHED] Adding playlists", function() {

        var playlist = undefined;

        before(function(done){
            mosto_server.driver.start();
            playlist = mosto_server.driver.TestPlaylist( 0 );
            done();
        });
        it("--scheduler should receive data and convert to scheduled_clips", function(done) {
            scheduler.once('datareceived', function (playlists) {
                console.log("mosto-logic-test: [SCHED TEST] Adding: fetch_downstream and datareceived ok!");
                console.log(playlists);
                (_.isEqual( playlists[0], playlist)) ? done() : done( new Error("playlists[0] and playlist doesn't match."));
            });
            fetcher.addPlaylist( playlist );
        });
        it("--scheduler should convert to scheduled_clips for the next upstreamCheck", function(done) {
            scheduler.once('sched_downstream', function (sched_clips) {
                console.log("mosto-logic-test: [SCHED TEST] Adding: sched_downstream ok! sched_clips: " + sched_clips.length + " vs " + playlist.medias.length);
                for( var i=0; i<playlist.medias.length; i++ ) {
                    if (!_.isEqual( playlist.medias[i], sched_clips[i].media )) {
                        return done(new Error( playlist.medias[i] + "!=" + sched_clips[i].media ));
                    }
                }
                done();
            });
            scheduler.emit('upstreamcheck');
        });

    });

    describe("#[LOGIC/SCHED] Adding playlists in the near future", function() {

        var playlist1 = undefined;
        var playlist2 = undefined;
        var splaylists = [];
        var start_playlist = undefined;

        before(function(done){

            mosto_server.driver.start();

            playlist1 = mosto_server.driver.TestPlaylist( 20000 );
            playlist2 = mosto_server.driver.TestPlaylist( 20000+90000 );

            splaylists.push(playlist1);
            splaylists.push(playlist2);

            start_playlist = moment( playlist1.startDate ).format("DD/MM/YYYY HH:mm:ss");

            done();
        });
        it("--scheduler should receive data", function(done) {
            scheduler.once('datareceived', function (playlists) {
                console.log("mosto-logic-test: [SCHED TEST] datareceived ok!");
                //console.log( playlists );
                (_.isEqual( splaylists[0], playlist1) && _.isEqual( splaylists[1], playlist2)) ? done() : done( new Error("playlists[0] and playlist doesn't match."));
            });
            scheduler.emit('datasend', splaylists);
        });

        it("--scheduler should convert to scheduled_clips", function(done) {
            scheduler.once('sched_downstream', function (sched_clips) {
                console.log("mosto-logic-test: [SCHED TEST] Adding: sched_downstream ok! sched_clips: " + sched_clips.length);
                console.log(sched_clips);
                assert.equal( sched_clips.length, 8 );//1 black at the start, 6 clips + 1 black at the end
                for( var i=1; i<sched_clips.length-1; i++ ) {
                    var pl_index = Math.floor( (i-1) / 3 );
                    var cl_index = Math.floor( (i-1) % 3 );
                    console.log("pl_index:"+ pl_index + " cl_index:" + cl_index);
                    if (!_.isEqual( sched_clips[i].media, splaylists[ pl_index ].medias[cl_index] )) {
                        return done(new Error( sched_clips[i].media.id+" not Equal to "+ splaylists[pl_index].medias[cl_index] ));
                    }
                }
                done();
            });
            scheduler.emit('upstreamcheck');
        });

    });

    describe("#[LOGIC] Updating playlist", function() {

        var playlist = undefined;

        before(function(done){
            playlist = mosto_server.driver.TestPlaylist();
            playlist.medias.splice( 2, 1 );
            done();
        });
        it("--should return the first clip of playlist", function(done) {
            scheduler.once( 'datareceived', function( playlists ) {
                console.log("mosto-logic-test: [SCHED TEST] Updating: fetch_downstream and datareceived ok!" + " rec_count:" + scheduler.rec_count + " ret_count:" + scheduler.ret_count );
                //console.log(playlists[0].id);
                scheduler.once( 'sched_downstream', function( sched_clips ) {
                    //assert.notEqual( sched_clips.length, 0 );
                    //assert.equal( sched_clips[0].media.id, playlist.medias[0].id );
                    console.log("mosto-logic-test: [SCHED TEST] Updating: sched_downstream ok! sched_clips: " + sched_clips.length + " vs " + playlist.medias.length);
                    for( var i=0; i<playlist.medias.length; i++ ) {
                        if (!_.isEqual( playlist.medias[i], sched_clips[i].media )) {
                            return done(new Error( playlist.medias[i] + "!=" + sched_clips[i].media ));
                        }
                    }
                    done();
                });
                scheduler.emit('upstreamcheck');
            });
            fetcher.updatePlaylist( playlist );
        });
    });

    describe("#[LOGIC] Remove playlist test", function() {
        before(function(done){
            done();
        });
        it("--should return only the blank black_id", function(done) {
            scheduler.once( 'datareceived', function( playlists ) {
                console.log("mosto-logic-test: [SCHED TEST] Removing: fetch_downstream and datareceived ok!" + " rec_count:" + scheduler.rec_count + " ret_count:" + scheduler.ret_count );
                scheduler.once( 'sched_downstream', function( sched_clips ) {
                    if (sched_clips.length==1 && ( sched_clips[0].media.id == "black_id" ) ) {
                        return done();
                    } else return done(new Error("No black id"));
                });
                scheduler.emit('upstreamcheck');
            });
            fetcher.removePlaylist( "test_playlist_1_id" );
        });

    });

    describe("#[LOGIC] Doing a checkoutPlaylists()", function() {
        var driver_playlists = undefined;

        before(function(done){
            mosto_server.driver.setCheckoutMode(true);
            fetcher.checkoutPlaylists( function(playlists) {
                driver_playlists = playlists;
                if (playlists.length==0) return done(new Error("test_driver->getPlaylists must return some playlists when called in co_mode:true"));
                done();
            });
        });
        it("--should return the same playlist", function(done) {
            scheduler.once( 'datareceived', function( data ) {
                scheduler.once( 'sched_downstream', function( sched_clips ) {
                    var co_playlist = driver_playlists[0];
                    for( var i=0; i<co_playlist.medias.length; i++ ) {
                        if (!_.isEqual( co_playlist.medias[i], sched_clips[i].media )) {
                            return done(new Error( co_playlist.medias[i] + "!=" + sched_clips[i].media ));
                        }
                    }
                    done();
                });
                scheduler.emit( 'upstreamcheck');
            });
            fetcher.checkoutPlaylists( function(playlists) {} );
        });

    });

    describe('#[LOGIC/SCHED] leave melted', function() {
	    it('-- leave melted', function(done) {
		    //mosto_server.stop();
		    mosto_server = null;
		    melted.stop(function(pid) {
			    melted.leave();
			    done();
		    });
	    });
    });

    describe('#last [LOGIC/SCHED] check ', function() {
        it("--should finish LOGIC", function(done) {
            done();
        });
    });

});
<|endoftext|>


<commit_before>/*
* grunt-ssh-deploy
* https://github.com/dcarlson/grunt-ssh-deploy
*
* Copyright (c) 2014 Dustin Carlson
* Licensed under the MIT license.
*/

'use strict';

module.exports = function(grunt) {

	grunt.registerTask('ssh_rollback', 'Begin Rollback', function() {
		this.async();
        var Connection = require('ssh2');
        var async = require('async');

        var options = grunt.config.get('environments')[this.args]['options'];

		var c = new Connection();
		c.on('connect', function() {
			grunt.log.subhead('Connecting :: ' + options.host);
		});
		c.on('ready', function() {
			grunt.log.subhead('Connected :: ' + options.host);
			// execution of tasks
			execCommands(options,c);
		});
		c.on('error', function(err) {
			grunt.log.subhead("Error :: " + options.host);
			grunt.log.errorlns(err);
			if (err) {throw err;}
		});
		c.on('close', function(had_error) {
			grunt.log.subhead("Closed :: " + options.host);

			return true;
		});
		c.connect(options);

		var execCommands = function(options, connection){

            // executes a remote command via ssh
            var execRemote = function(cmd, showLog, next){
                connection.exec(cmd, function(err, stream) {
                    if (err) {
                        grunt.log.errorlns(err);
                        grunt.log.subhead('ERROR ROLLING BACK. CLOSING CONNECTION.');
                    }
                    stream.on('data', function(data, extended) {
                        grunt.log.debug((extended === 'stderr' ? 'STDERR: ' : 'STDOUT: ') + data);
                    });
                    stream.on('end', function() {
                        grunt.log.debug('REMOTE: ' + cmd);
                        if(!err) {
                            next();
                        }
                    });
                });
            };

            var updateSymlink = function(callback) {
                var delete_symlink = 'rm -rf ' + options.deploy_path + '/' + options.current_symlink;
                var set_symlink = 'cd ' + options.deploy_path + ' && t=`ls -t1 releases/ | sed -n 2p` && ln -s releases/$t ' + options.current_symlink;
                var command = delete_symlink + ' && ' + set_symlink;
                grunt.log.subhead('--------------- UPDATING SYM LINK');
                grunt.log.subhead('--- ' + command);
                execRemote(command, options.debug, callback);
            };

            var deleteRelease = function(callback) {
                var command = 't=`ls -t1 ' + options.deploy_path + '/releases/ | sed -n 1p` && rm -rf ' + options.deploy_path + '/releases/$t/';
                grunt.log.subhead('--------------- DELETING RELEASE');
                grunt.log.subhead('--- ' + command);
                execRemote(command, options.debug, callback);
            };

            // closing connection to remote server
            var closeConnection = function(callback) {
                connection.end();

                return true;
            };

            async.series([
                updateSymlink,
                deleteRelease,
                closeConnection
            ]);
        };
    });
};<commit_msg>Fixed rollback hangs too<commit_after>/*
* grunt-ssh-deploy
* https://github.com/dcarlson/grunt-ssh-deploy
*
* Copyright (c) 2014 Dustin Carlson
* Licensed under the MIT license.
*/

'use strict';

module.exports = function(grunt) {

	grunt.registerTask('ssh_rollback', 'Begin Rollback', function() {
		var done = this.async();
        var Connection = require('ssh2');
        var async = require('async');

        var options = grunt.config.get('environments')[this.args]['options'];

		var c = new Connection();
		c.on('connect', function() {
			grunt.log.subhead('Connecting :: ' + options.host);
		});
		c.on('ready', function() {
			grunt.log.subhead('Connected :: ' + options.host);
			// execution of tasks
			execCommands(options,c);
		});
		c.on('error', function(err) {
			grunt.log.subhead("Error :: " + options.host);
			grunt.log.errorlns(err);
			if (err) {throw err;}
		});
		c.on('close', function(had_error) {
			grunt.log.subhead("Closed :: " + options.host);

			return true;
		});
		c.connect(options);

		var execCommands = function(options, connection){

            // executes a remote command via ssh
            var execRemote = function(cmd, showLog, next){
                connection.exec(cmd, function(err, stream) {
                    if (err) {
                        grunt.log.errorlns(err);
                        grunt.log.subhead('ERROR ROLLING BACK. CLOSING CONNECTION.');
                    }
                    stream.on('data', function(data, extended) {
                        grunt.log.debug((extended === 'stderr' ? 'STDERR: ' : 'STDOUT: ') + data);
                    });
                    stream.on('end', function() {
                        grunt.log.debug('REMOTE: ' + cmd);
                        if(!err) {
                            next();
                        }
                    });
                });
            };

            var updateSymlink = function(callback) {
                var delete_symlink = 'rm -rf ' + options.deploy_path + '/' + options.current_symlink;
                var set_symlink = 'cd ' + options.deploy_path + ' && t=`ls -t1 releases/ | sed -n 2p` && ln -s releases/$t ' + options.current_symlink;
                var command = delete_symlink + ' && ' + set_symlink;
                grunt.log.subhead('--------------- UPDATING SYM LINK');
                grunt.log.subhead('--- ' + command);
                execRemote(command, options.debug, callback);
            };

            var deleteRelease = function(callback) {
                var command = 't=`ls -t1 ' + options.deploy_path + '/releases/ | sed -n 1p` && rm -rf ' + options.deploy_path + '/releases/$t/';
                grunt.log.subhead('--------------- DELETING RELEASE');
                grunt.log.subhead('--- ' + command);
                execRemote(command, options.debug, callback);
            };

            // closing connection to remote server
            var closeConnection = function(callback) {
                connection.end();

                callback();
            };

            async.series([
                updateSymlink,
                deleteRelease,
                closeConnection
            ], done);
        };
    });
};<|endoftext|>


<commit_before>/* COMPONENT */

// See the properties declaration for details of constructor arguments.
//
Monocle.Component = function (book, id, index, chapters, source) {

  var API = { constructor: Monocle.Component }
  var k = API.constants = API.constructor;
  var p = API.properties = {
    // a back-reference to the public API of the book that owns this component
    book: book,

    // the string that represents this component in the book's component array
    id: id,

    // the position in the book's components array of this component
    index: index,

    // The chapters argument is an array of objects that list the chapters that
    // can be found in this component. A chapter object is defined as:
    //
    //  {
    //     title: str,
    //     fragment: str, // optional anchor id
    //     percent: n     // how far into the component the chapter begins
    //  }
    //
    // NOTE: the percent property is calculated by the component - you only need
    // to pass in the title and the optional id string.
    //
    chapters: chapters,

    // the frame provided by dataSource.getComponent() for this component
    source: source
  }

  // Makes this component the active component for the pageDiv. There are
  // several strategies for this (see loadFrame).
  //
  // When the component has been loaded into the pageDiv's frame, the callback
  // will be invoked with the pageDiv and this component as arguments.
  //
  function applyTo(pageDiv, callback) {
    prepareSource(pageDiv.m.reader);

    var evtData = { 'page': pageDiv, 'source': p.source };
    pageDiv.m.reader.dispatchEvent('monocle:componentchanging', evtData);

    var onLoaded = function () {
      setupFrame(
        pageDiv,
        pageDiv.m.activeFrame,
        function () { callback(pageDiv, API) }
      );
    }

    Monocle.defer(function () { loadFrame(pageDiv, onLoaded); });
  }

  // Loads this component into the given frame, using one of the following
  // strategies:
  //
  // * HTML - a HTML string
  // * URL - a URL string
  // * Nodes - an array of DOM body nodes (NB: no way to populate head)
  // * Document - a DOM DocumentElement object
  //
  function loadFrame(pageDiv, callback) {
    var frame = pageDiv.m.activeFrame;

    // We own this frame now.
    frame.m.component = API;

    // Hide the frame while we're changing it.
    frame.style.visibility = "hidden";

    frame.whenDocumentReady = function () {
      var doc = frame.contentDocument;
      var evtData = { 'page': pageDiv, 'document': doc, 'component': API };
      pageDiv.m.reader.dispatchEvent('monocle:componentmodify', evtData);
      frame.whenDocumentReady = null;
    }

    if (p.source.html) {
      return loadFrameFromHTML(p.source.html || p.source, frame, callback);
    } else if (p.source.url) {
      return loadFrameFromURL(p.source.url, frame, callback);
    } else if (p.source.doc) {
      return loadFrameFromDocument(p.source.doc, frame, callback);
    }
  }

  // LOAD STRATEGY: HTML
  // Loads a HTML string into the given frame, invokes the callback once loaded.
  //
  function loadFrameFromHTML(src, frame, callback) {
    var fn = function () {
      Monocle.Events.deafen(frame, 'load', fn);
      frame.whenDocumentReady();
      Monocle.defer(callback);
    }
    Monocle.Events.listen(frame, 'load', fn);
    frame.contentWindow['monCmptData'] = src;
    frame.src = "javascript:window['monCmptData'];"
  }

  // LOAD STRATEGY: URL
  // Loads the URL into the given frame, invokes callback once loaded.
  //
  function loadFrameFromURL(url, frame, callback) {
    // If it's a relative path, we need to make it absolute.
    if (!url.match(/^\//)) {
      url = absoluteURL(url);
    }
    var onDocumentReady = function () {
      Monocle.Events.deafen(frame, 'load', onDocumentReady);
      frame.whenDocumentReady();
    }
    var onDocumentLoad = function () {
      Monocle.Events.deafen(frame, 'load', onDocumentLoad);
      Monocle.defer(callback);
    }
    Monocle.Events.listen(frame, 'load', onDocumentReady);
    Monocle.Events.listen(frame, 'load', onDocumentLoad);
    frame.contentWindow.location.replace(url);
  }

  // LOAD STRATEGY: DOCUMENT
  // Replaces the DocumentElement of the given frame with the given srcDoc.
  // Invokes the callback when loaded.
  //
  function loadFrameFromDocument(srcDoc, frame, callback) {
    var doc = frame.contentDocument;

    // WebKit has an interesting quirk. The <base> tag must exist in the
    // document being replaced, not the new document.
    if (Monocle.Browser.is.WebKit) {
      var srcBase = srcDoc.querySelector('base');
      if (srcBase) {
        var head = doc.querySelector('head');
        if (!head) {
          try {
            head = doc.createElement('head');
            prependChild(doc.documentElement, head);
          } catch (e) {
            head = doc.body;
          }
        }
        var base = doc.createElement('base');
        base.setAttribute('href', srcBase.href);
        head.appendChild(base);
      }
    }

    doc.replaceChild(
      doc.importNode(srcDoc.documentElement, true),
      doc.documentElement
    );

    // NB: It's a significant problem with this load strategy that there's
    // no indication when it is complete.
    Monocle.defer(callback);
  }

  // Once a frame is loaded with this component, call this method to style
  // and measure its contents.
  //
  function setupFrame(pageDiv, frame, callback) {
    updateDimensions(pageDiv, function () {
      frame.style.visibility = "visible";

      // Find the place of any chapters in the component.
      locateChapters(pageDiv);

      // Announce that the component has changed.
      var doc = frame.contentDocument;
      var evtData = { 'page': pageDiv, 'document': doc, 'component': API };
      pageDiv.m.reader.dispatchEvent('monocle:componentchange', evtData);

      callback();
    });
  }

  // Checks whether the pageDiv dimensions have changed. If they have,
  // remeasures dimensions and returns true. Otherwise returns false.
  //
  function updateDimensions(pageDiv, callback) {
    pageDiv.m.dimensions.update(function (pageLength) {
      p.pageLength = pageLength;
      if (typeof callback == "function") { callback() };
    });
  }

  // Iterates over all the chapters that are within this component
  // (according to the array we were provided on initialization) and finds
  // their location (in percentage terms) within the text.
  //
  // Stores this percentage with the chapter object in the chapters array.
  //
  function locateChapters(pageDiv) {
    if (p.chapters[0] && typeof p.chapters[0].percent == "number") {
      return;
    }
    var doc = pageDiv.m.activeFrame.contentDocument;
    for (var i = 0; i < p.chapters.length; ++i) {
      var chp = p.chapters[i];
      chp.percent = 0;
      if (chp.fragment) {
        var node = doc.getElementById(chp.fragment);
        chp.percent = pageDiv.m.dimensions.percentageThroughOfNode(node);
      }
    }
    return p.chapters;
  }

  // For a given page number within the component, return the chapter that
  // starts on or most-recently-before this page.
  //
  // Useful, for example, in displaying the current chapter title as a
  // running head on the page.
  //
  function chapterForPage(pageN) {
    var cand = null;
    var percent = (pageN - 1) / p.pageLength;
    for (var i = 0; i < p.chapters.length; ++i) {
      if (percent >= p.chapters[i].percent) {
        cand = p.chapters[i];
      } else {
        return cand;
      }
    }
    return cand;
  }

  // For a given chapter fragment (the bit after the hash
  // in eg, "index.html#foo"), return the page number on which
  // the chapter starts. If the fragment is null or blank, will
  // return the first page of the component.
  //
  function pageForChapter(fragment, pageDiv) {
    if (!fragment) {
      return 1;
    }
    for (var i = 0; i < p.chapters.length; ++i) {
      if (p.chapters[i].fragment == fragment) {
        return percentToPageNumber(p.chapters[i].percent);
      }
    }
    var doc = pageDiv.m.activeFrame.contentDocument;
    var node = doc.getElementById(fragment);
    var percent = pageDiv.m.dimensions.percentageThroughOfNode(node);
    return percentToPageNumber(percent);
  }

  function pageForXPath(xpath, pageDiv) {
    var doc = pageDiv.m.activeFrame.contentDocument;
    var percent = 0;
    if (Monocle.Browser.env.supportsXPath) {
      var node = doc.evaluate(xpath, doc, null, 9, null).singleNodeValue;
      if (node) {
        percent = pageDiv.m.dimensions.percentageThroughOfNode(node);
      }
    } else {
      console.warn("XPath not supported in this client.");
    }
    return percentToPageNumber(percent);
  }

  function pageForSelector(selector, pageDiv) {
    var doc = pageDiv.m.activeFrame.contentDocument;
    var percent = 0;
    if (Monocle.Browser.env.supportsQuerySelector) {
      var node = doc.querySelector(selector);
      if (node) {
        percent = pageDiv.m.dimensions.percentageThroughOfNode(node);
      }
    } else {
      console.warn("querySelector not supported in this client.");
    }
    return percentToPageNumber(percent);
  }

  function percentToPageNumber(pc) {
    return Math.floor(pc * p.pageLength) + 1;
  }

  // A public getter for p.pageLength.
  //
  function lastPageNumber() {
    return p.pageLength;
  }

  function prepareSource(reader) {
    if (p.sourcePrepared) { return; }
    p.sourcePrepared = true;

    if (typeof p.source == "string") {
      p.source = { html: p.source };
    }

    // If supplied as escaped javascript, unescape it to HTML by evalling it.
    if (p.source.javascript) {
      console.deprecation(
        "Loading a component by 'javascript' is deprecated. " +
        "Use { 'html': src } -- no need to escape or clean the string."
      );
      var src = p.source.javascript;
      src = src.replace(/\\n/g, "\n");
      src = src.replace(/\\r/g, "\r");
      src = src.replace(/\\'/g, "'");
      p.source = { html: src };
    }

    // If supplied as DOM nodes, convert to HTML by concatenating outerHTMLs.
    if (p.source.nodes) {
      var srcs = [];
      for (var i = 0, ii = p.source.nodes.length; i < ii; ++i) {
        var node = p.source.nodes[i];
        if (node.outerHTML) {
          srcs.push(node.outerHTML);
        } else {
          var div = document.createElement('div');
          div.appendChild(node.cloneNode(true));
          srcs.push(div.innerHTML);
          delete(div);
        }
      }
      p.source = { html: srcs.join('') };
    }

    if (p.source.html && !p.source.html.match(new RegExp("<base\s.+>", "im"))) {
      var baseURI = computeBaseURI(reader);
      if (baseURI) {
        p.source.html = p.source.html.replace(
          new RegExp("(<head[^>]*>)", "im"),
          '$1<base href="'+baseURI+'" />'
        );
      }
    }

    if (p.source.doc && !p.source.doc.querySelector('base')) {
      var srcHead = p.source.doc.querySelector('head') || p.source.doc.body;
      var baseURI = computeBaseURI(reader);
      if (srcHead && baseURI) {
        var srcBase = p.source.doc.createElement('base');
        srcBase.setAttribute('href', baseURI);
        prependChild(srcHead, srcBase);
      }
    }
  }

  function computeBaseURI(reader) {
    var evtData = { cmptId: p.id, cmptURI: absoluteURL(p.id) }
    if (reader.dispatchEvent('monocle:component:baseuri', evtData, true)) {
      return evtData.cmptURI;
    }
  }

  function absoluteURL(url) {
    var link = document.createElement('a');
    link.setAttribute('href', url);
    result = link.href;
    delete(link);
    return result;
  }

  function prependChild(pr, el) {
    pr.firstChild ? pr.insertBefore(el, pr.firstChild) : pr.appendChild(el);
  }

  API.applyTo = applyTo;
  API.updateDimensions = updateDimensions;
  API.chapterForPage = chapterForPage;
  API.pageForChapter = pageForChapter;
  API.pageForXPath = pageForXPath;
  API.pageForSelector = pageForSelector;
  API.lastPageNumber = lastPageNumber;

  return API;
}
<commit_msg>Nothing can stop iframe scrolling on Android, so we resort to undoing it.<commit_after>/* COMPONENT */

// See the properties declaration for details of constructor arguments.
//
Monocle.Component = function (book, id, index, chapters, source) {

  var API = { constructor: Monocle.Component }
  var k = API.constants = API.constructor;
  var p = API.properties = {
    // a back-reference to the public API of the book that owns this component
    book: book,

    // the string that represents this component in the book's component array
    id: id,

    // the position in the book's components array of this component
    index: index,

    // The chapters argument is an array of objects that list the chapters that
    // can be found in this component. A chapter object is defined as:
    //
    //  {
    //     title: str,
    //     fragment: str, // optional anchor id
    //     percent: n     // how far into the component the chapter begins
    //  }
    //
    // NOTE: the percent property is calculated by the component - you only need
    // to pass in the title and the optional id string.
    //
    chapters: chapters,

    // the frame provided by dataSource.getComponent() for this component
    source: source
  }

  // Makes this component the active component for the pageDiv. There are
  // several strategies for this (see loadFrame).
  //
  // When the component has been loaded into the pageDiv's frame, the callback
  // will be invoked with the pageDiv and this component as arguments.
  //
  function applyTo(pageDiv, callback) {
    prepareSource(pageDiv.m.reader);

    var evtData = { 'page': pageDiv, 'source': p.source };
    pageDiv.m.reader.dispatchEvent('monocle:componentchanging', evtData);

    var onLoaded = function () {
      setupFrame(
        pageDiv,
        pageDiv.m.activeFrame,
        function () { callback(pageDiv, API) }
      );
    }

    Monocle.defer(function () { loadFrame(pageDiv, onLoaded); });
  }

  // Loads this component into the given frame, using one of the following
  // strategies:
  //
  // * HTML - a HTML string
  // * URL - a URL string
  // * Nodes - an array of DOM body nodes (NB: no way to populate head)
  // * Document - a DOM DocumentElement object
  //
  function loadFrame(pageDiv, callback) {
    var frame = pageDiv.m.activeFrame;

    // We own this frame now.
    frame.m.component = API;

    // Hide the frame while we're changing it.
    frame.style.visibility = "hidden";

    frame.whenDocumentReady = function () {
      var doc = frame.contentDocument;
      var evtData = { 'page': pageDiv, 'document': doc, 'component': API };
      pageDiv.m.reader.dispatchEvent('monocle:componentmodify', evtData);
      frame.whenDocumentReady = null;
    }

    if (p.source.html) {
      return loadFrameFromHTML(p.source.html || p.source, frame, callback);
    } else if (p.source.url) {
      return loadFrameFromURL(p.source.url, frame, callback);
    } else if (p.source.doc) {
      return loadFrameFromDocument(p.source.doc, frame, callback);
    }
  }

  // LOAD STRATEGY: HTML
  // Loads a HTML string into the given frame, invokes the callback once loaded.
  //
  function loadFrameFromHTML(src, frame, callback) {
    var fn = function () {
      Monocle.Events.deafen(frame, 'load', fn);
      frame.whenDocumentReady();
      Monocle.defer(callback);
    }
    Monocle.Events.listen(frame, 'load', fn);
    frame.contentWindow['monCmptData'] = src;
    frame.src = "javascript:window['monCmptData'];"
  }

  // LOAD STRATEGY: URL
  // Loads the URL into the given frame, invokes callback once loaded.
  //
  function loadFrameFromURL(url, frame, callback) {
    // If it's a relative path, we need to make it absolute.
    if (!url.match(/^\//)) {
      url = absoluteURL(url);
    }
    var onDocumentReady = function () {
      Monocle.Events.deafen(frame, 'load', onDocumentReady);
      frame.whenDocumentReady();
    }
    var onDocumentLoad = function () {
      Monocle.Events.deafen(frame, 'load', onDocumentLoad);
      Monocle.defer(callback);
    }
    Monocle.Events.listen(frame, 'load', onDocumentReady);
    Monocle.Events.listen(frame, 'load', onDocumentLoad);
    frame.contentWindow.location.replace(url);
  }

  // LOAD STRATEGY: DOCUMENT
  // Replaces the DocumentElement of the given frame with the given srcDoc.
  // Invokes the callback when loaded.
  //
  function loadFrameFromDocument(srcDoc, frame, callback) {
    var doc = frame.contentDocument;

    // WebKit has an interesting quirk. The <base> tag must exist in the
    // document being replaced, not the new document.
    if (Monocle.Browser.is.WebKit) {
      var srcBase = srcDoc.querySelector('base');
      if (srcBase) {
        var head = doc.querySelector('head');
        if (!head) {
          try {
            head = doc.createElement('head');
            prependChild(doc.documentElement, head);
          } catch (e) {
            head = doc.body;
          }
        }
        var base = doc.createElement('base');
        base.setAttribute('href', srcBase.href);
        head.appendChild(base);
      }
    }

    doc.replaceChild(
      doc.importNode(srcDoc.documentElement, true),
      doc.documentElement
    );

    // NB: It's a significant problem with this load strategy that there's
    // no indication when it is complete.
    Monocle.defer(callback);
  }

  // Once a frame is loaded with this component, call this method to style
  // and measure its contents.
  //
  function setupFrame(pageDiv, frame, callback) {
    updateDimensions(pageDiv, function () {
      frame.style.visibility = "visible";

      // Find the place of any chapters in the component.
      locateChapters(pageDiv);

      // Nothing can prevent iframe scrolling on Android, so we have to undo it.
      if (Monocle.Browser.on.Android) {
        Monocle.Events.listen(frame.contentWindow, 'scroll', function () {
          frame.contentWindow.scrollTo(0,0);
        });
      }

      // Announce that the component has changed.
      var doc = frame.contentDocument;
      var evtData = { 'page': pageDiv, 'document': doc, 'component': API };
      pageDiv.m.reader.dispatchEvent('monocle:componentchange', evtData);

      callback();
    });
  }

  // Checks whether the pageDiv dimensions have changed. If they have,
  // remeasures dimensions and returns true. Otherwise returns false.
  //
  function updateDimensions(pageDiv, callback) {
    pageDiv.m.dimensions.update(function (pageLength) {
      p.pageLength = pageLength;
      if (typeof callback == "function") { callback() };
    });
  }

  // Iterates over all the chapters that are within this component
  // (according to the array we were provided on initialization) and finds
  // their location (in percentage terms) within the text.
  //
  // Stores this percentage with the chapter object in the chapters array.
  //
  function locateChapters(pageDiv) {
    if (p.chapters[0] && typeof p.chapters[0].percent == "number") {
      return;
    }
    var doc = pageDiv.m.activeFrame.contentDocument;
    for (var i = 0; i < p.chapters.length; ++i) {
      var chp = p.chapters[i];
      chp.percent = 0;
      if (chp.fragment) {
        var node = doc.getElementById(chp.fragment);
        chp.percent = pageDiv.m.dimensions.percentageThroughOfNode(node);
      }
    }
    return p.chapters;
  }

  // For a given page number within the component, return the chapter that
  // starts on or most-recently-before this page.
  //
  // Useful, for example, in displaying the current chapter title as a
  // running head on the page.
  //
  function chapterForPage(pageN) {
    var cand = null;
    var percent = (pageN - 1) / p.pageLength;
    for (var i = 0; i < p.chapters.length; ++i) {
      if (percent >= p.chapters[i].percent) {
        cand = p.chapters[i];
      } else {
        return cand;
      }
    }
    return cand;
  }

  // For a given chapter fragment (the bit after the hash
  // in eg, "index.html#foo"), return the page number on which
  // the chapter starts. If the fragment is null or blank, will
  // return the first page of the component.
  //
  function pageForChapter(fragment, pageDiv) {
    if (!fragment) {
      return 1;
    }
    for (var i = 0; i < p.chapters.length; ++i) {
      if (p.chapters[i].fragment == fragment) {
        return percentToPageNumber(p.chapters[i].percent);
      }
    }
    var doc = pageDiv.m.activeFrame.contentDocument;
    var node = doc.getElementById(fragment);
    var percent = pageDiv.m.dimensions.percentageThroughOfNode(node);
    return percentToPageNumber(percent);
  }

  function pageForXPath(xpath, pageDiv) {
    var doc = pageDiv.m.activeFrame.contentDocument;
    var percent = 0;
    if (Monocle.Browser.env.supportsXPath) {
      var node = doc.evaluate(xpath, doc, null, 9, null).singleNodeValue;
      if (node) {
        percent = pageDiv.m.dimensions.percentageThroughOfNode(node);
      }
    } else {
      console.warn("XPath not supported in this client.");
    }
    return percentToPageNumber(percent);
  }

  function pageForSelector(selector, pageDiv) {
    var doc = pageDiv.m.activeFrame.contentDocument;
    var percent = 0;
    if (Monocle.Browser.env.supportsQuerySelector) {
      var node = doc.querySelector(selector);
      if (node) {
        percent = pageDiv.m.dimensions.percentageThroughOfNode(node);
      }
    } else {
      console.warn("querySelector not supported in this client.");
    }
    return percentToPageNumber(percent);
  }

  function percentToPageNumber(pc) {
    return Math.floor(pc * p.pageLength) + 1;
  }

  // A public getter for p.pageLength.
  //
  function lastPageNumber() {
    return p.pageLength;
  }

  function prepareSource(reader) {
    if (p.sourcePrepared) { return; }
    p.sourcePrepared = true;

    if (typeof p.source == "string") {
      p.source = { html: p.source };
    }

    // If supplied as escaped javascript, unescape it to HTML by evalling it.
    if (p.source.javascript) {
      console.deprecation(
        "Loading a component by 'javascript' is deprecated. " +
        "Use { 'html': src } -- no need to escape or clean the string."
      );
      var src = p.source.javascript;
      src = src.replace(/\\n/g, "\n");
      src = src.replace(/\\r/g, "\r");
      src = src.replace(/\\'/g, "'");
      p.source = { html: src };
    }

    // If supplied as DOM nodes, convert to HTML by concatenating outerHTMLs.
    if (p.source.nodes) {
      var srcs = [];
      for (var i = 0, ii = p.source.nodes.length; i < ii; ++i) {
        var node = p.source.nodes[i];
        if (node.outerHTML) {
          srcs.push(node.outerHTML);
        } else {
          var div = document.createElement('div');
          div.appendChild(node.cloneNode(true));
          srcs.push(div.innerHTML);
          delete(div);
        }
      }
      p.source = { html: srcs.join('') };
    }

    if (p.source.html && !p.source.html.match(new RegExp("<base\s.+>", "im"))) {
      var baseURI = computeBaseURI(reader);
      if (baseURI) {
        p.source.html = p.source.html.replace(
          new RegExp("(<head[^>]*>)", "im"),
          '$1<base href="'+baseURI+'" />'
        );
      }
    }

    if (p.source.doc && !p.source.doc.querySelector('base')) {
      var srcHead = p.source.doc.querySelector('head') || p.source.doc.body;
      var baseURI = computeBaseURI(reader);
      if (srcHead && baseURI) {
        var srcBase = p.source.doc.createElement('base');
        srcBase.setAttribute('href', baseURI);
        prependChild(srcHead, srcBase);
      }
    }
  }

  function computeBaseURI(reader) {
    var evtData = { cmptId: p.id, cmptURI: absoluteURL(p.id) }
    if (reader.dispatchEvent('monocle:component:baseuri', evtData, true)) {
      return evtData.cmptURI;
    }
  }

  function absoluteURL(url) {
    var link = document.createElement('a');
    link.setAttribute('href', url);
    result = link.href;
    delete(link);
    return result;
  }

  function prependChild(pr, el) {
    pr.firstChild ? pr.insertBefore(el, pr.firstChild) : pr.appendChild(el);
  }

  API.applyTo = applyTo;
  API.updateDimensions = updateDimensions;
  API.chapterForPage = chapterForPage;
  API.pageForChapter = pageForChapter;
  API.pageForXPath = pageForXPath;
  API.pageForSelector = pageForSelector;
  API.lastPageNumber = lastPageNumber;

  return API;
}
<|endoftext|>


<commit_before>//load statechart from scxml string; return initial state and token id

//send event to statechart with tokenid
//clean up statechart

var scxml = require('..'),
    http = require('http');

var sessionCounter = 0, sessions = {}, timeouts = {}, timeoutMs = 5000;

function loadScxml(scxmlStr){
}

function cleanUp(sessionToken){
    delete sessions[sessionToken];
}

http.createServer(function (req, res) {
    //TODO: set a timeout to clean up if we don't hear back for a while
    var s = "";
    req.on("data",function(data){
        s += data;
    });
    req.on("end",function(){
        var sessionToken;
        try{
            var reqJson = JSON.parse(s);
            if(reqJson.load){
                console.log("Loading new statechart",reqJson.load);

                scxml.urlToModel(reqJson.load,function(err,model){
                    //console.log('model',model);
                    if(err){
                        console.error(err.stack);
                        res.writeHead(500, {'Content-Type': 'text/plain'});
                        res.end(err.message);
                    }else{
                        var interpreter = new scxml.scion.Statechart(model, { });

                        var sessionToken = sessionCounter;
                        sessionCounter++;
                        sessions[sessionToken] = interpreter;

                        var conf = interpreter.start();

                        res.writeHead(200, {'Content-Type': 'application/json'});
                        res.end(JSON.stringify({
                            sessionToken : sessionToken,
                            nextConfiguration : conf
                        }));

                        timeouts[sessionToken] = setTimeout(function(){cleanUp(sessionToken);},timeoutMs);
                    }
                });

            }else if(reqJson.event && (typeof reqJson.sessionToken === "number")){
                console.log("sending event to statechart",reqJson.event);
                sessionToken = reqJson.sessionToken;
                var nextConfiguration = sessions[sessionToken].gen(reqJson.event);
                console.log('nextConfiguration',nextConfiguration);
                res.writeHead(200, {'Content-Type': 'application/json'});
                res.end(JSON.stringify({
                    nextConfiguration : nextConfiguration
                }));

                clearTimeout(timeouts[sessionToken]);
                timeouts[sessionToken] = setTimeout(function(){cleanUp(sessionToken);},timeoutMs);
            }else{
                //unrecognized. send back an error
                res.writeHead(400, {'Content-Type': 'text/plain'});
                res.end("Unrecognized request.\n");
            }
        }catch(e){
            console.error(e.stack);
            console.error(e);
            res.writeHead(500, {'Content-Type': 'text/plain'});
            res.end(e.message);
        }
    });
}).listen(42000, '127.0.0.1');

<commit_msg>Send sessionid<commit_after>//load statechart from scxml string; return initial state and token id

//send event to statechart with tokenid
//clean up statechart

var scxml = require('..'),
    http = require('http');

var sessionCounter = 0, sessions = {}, timeouts = {}, timeoutMs = 5000;

function loadScxml(scxmlStr){
}

function cleanUp(sessionToken){
    delete sessions[sessionToken];
}

http.createServer(function (req, res) {
    //TODO: set a timeout to clean up if we don't hear back for a while
    var s = "";
    req.on("data",function(data){
        s += data;
    });
    req.on("end",function(){
        var sessionToken;
        try{
            var reqJson = JSON.parse(s);
            if(reqJson.load){
                console.log("Loading new statechart",reqJson.load);

                scxml.urlToModel(reqJson.load,function(err,model){
                    //console.log('model',model);
                    if(err){
                        console.error(err.stack);
                        res.writeHead(500, {'Content-Type': 'text/plain'});
                        res.end(err.message);
                    }else{
                        var interpreter = new scxml.scion.Statechart(model, { sessionid: sessionCounter });

                        var sessionToken = sessionCounter;
                        sessionCounter++;
                        sessions[sessionToken] = interpreter;

                        var conf = interpreter.start();

                        res.writeHead(200, {'Content-Type': 'application/json'});
                        res.end(JSON.stringify({
                            sessionToken : sessionToken,
                            nextConfiguration : conf
                        }));

                        timeouts[sessionToken] = setTimeout(function(){cleanUp(sessionToken);},timeoutMs);
                    }
                });

            }else if(reqJson.event && (typeof reqJson.sessionToken === "number")){
                console.log("sending event to statechart",reqJson.event);
                sessionToken = reqJson.sessionToken;
                var nextConfiguration = sessions[sessionToken].gen(reqJson.event);
                console.log('nextConfiguration',nextConfiguration);
                res.writeHead(200, {'Content-Type': 'application/json'});
                res.end(JSON.stringify({
                    nextConfiguration : nextConfiguration
                }));

                clearTimeout(timeouts[sessionToken]);
                timeouts[sessionToken] = setTimeout(function(){cleanUp(sessionToken);},timeoutMs);
            }else{
                //unrecognized. send back an error
                res.writeHead(400, {'Content-Type': 'text/plain'});
                res.end("Unrecognized request.\n");
            }
        }catch(e){
            console.error(e.stack);
            console.error(e);
            res.writeHead(500, {'Content-Type': 'text/plain'});
            res.end(e.message);
        }
    });
}).listen(42000, '127.0.0.1');

<|endoftext|>