<commit_before>/**
 * Created by igors on 7/12/16.
 */

var proxyquire = require('proxyquire');
var chai = require('chai');
var expect = chai.expect;
var sinon = require('sinon');
var Q = require('Q');

describe('Playlist Generator spec', function() {

    var config = require('./../../common/Configuration');
    config.set('playlistConfig', {
        "debug": false,
        "waitTimeBeforeRemoval": 100000,
        "maxChunkGapSize": 10000,
        "maxAllowedPTSDrift": 500,
        "timestampToleranceMs": 2,
        "humanReadable": true,
        "keyFrameSearchOffsetMs": 1,
        "segmentDuration": 10000,
        "dontInitializePlaylist" : false,
        "enablePlaylistHistory": false,
        "skipPathCheck": true
    });
    config.set('logToConsole',false);
    var fs = require('fs');
    var path = require('path');
    var PlaylistGenerator = require('./../../lib/PlaylistGenerator/PlaylistGenerator');
    var Playlist = require('./../../lib/PlaylistGenerator/Playlist');
    var _ = require('underscore');
    var t = require('tmp');

    var fileInfos = [
        { startTime: 1459270805911,
            sig: 'C53429E60F33B192FD124A2CC22C8717',
            video:
            { duration: 16224.699999999999,
                firstDTS: 1459270805994,
                firstEncoderDTS: 83,
                wrapEncoderDTS: 95443718 },
            audio:
            { duration: 16277.333333333334,
                firstDTS: 1459270805911,
                firstEncoderDTS: 0,
                wrapEncoderDTS: 95443718 },
            path: '/var/tmp/media-u774d8hoj_w20128143_1.mp4',
            flavor: "32"
        },
        { startTime: 1459270822434,
            sig: 'CBD793B1A2D5D3FF7A3FB4A888C746F2',
            video:
            { duration: 7131.700000000001,
                firstDTS: 1459270822434,
                firstEncoderDTS: 16308,
                wrapEncoderDTS: 95443718 },
            audio:
            { duration: 7061.333333333334,
                firstDTS: 1459270822446,
                firstEncoderDTS: 16320,
                wrapEncoderDTS: 95443718 },
            path: '/var/tmp/media-u774d8hoj_w20128143_2.mp4',
            flavor: "32" },
        { startTime: 1459270829426,
            sig: 'B3F857AB9FCB2E440BBBD14BDADAC54F',
            video:
            { duration: 12971.7,
                firstDTS: 1459270829442,
                firstEncoderDTS: 23440,
                wrapEncoderDTS: 95443718 },
            audio:
            { duration: 12949.333333333334,
                firstDTS: 1459270829426,
                firstEncoderDTS: 23424,
                wrapEncoderDTS: 95443718 },
            path: '/var/tmp/media-u774d8hoj_w20128143_3.mp4' ,
            flavor: "32"},
        { startTime: 1459270842369,
            sig: 'A3D511E62069A1F10523A2BC435DA977',
            video:
            { duration: 10385.7,
                firstDTS: 1459270842369,
                firstEncoderDTS: 36411,
                wrapEncoderDTS: 95443718 },
            audio:
            { duration: 10325.333333333332,
                firstDTS: 1459270842374,
                firstEncoderDTS: 36416,
                wrapEncoderDTS: 95443718 },
            path: '/var/tmp/media-u774d8hoj_w20128143_4.mp4' ,
            flavor: "32" }
    ];

    var createPlaylistGenerator = function(windowSize){

        config.set('rootFolderPath',t.dirSync().name);

        windowSize = windowSize || 100;

        var entry = {
                entryId:'abc',
                playWindow:windowSize
            };

        var plGen = new PlaylistGenerator(entry,true);
        return plGen.start();
    };

    //convert Playlist class type to plain object
    var jsonize = function(plGen) {
        var retval = JSON.parse(JSON.stringify(plGen.playlistImp));
        expect(retval).to.not.be.empty;
        return retval;
    };

    // produce fileInfo with all fileds offset by same <offset>
    var offsetFileInfo = function(fi,offset){

        offset = offset || (fi.video ? fi.video.duration : fi.audio.duration);
        var n = _.clone(fi);
        n.startTime += offset;
        if(n.video) {
            n.video = _.clone(n.video);
            n.video.firstDTS += offset;
            n.video.firstEncoderDTS += offset;
            n.video.firstEncoderDTS %= n.video.wrapEncoderDTS;
        }
        if(n.audio) {
            n.audio = _.clone(n.audio);
            n.audio.firstDTS += offset;
            n.audio.firstEncoderDTS += offset;
            n.audio.firstEncoderDTS %= n.audio.wrapEncoderDTS;
        }
        n.path = n.path.split('.').join('-' + n.startTime + '.');
        return n;
    };

    var updatePlaylist = function(plGen,fis){
        return Q.allSettled(plGen.update(fis)).then(function(){
            return Q.resolve(jsonize(plGen));
        });
    };

    it('should correctly serialize from existing playlist and to JSON', function(done)
    {
        var expectedPlaylist = fs.readFileSync(path.join(__dirname, '/../resources/playlist.json'), 'utf8');

        var playlist = new Playlist('test',JSON.parse(expectedPlaylist));

         expect(JSON.stringify(playlist)).to.eql(expectedPlaylist);
         done();
    });

    it('should update duration when add an item', function(done)
    {
        createPlaylistGenerator().then( function(plGen) {
            var fi = { startTime: 1459270805911,
                sig: 'C53429E60F33B192FD124A2CC22C8717',
                video:
                { duration: 16224.699999999999,
                    firstDTS: 1459270805911,
                    firstEncoderDTS: 83,
                    wrapEncoderDTS: 95443718 },
                path: '/var/tmp/media-u774d8hoj_w20128143_1.mp4',
                flavor: "32"
            };
            updatePlaylist(plGen,[fi]).then(function (result) {
                expect(result.durations[0]).to.eql(Math.ceil(fi.video.duration));
                done();
            }).catch(function (err) {
                done(err);
            });
        });
    });

    it('should reject item with duplicate filename', function(done)
    {
        createPlaylistGenerator().then( function(plGen) {
            var fi = { startTime: 1459270805911,
                sig: 'C53429E60F33B192FD124A2CC22C8717',
                video:
                { duration: 16224.699999999999,
                    firstDTS: 1459270805911,
                    firstEncoderDTS: 83,
                    wrapEncoderDTS: 95443718 },
                path: '/var/tmp/media-u774d8hoj_w20128143_1.mp4',
                flavor: "32"
            }, bad = offsetFileInfo(fi);

            bad.path = fi.path;

            updatePlaylist(plGen,[fi,bad]).then(function (result) {
                expect(result.durations[0]).to.eql(Math.ceil(fi.video.duration));
                expect(result.sequences[0].clips[0].sources[0].paths.length).to.eql(1);
                done();
            }).catch(function (err) {
                done(err);
            });
        });
    });

    it('should create gap', function(done)
    {
        createPlaylistGenerator().then( function(plGen) {
            updatePlaylist(plGen,[fileInfos[0],fileInfos[fileInfos.length-1]]).then(function(obj) {
                expect(obj.durations.length).to.eql(2);
                done();
            }).catch(function(err){
                done(err);
            });
        });
    });

    it('should pop item when window rolls on', function(done)
    {

        var fi = { startTime: 1459270805911,
            sig: 'C53429E60F33B192FD124A2CC22C8717',
            video:
            { duration: 16225,
                firstDTS: 1459270805911,
                firstEncoderDTS: 83,
                wrapEncoderDTS: 95443718 },
            path: '/var/tmp/media-u774d8hoj_w20128143_1.mp4',
            flavor: "32"
        };

        var fis = [];

        for(var i = 0; i < 6; i++){
            fi = offsetFileInfo(fi,fi.video.duration);
            fis.push(fi);
        }

        var windowSize = fi.video.duration * 2;

        createPlaylistGenerator(Math.ceil(windowSize / 1000)).then( function(plGen) {
            updatePlaylist(plGen,fis).then(function (obj) {
                expect(obj.durations[0]).to.at.most(windowSize+fi.video.duration);
                done();
            }).catch(function (err) {
                done(err);
            });
        });
    });

    it('should be able to add item despite wrong id 3 tamestamp', function(done)
    {
        createPlaylistGenerator().then( function(plGen) {

            var fis = { startTime: 1459270805911,
                sig: 'C53429E60F33B192FD124A2CC22C8717',
                video:
                { duration: 16224.699999999999,
                    firstDTS: 1459270805911,
                    firstEncoderDTS: 83,
                    wrapEncoderDTS: 95443718 },
                path: '/var/tmp/media-u774d8hoj_w20128143_1.mp4',
                flavor: "32"
            };

            var bad = offsetFileInfo(fis,fis.video.duration);
            //scramble firstDTS
            bad.video.firstDTS -= 10000000;

            updatePlaylist(plGen,[fis,bad]).then(function (obj) {
                expect(obj.durations.length).eql(1);
                done();
            }).catch(function (err) {
                done(err);
            });
        });
    });

    it('should produce gap despite id3 timestamp = 0', function(done)
    {
        createPlaylistGenerator().then( function(plGen) {

            var fis = { startTime: 1459270805911,
                sig: 'C53429E60F33B192FD124A2CC22C8717',
                video:
                { duration: 16224.699999999999,
                    firstDTS: 1459270805911,
                    firstEncoderDTS: 83,
                    wrapEncoderDTS: 95443718 },
                path: '/var/tmp/media-u774d8hoj_w20128143_1.mp4',
                flavor: "32"
            };

            var bad = offsetFileInfo(fis,60000);
            //scramble firstDTS
            bad.startTime = bad.video.firstDTS = 0;

            var good = offsetFileInfo(fis,60000+bad.video.duration);

            updatePlaylist(plGen,[fis,bad,good]).then(function (result) {
                expect(result.durations.length).eql(2);
                expect(result.clipTimes[1]).to.be.within(good.video.firstDTS-1,good.video.firstDTS+1);
                done();
            }).catch(function (err) {
                done(err);
            });
        });
    });

});
<commit_msg>playlist generator unit test<commit_after>/**
 * Created by igors on 7/12/16.
 */

var proxyquire = require('proxyquire');
var chai = require('chai');
var expect = chai.expect;
var sinon = require('sinon');
var Q = require('Q');

describe('Playlist Generator spec', function() {

    var config = require('./../../common/Configuration');
    config.set('playlistConfig', {
        "debug": false,
        "waitTimeBeforeRemoval": 100000,
        "maxChunkGapSize": 10000,
        "maxAllowedPTSDrift": 500,
        "timestampToleranceMs": 2,
        "humanReadable": true,
        "keyFrameSearchOffsetMs": 1,
        "segmentDuration": 10000,
        "dontInitializePlaylist" : false,
        "enablePlaylistHistory": false,
        "skipPathCheck": true
    });
    config.set('logToConsole',false);
    var fs = require('fs');
    var path = require('path');
    var PlaylistGenerator = require('./../../lib/PlaylistGenerator/PlaylistGenerator');
    var Playlist = require('./../../lib/PlaylistGenerator/Playlist');
    var _ = require('underscore');
    var t = require('tmp');

    var fileInfos = [
        { startTime: 1459270805911,
            sig: 'C53429E60F33B192FD124A2CC22C8717',
            video:
            { duration: 16224.699999999999,
                firstDTS: 1459270805994,
                firstEncoderDTS: 83,
                wrapEncoderDTS: 95443718 },
            audio:
            { duration: 16277.333333333334,
                firstDTS: 1459270805911,
                firstEncoderDTS: 0,
                wrapEncoderDTS: 95443718 },
            path: '/var/tmp/media-u774d8hoj_w20128143_1.mp4',
            flavor: "32"
        },
        { startTime: 1459270822434,
            sig: 'CBD793B1A2D5D3FF7A3FB4A888C746F2',
            video:
            { duration: 7131.700000000001,
                firstDTS: 1459270822434,
                firstEncoderDTS: 16308,
                wrapEncoderDTS: 95443718 },
            audio:
            { duration: 7061.333333333334,
                firstDTS: 1459270822446,
                firstEncoderDTS: 16320,
                wrapEncoderDTS: 95443718 },
            path: '/var/tmp/media-u774d8hoj_w20128143_2.mp4',
            flavor: "32" },
        { startTime: 1459270829426,
            sig: 'B3F857AB9FCB2E440BBBD14BDADAC54F',
            video:
            { duration: 12971.7,
                firstDTS: 1459270829442,
                firstEncoderDTS: 23440,
                wrapEncoderDTS: 95443718 },
            audio:
            { duration: 12949.333333333334,
                firstDTS: 1459270829426,
                firstEncoderDTS: 23424,
                wrapEncoderDTS: 95443718 },
            path: '/var/tmp/media-u774d8hoj_w20128143_3.mp4' ,
            flavor: "32"},
        { startTime: 1459270842369,
            sig: 'A3D511E62069A1F10523A2BC435DA977',
            video:
            { duration: 10385.7,
                firstDTS: 1459270842369,
                firstEncoderDTS: 36411,
                wrapEncoderDTS: 95443718 },
            audio:
            { duration: 10325.333333333332,
                firstDTS: 1459270842374,
                firstEncoderDTS: 36416,
                wrapEncoderDTS: 95443718 },
            path: '/var/tmp/media-u774d8hoj_w20128143_4.mp4' ,
            flavor: "32" }
    ];

    var createPlaylistGenerator = function(windowSize){

        config.set('rootFolderPath',t.dirSync().name);

        windowSize = windowSize || 100;

        var entry = {
                entryId:'abc',
                playWindow:windowSize
            };

        var plGen = new PlaylistGenerator(entry,true);
        return plGen.start();
    };

    //convert Playlist class type to plain object
    var jsonize = function(plGen) {
        var retval = JSON.parse(JSON.stringify(plGen.playlistImp));
        expect(retval).to.not.be.empty;
        return retval;
    };

    var deepCloneFileInfo = function(fi) {
        var n = _.clone(fi);
        if(fi.video){
            n.video = _.clone(fi.video);
        }
        if(fi.audio){
            n.audio = _.clone(fi.audio);
        }
        return n;
    };

    // produce fileInfo with all fileds offset by same <offset>
    var offsetFileInfo = function(fi,offset){

        offset = offset || (fi.video ? fi.video.duration : fi.audio.duration);
        var n = deepCloneFileInfo(fi);
        n.startTime += offset;
        if(n.video) {
            n.video.firstDTS += offset;
            n.video.firstEncoderDTS += offset;
            n.video.firstEncoderDTS %= n.video.wrapEncoderDTS;
        }
        if(n.audio) {
            n.audio.firstDTS += offset;
            n.audio.firstEncoderDTS += offset;
            n.audio.firstEncoderDTS %= n.audio.wrapEncoderDTS;
        }
        n.path = n.path.split('.').join('-' + n.startTime + '.');
        return n;
    };

    var updatePlaylist = function(plGen,fis){
        return Q.allSettled(plGen.update(fis)).then(function(){
            return Q.resolve(jsonize(plGen));
        });
    };

    it('should correctly serialize from existing playlist and to JSON', function(done)
    {
        var expectedPlaylist = fs.readFileSync(path.join(__dirname, '/../resources/playlist.json'), 'utf8');

        var playlist = new Playlist('test',JSON.parse(expectedPlaylist));

         expect(JSON.stringify(playlist)).to.eql(expectedPlaylist);
         done();
    });

    it('should update duration when add an item', function(done)
    {
        createPlaylistGenerator().then( function(plGen) {
            var fi = { startTime: 1459270805911,
                sig: 'C53429E60F33B192FD124A2CC22C8717',
                video:
                { duration: 16224.699999999999,
                    firstDTS: 1459270805911,
                    firstEncoderDTS: 83,
                    wrapEncoderDTS: 95443718 },
                path: '/var/tmp/media-u774d8hoj_w20128143_1.mp4',
                flavor: "32"
            };
            updatePlaylist(plGen,[fi]).then(function (result) {
                expect(result.durations[0]).to.eql(Math.ceil(fi.video.duration));
                done();
            }).catch(function (err) {
                done(err);
            });
        });
    });

    it('should update duration when add an audio only item', function(done)
    {
        createPlaylistGenerator().then( function(plGen) {
            var fi = { startTime: 1459270805911,
                sig: 'C53429E60F33B192FD124A2CC22C8717',
                audio:
                { duration: 16224.699999999999,
                    firstDTS: 1459270805911,
                    firstEncoderDTS: 83,
                    wrapEncoderDTS: 95443718 },
                path: '/var/tmp/media-u774d8hoj_w20128143_1.mp4',
                flavor: "32"
            };
            updatePlaylist(plGen,[fi]).then(function (result) {
                expect(result.durations[0]).to.eql(Math.ceil(fi.audio.duration));
                done();
            }).catch(function (err) {
                done(err);
            });
        });
    });

    it('should reject item with duplicate filename', function(done)
    {
        createPlaylistGenerator().then( function(plGen) {
            var fi = { startTime: 1459270805911,
                sig: 'C53429E60F33B192FD124A2CC22C8717',
                video:
                { duration: 16224.699999999999,
                    firstDTS: 1459270805911,
                    firstEncoderDTS: 83,
                    wrapEncoderDTS: 95443718 },
                path: '/var/tmp/media-u774d8hoj_w20128143_1.mp4',
                flavor: "32"
            }, bad = offsetFileInfo(fi);

            bad.path = fi.path;

            updatePlaylist(plGen,[fi,bad]).then(function (result) {
                expect(result.durations[0]).to.eql(Math.ceil(fi.video.duration));
                expect(result.sequences[0].clips[0].sources[0].paths.length).to.eql(1);
                done();
            }).catch(function (err) {
                done(err);
            });
        });
    });

    it('should create gap', function(done)
    {
        createPlaylistGenerator().then( function(plGen) {
            updatePlaylist(plGen,[fileInfos[0],fileInfos[fileInfos.length-1]]).then(function(obj) {
                expect(obj.durations.length).to.eql(2);
                done();
            }).catch(function(err){
                done(err);
            });
        });
    });

    it('should create gap audio only', function(done)
    {
        var a1 = deepCloneFileInfo(fileInfos[0]),
            a2 = deepCloneFileInfo(fileInfos[fileInfos.length-1]);
        delete a1.video;
        delete a2.video;
        createPlaylistGenerator().then( function(plGen) {
            updatePlaylist(plGen,[a1,a2]).then(function(obj) {
                expect(obj.durations.length).to.eql(2);
                done();
            }).catch(function(err){
                done(err);
            });
        });
    });

    it('should pop item when window rolls on', function(done)
    {

        var fi = { startTime: 1459270805911,
            sig: 'C53429E60F33B192FD124A2CC22C8717',
            video:
            { duration: 16225,
                firstDTS: 1459270805911,
                firstEncoderDTS: 83,
                wrapEncoderDTS: 95443718 },
            path: '/var/tmp/media-u774d8hoj_w20128143_1.mp4',
            flavor: "32"
        };

        var fis = [];

        for(var i = 0; i < 6; i++){
            fi = offsetFileInfo(fi,fi.video.duration);
            fis.push(fi);
        }

        var windowSize = fi.video.duration * 2;

        createPlaylistGenerator(Math.ceil(windowSize / 1000)).then( function(plGen) {
            updatePlaylist(plGen,fis).then(function (obj) {
                expect(obj.durations[0]).to.at.most(windowSize+fi.video.duration);
                done();
            }).catch(function (err) {
                done(err);
            });
        });
    });

    it('should be able to add item despite wrong id 3 tamestamp', function(done)
    {
        createPlaylistGenerator().then( function(plGen) {

            var fis = { startTime: 1459270805911,
                sig: 'C53429E60F33B192FD124A2CC22C8717',
                video:
                { duration: 16224.699999999999,
                    firstDTS: 1459270805911,
                    firstEncoderDTS: 83,
                    wrapEncoderDTS: 95443718 },
                path: '/var/tmp/media-u774d8hoj_w20128143_1.mp4',
                flavor: "32"
            };

            var bad = offsetFileInfo(fis,fis.video.duration);
            //scramble firstDTS
            bad.video.firstDTS -= 10000000;

            updatePlaylist(plGen,[fis,bad]).then(function (obj) {
                expect(obj.durations.length).eql(1);
                done();
            }).catch(function (err) {
                done(err);
            });
        });
    });

    it('should produce gap despite id3 timestamp = 0', function(done)
    {
        createPlaylistGenerator().then( function(plGen) {

            var fis = { startTime: 1459270805911,
                sig: 'C53429E60F33B192FD124A2CC22C8717',
                video:
                { duration: 16224.699999999999,
                    firstDTS: 1459270805911,
                    firstEncoderDTS: 83,
                    wrapEncoderDTS: 95443718 },
                path: '/var/tmp/media-u774d8hoj_w20128143_1.mp4',
                flavor: "32"
            };

            var bad = offsetFileInfo(fis,60000);
            //scramble firstDTS
            bad.startTime = bad.video.firstDTS = 0;

            var good = offsetFileInfo(fis,60000+bad.video.duration);

            updatePlaylist(plGen,[fis,bad,good]).then(function (result) {
                expect(result.durations.length).eql(2);
                expect(result.clipTimes[1]).to.be.within(good.video.firstDTS-1,good.video.firstDTS+1);
                done();
            }).catch(function (err) {
                done(err);
            });
        });
    });

});
<|endoftext|>


<commit_before>/*
 * FontLoader
 * Visit http://createjs.com/ for documentation, updates and examples.
 *
 *
 * Copyright (c) 2012 gskinner.com, inc.
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use,
 * copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following
 * conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 */

 /*

 Notes:
 getResult should probably just return true?
 defFromSrc
 defFromCSSSrc

 Param examples:
 [font list], "font"
 fontName, "font"?
 src
 css

100 - Thin
200 - Extra Light, Ultra Light
300 - Light, Semi Light, Demi Light
400 - Normal, Book, Regular
500 - Medium
600 - Semi Bold, Demi Bold
700 - Bold
800 - Extra Bold, Ultra Bold
900 - Black, Heavy
 From: http://www.w3.org/TR/css3-fonts/#font-weight-numeric-values
 With addition of book, regular, semi light, demi light

 http://jsfiddle.net/7aw9mkqn/

 Italic & Oblique are both translated to italic.
  */

/**
 * @module PreloadJS
 */

// namespace:
this.createjs = this.createjs || {};

(function () {
	"use strict";

// constructor:
	/**
	 * Description
	 * @class FontLoader
	 * @constructor
	 **/
	function FontLoader(loadItem, preferXHR) {
		this.AbstractLoader_constructor(loadItem, preferXHR, loadItem.type);

		// public properties:

		// private properties:
		this._faces = {};
		this._watched = [];
		this._count = 0;

		this._watchInterval = null;
		this._loadTimeout = null;
		this._injectCSS = (loadItem.injectCSS === undefined) ? true : loadItem.injectCSS;

		this.dispatchEvent("initialize");
	}
	var p = createjs.extend(FontLoader, createjs.AbstractLoader);

    /**
     * Determines if the loader can load a specific item. This loader can only load items that are of type
     * {{#crossLink "AbstractLoader/FONT:property"}}{{/crossLink}}.
     * @method canLoadItem
     * @param {LoadItem|Object} item The LoadItem that a LoadQueue is trying to load.
     * @returns {Boolean} Whether the loader can load the item.
     * @static
     */
    FontLoader.canLoadItem = function (item) {
        return item.type == createjs.AbstractLoader.FONT || item.type == createjs.AbstractLoader.FONTCSS;
    };

// static properties:
	FontLoader.sampleText = "abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ";
	FontLoader._ctx = document.createElement("canvas").getContext("2d");
	FontLoader._referenceFonts = ["serif","monospace"];

	FontLoader.WEIGHT_REGEX = /[- ._]*(thin|normal|book|regular|medium|black|heavy|[1-9]00|(?:extra|ultra|semi|demi)?[- ._]*(?:light|bold))[- ._]*/ig;
	FontLoader.STYLE_REGEX = /[- ._]*(italic|oblique)[- ._]*/ig;
	FontLoader.FONT_FORMAT = {"woff2":"woff2", woff:"woff", ttf:"truetype", otf:"truetype"};
	FontLoader.FONT_WEIGHT = {thin:100, extralight:200, ultralight:200, light:300, semilight:300, demilight:300, book:"normal", regular:"normal", semibold:600, demibold:600, extrabold:800, ultrabold:800, black:900, heavy:900};

// public methods:
	p.load = function() {
		if (this.type == createjs.AbstractLoader.FONTCSS) {
			var loaded = this._watchCSS();

			// If the CSS is not ready, it will create a request, which AbstractLoader can handle.
			if (!loaded) {
				this.AbstractLoader_load();
				return;
			}

		} else if (this._item.src instanceof Array) {
			this._watchFontArray();
		} else {
			var def = this._defFromSrc(this._item.src);
			this._watchFont(def);
			this._injectStyleTag(this._cssFromDef(def));
		}

		this._loadTimeout = setTimeout(createjs.proxy(this._handleTimeout, this), this._item.loadTimeout);

		this.dispatchEvent("loadstart");
	};

	p._handleTimeout = function () {
		this._stopWatching();
		this.dispatchEvent(new createjs.ErrorEvent("PRELOAD_TIMEOUT", null, event));
	};

	// WatchCSS does the work for us, and provides a modified src.
	p._createRequest = function() {
		return this._request;
	};

	p.handleEvent = function (event) {
		switch (event.type) {
			case "complete":
				clearTimeout(this._loadTimeout);
				this._rawResult = event.target._response;
				this._result = true;
				this._parseCSS(this._rawResult);
				return;
				break;
			case "error":
				clearTimeout(this._loadTimeout);
				this._stopWatching(); // Might not be necessary since fonts haven't started watching
				this.AbstractLoader_handleEvent(event);
				break;
		}
	};

// private methods:
	p._watchCSS = function() {
		var src = this._item.src;

		// An HTMLElement was passed in. Just use it.
		if (src instanceof HTMLStyleElement) {
			if (this._injectCSS && !src.parentNode) { (document.head || document.getElementsByTagName('head')[0]).appendChild(src); }
			this._injectCSS = false;
			src = "\n"+src.textContent;
		}

		// A CSS string was passed in. Parse and use it
		if (src.search(/\n|\r|@font-face/i) !== -1) { // css string.
			this._parseCSS(src);
			return true;
		}

		// Load a CSS Path. Note that we CAN NOT load it without XHR. LM: Why not?
		this._request = new createjs.XHRRequest(this._item);
		return false;
	};

	p._parseCSS = function(css) {
		var regex = /@font-face\s*\{([^}]+)}/g
		while (true) {
			var result = regex.exec(css);
			if (!result) { break; }
			this._watchFont(this._parseFontFace(result[1]));
		}
		this._injectStyleTag(css);
	};

	p._watchFontArray = function() {
		var arr = this._item.src, css = "", def;
		for (var i=arr.length-1; i>=0; i--) {
			var o = arr[i];
			if (typeof o === "string") { def = this._defFromSrc(o) }
			else { def = this._defFromObj(o); }
			this._watchFont(def);
			css += this._cssFromDef(def)+"\n";
		}
		this._injectStyleTag(css);
	};

	p._injectStyleTag = function(css) {
		//TODO: Maybe use an internal CSSLoader instance.
		if (!this._injectCSS) { return; }
		var head = document.head || document.getElementsByTagName('head')[0];
		var styleTag = document.createElement("style");
		styleTag.type = "text/css";
		if (styleTag.styleSheet){
			styleTag.styleSheet.cssText = css;
		} else {
			styleTag.appendChild(document.createTextNode(css));
		}
		head.appendChild(styleTag);
	};

	p._parseFontFace = function(str) {
		var family = this._getCSSValue(str, "font-family"), src = this._getCSSValue(str, "src");
		if (!family || !src) { return; }
		return this._defFromObj({
			family: family,
			src: src,
			style: this._getCSSValue(str, "font-style"),
			weight: this._getCSSValue(str, "font-weight")
		});
	};

	p._watchFont = function(def) {
		if (!def || this._faces[def.id]) { return; }
		this._faces[def.id] = def;
		this._watched.push(def);
		this._count++;

		this._calculateReferenceSizes(def);
		this._startWatching();
	};

	p._startWatching = function() {
		if (this._watchInterval != null) { return; }
		this._watchInterval = setInterval(createjs.proxy(this._watch, this), FontLoader.WATCH_DURATION);
	};

	p._stopWatching = function() {
		clearInterval(this._watchInterval);
		this._watchInterval = null;
	};

	p._watch = function() {
		var defs = this._watched, refFonts = FontLoader._referenceFonts, l = defs.length;
		for (var i = l - 1; i >= 0; i--) {
			var def = defs[i], refs = def.refs;
			for (var j = refs.length - 1; j >= 0; j--) {
				var w = this._getTextWidth(def.family + "," + refFonts[j], def.weight, def.style);
				if (w != refs[j]) {
					var event = new createjs.Event("fileload");
					event.item = def.family;
					this.dispatchEvent(event);
					defs.splice(i, 1);
					break;
				}
			}
		}
		if (l !== defs.length) {
			var event = new createjs.ProgressEvent(this._count-defs.length, this._count);
			this.dispatchEvent(event);
		}
		if (l === 0) {
			this._stopWatching();
			this._sendComplete();
		}
	};

	p._calculateReferenceSizes = function(def) {
		var refFonts = FontLoader._referenceFonts;
		var refs = def.refs = [];
		for (var i=0; i<refFonts.length; i++) {
			refs[i] = this._getTextWidth(refFonts[i], def.weight, def.style);
		}
	};

	p._defFromSrc = function(src) {
		var re = /[- ._]+/g, name = src, ext = null, index;

		index = name.search(/[?#]/);
		if (index !== -1) {
			name = name.substr(0,index);
		}
		index = name.lastIndexOf(".");
		if (index !== -1) {
			ext = name.substr(index+1);
			name = name.substr(0,index);
		}
		index = name.lastIndexOf("/");
		if (index !== -1) {
			name = name.substr(index+1);
		}

		var family=name;

		var weight = family.match(FontLoader.WEIGHT_REGEX);
		if (weight) {
			weight = weight[0];
			family = family.replace(weight, "");
			weight = weight.replace(re, "").toLowerCase();
		}
		var style = name.match(FontLoader.STYLE_REGEX);
		if (style) {
			family = family.replace(style[0], "");
			style = "italic";
		}
		family = family.replace(re, "");

		var cssSrc = "local('"+name.replace(re," ")+"'), url('"+src+"')";
		var format = FontLoader.FONT_FORMAT[ext];
		if (format) { cssSrc += " format('"+format+"')"; }

		return this._defFromObj({
			family: family,
			weight: FontLoader.FONT_WEIGHT[weight]||weight,
			style: style,
			src: cssSrc
		});
	};

	p._defFromObj = function(o) {
		var def = {
			family: o.family,
			src: o.src,
			style: o.style || "normal",
			weight: o.weight || "normal"
		};
		def.id = def.family + ";" + def.style + ";" + def.weight;
		return def;
	};

	p._cssFromDef = function(def) {
		return "@font-face {\n" +
			"\tfont-family: '"+def.family+"';\n" +
			"\tfont-style: "+def.style+";\n" +
			"\tfont-weight: "+def.weight+";\n" +
			"\tsrc: "+def.src+";\n" +
			"}";
	};

	p._getTextWidth = function(family, weight, style) {
		var ctx = FontLoader._ctx;
		ctx.font = style+" "+weight+" 72px "+family;
		return ctx.measureText(FontLoader.sampleText).width;
	};

	p._getCSSValue = function(str, value) {
		var regex = new RegExp(value+":\s*([^;}]+?)\s*[;}]");
		var result = regex.exec(str);
		if (!result || !result[1]) { return null; }
		return result[1];
	};

	createjs.FontLoader = createjs.promote(FontLoader, "AbstractLoader");
})();

<commit_msg>Added WATCH_DURATION to FontLoader. Missed in earlier commit.<commit_after>/*
 * FontLoader
 * Visit http://createjs.com/ for documentation, updates and examples.
 *
 *
 * Copyright (c) 2012 gskinner.com, inc.
 *
 * Permission is hereby granted, free of charge, to any person
 * obtaining a copy of this software and associated documentation
 * files (the "Software"), to deal in the Software without
 * restriction, including without limitation the rights to use,
 * copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following
 * conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 * OTHER DEALINGS IN THE SOFTWARE.
 */

 /*

 Notes:
 getResult should probably just return true?
 defFromSrc
 defFromCSSSrc

 Param examples:
 [font list], "font"
 fontName, "font"?
 src
 css

100 - Thin
200 - Extra Light, Ultra Light
300 - Light, Semi Light, Demi Light
400 - Normal, Book, Regular
500 - Medium
600 - Semi Bold, Demi Bold
700 - Bold
800 - Extra Bold, Ultra Bold
900 - Black, Heavy
 From: http://www.w3.org/TR/css3-fonts/#font-weight-numeric-values
 With addition of book, regular, semi light, demi light

 http://jsfiddle.net/7aw9mkqn/

 Italic & Oblique are both translated to italic.
  */

/**
 * @module PreloadJS
 */

// namespace:
this.createjs = this.createjs || {};

(function () {
	"use strict";

// constructor:
	/**
	 * Description
	 * @class FontLoader
	 * @constructor
	 **/
	function FontLoader(loadItem, preferXHR) {
		this.AbstractLoader_constructor(loadItem, preferXHR, loadItem.type);

		// public properties:

		// private properties:
		this._faces = {};
		this._watched = [];
		this._count = 0;

		this._watchInterval = null;
		this._loadTimeout = null;
		this._injectCSS = (loadItem.injectCSS === undefined) ? true : loadItem.injectCSS;

		this.dispatchEvent("initialize");
	}
	var p = createjs.extend(FontLoader, createjs.AbstractLoader);

    /**
     * Determines if the loader can load a specific item. This loader can only load items that are of type
     * {{#crossLink "AbstractLoader/FONT:property"}}{{/crossLink}}.
     * @method canLoadItem
     * @param {LoadItem|Object} item The LoadItem that a LoadQueue is trying to load.
     * @returns {Boolean} Whether the loader can load the item.
     * @static
     */
    FontLoader.canLoadItem = function (item) {
        return item.type == createjs.AbstractLoader.FONT || item.type == createjs.AbstractLoader.FONTCSS;
    };

// static properties:
	FontLoader.sampleText = "abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ";
	FontLoader._ctx = document.createElement("canvas").getContext("2d");
	FontLoader._referenceFonts = ["serif","monospace"];

	FontLoader.WEIGHT_REGEX = /[- ._]*(thin|normal|book|regular|medium|black|heavy|[1-9]00|(?:extra|ultra|semi|demi)?[- ._]*(?:light|bold))[- ._]*/ig;
	FontLoader.STYLE_REGEX = /[- ._]*(italic|oblique)[- ._]*/ig;
	FontLoader.FONT_FORMAT = {"woff2":"woff2", woff:"woff", ttf:"truetype", otf:"truetype"};
	FontLoader.FONT_WEIGHT = {thin:100, extralight:200, ultralight:200, light:300, semilight:300, demilight:300, book:"normal", regular:"normal", semibold:600, demibold:600, extrabold:800, ultrabold:800, black:900, heavy:900};

	FontLoader.WATCH_DURATION = 10;
// public methods:
	p.load = function() {
		if (this.type == createjs.AbstractLoader.FONTCSS) {
			var loaded = this._watchCSS();

			// If the CSS is not ready, it will create a request, which AbstractLoader can handle.
			if (!loaded) {
				this.AbstractLoader_load();
				return;
			}

		} else if (this._item.src instanceof Array) {
			this._watchFontArray();
		} else {
			var def = this._defFromSrc(this._item.src);
			this._watchFont(def);
			this._injectStyleTag(this._cssFromDef(def));
		}

		this._loadTimeout = setTimeout(createjs.proxy(this._handleTimeout, this), this._item.loadTimeout);

		this.dispatchEvent("loadstart");
	};

	p._handleTimeout = function () {
		this._stopWatching();
		this.dispatchEvent(new createjs.ErrorEvent("PRELOAD_TIMEOUT", null, event));
	};

	// WatchCSS does the work for us, and provides a modified src.
	p._createRequest = function() {
		return this._request;
	};

	p.handleEvent = function (event) {
		switch (event.type) {
			case "complete":
				clearTimeout(this._loadTimeout);
				this._rawResult = event.target._response;
				this._result = true;
				this._parseCSS(this._rawResult);
				return;
				break;
			case "error":
				clearTimeout(this._loadTimeout);
				this._stopWatching(); // Might not be necessary since fonts haven't started watching
				this.AbstractLoader_handleEvent(event);
				break;
		}
	};

// private methods:
	p._watchCSS = function() {
		var src = this._item.src;

		// An HTMLElement was passed in. Just use it.
		if (src instanceof HTMLStyleElement) {
			if (this._injectCSS && !src.parentNode) { (document.head || document.getElementsByTagName('head')[0]).appendChild(src); }
			this._injectCSS = false;
			src = "\n"+src.textContent;
		}

		// A CSS string was passed in. Parse and use it
		if (src.search(/\n|\r|@font-face/i) !== -1) { // css string.
			this._parseCSS(src);
			return true;
		}

		// Load a CSS Path. Note that we CAN NOT load it without XHR. LM: Why not?
		this._request = new createjs.XHRRequest(this._item);
		return false;
	};

	p._parseCSS = function(css) {
		var regex = /@font-face\s*\{([^}]+)}/g
		while (true) {
			var result = regex.exec(css);
			if (!result) { break; }
			this._watchFont(this._parseFontFace(result[1]));
		}
		this._injectStyleTag(css);
	};

	p._watchFontArray = function() {
		var arr = this._item.src, css = "", def;
		for (var i=arr.length-1; i>=0; i--) {
			var o = arr[i];
			if (typeof o === "string") { def = this._defFromSrc(o) }
			else { def = this._defFromObj(o); }
			this._watchFont(def);
			css += this._cssFromDef(def)+"\n";
		}
		this._injectStyleTag(css);
	};

	p._injectStyleTag = function(css) {
		//TODO: Maybe use an internal CSSLoader instance.
		if (!this._injectCSS) { return; }
		var head = document.head || document.getElementsByTagName('head')[0];
		var styleTag = document.createElement("style");
		styleTag.type = "text/css";
		if (styleTag.styleSheet){
			styleTag.styleSheet.cssText = css;
		} else {
			styleTag.appendChild(document.createTextNode(css));
		}
		head.appendChild(styleTag);
	};

	p._parseFontFace = function(str) {
		var family = this._getCSSValue(str, "font-family"), src = this._getCSSValue(str, "src");
		if (!family || !src) { return null; }
		return this._defFromObj({
			family: family,
			src: src,
			style: this._getCSSValue(str, "font-style"),
			weight: this._getCSSValue(str, "font-weight")
		});
	};

	p._watchFont = function(def) {
		if (!def || this._faces[def.id]) { return; }
		this._faces[def.id] = def;
		this._watched.push(def);
		this._count++;

		this._calculateReferenceSizes(def);
		this._startWatching();
	};

	p._startWatching = function() {
		if (this._watchInterval != null) { return; }
		this._watchInterval = setInterval(createjs.proxy(this._watch, this), FontLoader.WATCH_DURATION);
	};

	p._stopWatching = function() {
		clearInterval(this._watchInterval);
		this._watchInterval = null;
	};

	p._watch = function() {
		var defs = this._watched, refFonts = FontLoader._referenceFonts, l = defs.length;
		for (var i = l - 1; i >= 0; i--) {
			var def = defs[i], refs = def.refs;
			for (var j = refs.length - 1; j >= 0; j--) {
				var w = this._getTextWidth(def.family + "," + refFonts[j], def.weight, def.style);
				if (w != refs[j]) {
					var event = new createjs.Event("fileload");
					event.item = def.family;
					this.dispatchEvent(event);
					defs.splice(i, 1);
					break;
				}
			}
		}
		if (l !== defs.length) {
			var event = new createjs.ProgressEvent(this._count-defs.length, this._count);
			this.dispatchEvent(event);
		}
		if (l === 0) {
			this._stopWatching();
			this._sendComplete();
		}
	};

	p._calculateReferenceSizes = function(def) {
		var refFonts = FontLoader._referenceFonts;
		var refs = def.refs = [];
		for (var i=0; i<refFonts.length; i++) {
			refs[i] = this._getTextWidth(refFonts[i], def.weight, def.style);
		}
	};

	p._defFromSrc = function(src) {
		var re = /[- ._]+/g, name = src, ext = null, index;

		index = name.search(/[?#]/);
		if (index !== -1) {
			name = name.substr(0,index);
		}
		index = name.lastIndexOf(".");
		if (index !== -1) {
			ext = name.substr(index+1);
			name = name.substr(0,index);
		}
		index = name.lastIndexOf("/");
		if (index !== -1) {
			name = name.substr(index+1);
		}

		var family=name;

		var weight = family.match(FontLoader.WEIGHT_REGEX);
		if (weight) {
			weight = weight[0];
			family = family.replace(weight, "");
			weight = weight.replace(re, "").toLowerCase();
		}
		var style = name.match(FontLoader.STYLE_REGEX);
		if (style) {
			family = family.replace(style[0], "");
			style = "italic";
		}
		family = family.replace(re, "");

		var cssSrc = "local('"+name.replace(re," ")+"'), url('"+src+"')";
		var format = FontLoader.FONT_FORMAT[ext];
		if (format) { cssSrc += " format('"+format+"')"; }

		return this._defFromObj({
			family: family,
			weight: FontLoader.FONT_WEIGHT[weight]||weight,
			style: style,
			src: cssSrc
		});
	};

	p._defFromObj = function(o) {
		var def = {
			family: o.family,
			src: o.src,
			style: o.style || "normal",
			weight: o.weight || "normal"
		};
		def.id = def.family + ";" + def.style + ";" + def.weight;
		return def;
	};

	p._cssFromDef = function(def) {
		return "@font-face {\n" +
			"\tfont-family: '"+def.family+"';\n" +
			"\tfont-style: "+def.style+";\n" +
			"\tfont-weight: "+def.weight+";\n" +
			"\tsrc: "+def.src+";\n" +
			"}";
	};

	p._getTextWidth = function(family, weight, style) {
		var ctx = FontLoader._ctx;
		ctx.font = style+" "+weight+" 72px "+family;
		return ctx.measureText(FontLoader.sampleText).width;
	};

	p._getCSSValue = function(str, value) {
		var regex = new RegExp(value+":\s*([^;}]+?)\s*[;}]");
		var result = regex.exec(str);
		if (!result || !result[1]) { return null; }
		return result[1];
	};

	createjs.FontLoader = createjs.promote(FontLoader, "AbstractLoader");
})();

<|endoftext|>


<commit_before>var BaseService = require('../libs/baseService.js');
var Article = require('../models/article.js');
var UserArticle = require('../models/userArticle.js');
var utils = require('../libs/utils.js');
var _ = require('underscore');
var readability = require('node-readability');
var moment = require('moment');
var toMarkdown = require('to-markdown').toMarkdown;
var URL = require('url');
var service = new BaseService();

function getFavIcon(document, urlObj) {
    var favicon = false;
    var nodeList = document.getElementsByTagName("link");
    for (var i = 0; i < nodeList.length; i++) {
        if ((nodeList[i].getAttribute("rel") == "icon") || (nodeList[i].getAttribute("rel") == "shortcut icon")) {
            favicon = nodeList[i].getAttribute("href");
        }
    }
    if (favicon) {
        if (favicon.indexOf('http') === 0) {

        } else {
            if (favicon.indexOf('/') === 0) {
                favicon = urlObj.protocol + '//' + urlObj.hostname + favicon;
            } else {
                favicon = urlObj.protocol + '//' + urlObj.hostname + '/' + favicon;
            }

        }
    } else {
        favicon = '';
    }
    return favicon;
}

//添加文章
service.add = function (req, res, next) {
    var url = req.body.url;
    var urlObj = URL.parse(url);
    var user_id = req.session.user_id;
    var url_md5 = utils.md5(url);
    var favicon = req.body.favicon || '';
    var html = req.body.html || '';
    var hostname = req.body.hostname;
    var title = req.body.title;
    Article.schema.findOne({url_md5: url_md5})
        .select("_id")
        .exec(function (error, result) {
            if (error) {
                service.restError(res, next, -1, 'db_error');
            } else {
                if (result) {
                    UserArticle.insertIfNoExist(user_id, result._id, function (err) {
                        if (err) {
                            service.restError(res, -1, err);
                            return;
                        }
                        service.restSuccess(res);
                    });
                } else {
                    if (html !== '') {
                        var newArticle = {
                            hostname: hostname,
                            favico: favicon,
                            title: title,
                            content: html,
                        };
                        addArticleToDB(newArticle, url, url_md5, user_id, function (err) {
                            if (err) {
                                console.log(err);
                                service.restError(res, -1, err);
                                return;
                            }
                            service.restSuccess(res);
                        });
                    } else {
                        readability(url, {
                            headers: {
                                'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',
                                'Accept-Language': 'en-US,en;q=0.8,zh-CN;q=0.6,zh-TW;q=0.4',
                                'Cache-Control': 'max-age=0',
                                'Connection': 'keep-alive',
                                'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36'
                            }
                        }, function (err, article) {
                            article.hostname = hostname;
                            if (favicon === '') {
                                favicon = getFavIcon(article.document, urlObj);
                            }
                            article.favico = favicon;
                            addArticleToDB(article, url, url_md5, user_id, function (err) {
                                article.close();
                                if (err) {
                                    console.log(err);
                                    service.restError(res, -1, err);
                                    return;
                                }
                                service.restSuccess(res);
                            });
                        });
                    }
                }
            }
        });
};

//删除文章
service.del = function (req, res, next) {
    var user_id = req.session.user_id;
    var article_id = req.body.article_id;
    UserArticle.del(user_id, article_id, function (err) {
        if (err) {
            service.restError(res, -1, err);
            return;
        }
        service.restSuccess(res);
    });
};

//获取文章markdown文件
service.createMarkdown = function (req, res, next) {
    var article_id = req.params.articleId;
    Article.schema.findById(article_id, function (error, result) {
        if (error) {
            res.send('操作失败');
            return;
        }
        var content = result.content.toString();
        content = content.replace(/<span>/ig, "");
        content = content.replace(/<\/span>/ig, "");
        content = content.replace(/\n/ig, "");
        var mkContent = toMarkdown(content);

        mkContent = '转载自：[' + result.title + '](' + result.url + ') \n\n' + mkContent;
        res.writeHeader(200, {
            "Content-Type": "application/x-msdownload",
            "Content-Disposition": "attachment;filename=article.md"
        });
        res.write(mkContent);
        res.end();
    });
};

//获取readList
service.getList = function (req, res, next) {
    var page = req.params.page ? req.params.page : 1;
    page--;
    var limit = 50;
    var skip = 50 * page;
    Article.schema.find({title: {"$ne": ""}, content: {"$ne": ""}})
        .skip(skip)
        .limit(limit)
        .sort({created: -1})
        .exec(function (error, result) {
            if (error) {
                service.restError(res, next, -1, 'db_error');
                return;
            }
            _.each(result, function (item) {
                if (item.title.length > 30) {
                    item.title = utils.subString(item.title, 30, true);
                }
                item.content = utils.delHtmlTag(item.content);
                item.content = item.content.substr(0, 300);
                item.content = utils.delBlank(item.content);
                if (item.content.length > 100) {
                    item.content = utils.subString(item.content, 100, true);
                }
            });
            service.restSuccess(res, result);
        });
};

function getList(articleIds, res, next) {
    Article.schema.find({_id: {"$in": articleIds}})
        .sort({created: -1})
        .select("_id title content created")
        .exec(function (error, result) {
            if (error) {
                service.restError(res, next, -1, 'db_error');
                return;
            }
            _.each(result, function (item) {
                if (item.title.length > 30) {
                    item.title = utils.subString(item.title, 30, true);
                }
                item.content = utils.delHtmlTag(item.content);
                item.content = item.content.substr(0, 300);
                item.content = utils.delBlank(item.content);
                if (item.content.length > 100) {
                    item.content = utils.subString(item.content, 100, true);
                }
            });
            service.restSuccess(res, result);
        });
}

function addArticleToDB(article, url, url_md5, user_id, next) {
    if (article && article.title && article.content) {
        var data = new Article.schema({
            user_id: user_id,
            title: article.title,
            content: article.content,
            url: url,
            url_md5: url_md5,
            from_site: article.hostname,
            site_ico: article.favico,
            created: moment().format('YYYY-MM-DD HH:mm:ss')
        });
        data.save(function (err) {
            if (err) {
                next('db_error');
                return;
            }
            UserArticle.insertIfNoExist(user_id, data._id.toString(), function (err) {
                if (err) {
                    next(err);
                    return;
                }
                next(false, data._id.toString());
            });
        });
    } else {
        next('readability_error');
    }
}

module.exports = service;
module.exports.addArticleToDB = addArticleToDB;<commit_msg>update dependencies<commit_after>var BaseService = require('../libs/baseService.js');
var Article = require('../models/article.js');
var UserArticle = require('../models/userArticle.js');
var utils = require('../libs/utils.js');
var _ = require('underscore');
var readability = require('node-readability');
var moment = require('moment');
var toMarkdown = require('to-markdown').toMarkdown;
var URL = require('url');
var service = new BaseService();

function getFavIcon(document, urlObj) {
    var favicon = false;
    var nodeList = document.getElementsByTagName("link");
    for (var i = 0; i < nodeList.length; i++) {
        if ((nodeList[i].getAttribute("rel") == "icon") || (nodeList[i].getAttribute("rel") == "shortcut icon")) {
            favicon = nodeList[i].getAttribute("href");
        }
    }
    if (favicon) {
        if (favicon.indexOf('http') === 0) {

        } else {
            if (favicon.indexOf('/') === 0) {
                favicon = urlObj.protocol + '//' + urlObj.hostname + favicon;
            } else {
                favicon = urlObj.protocol + '//' + urlObj.hostname + '/' + favicon;
            }

        }
    } else {
        favicon = '';
    }
    return favicon;
}

//添加文章
service.add = function (req, res, next) {
    var url = req.body.url;
    var urlObj = URL.parse(url);
    var user_id = req.session.user_id;
    var url_md5 = utils.md5(url);
    var favicon = req.body.favicon || '';
    var html = req.body.html || '';
    var hostname = req.body.hostname;
    var title = req.body.title;
    Article.schema.findOne({url_md5: url_md5})
        .select("_id")
        .exec(function (error, result) {
            if (error) {
                service.restError(res, next, -1, 'db_error');
            } else {
                if (result) {
                    UserArticle.insertIfNoExist(user_id, result._id, function (err) {
                        if (err) {
                            service.restError(res, -1, err);
                            return;
                        }
                        service.restSuccess(res);
                    });
                } else {
                    if (html !== '') {
                        var newArticle = {
                            hostname: hostname,
                            favico: favicon,
                            title: title,
                            content: html,
                        };
                        addArticleToDB(newArticle, url, url_md5, user_id, function (err) {
                            if (err) {
                                console.log(err);
                                service.restError(res, -1, err);
                                return;
                            }
                            service.restSuccess(res);
                        });
                    } else {
                        readability(url, {
                            headers: {
                                'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',
                                'Accept-Language': 'en-US,en;q=0.8,zh-CN;q=0.6,zh-TW;q=0.4',
                                'Cache-Control': 'max-age=0',
                                'Connection': 'keep-alive',
                                'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36'
                            },
                            imgCrossDomainConfig:{
                                imgUrlPrefix:'/images/grabed/',
                                imgStorePath:'public/images/grabed/'
                            }
                        }, function (err, article) {
                            article.hostname = hostname;
                            if (favicon === '') {
                                favicon = getFavIcon(article.document, urlObj);
                            }
                            article.favico = favicon;
                            addArticleToDB(article, url, url_md5, user_id, function (err) {
                                article.close();
                                if (err) {
                                    console.log(err);
                                    service.restError(res, -1, err);
                                    return;
                                }
                                service.restSuccess(res);
                            });
                        });
                    }
                }
            }
        });
};

//删除文章
service.del = function (req, res, next) {
    var user_id = req.session.user_id;
    var article_id = req.body.article_id;
    UserArticle.del(user_id, article_id, function (err) {
        if (err) {
            service.restError(res, -1, err);
            return;
        }
        service.restSuccess(res);
    });
};

//获取文章markdown文件
service.createMarkdown = function (req, res, next) {
    var article_id = req.params.articleId;
    Article.schema.findById(article_id, function (error, result) {
        if (error) {
            res.send('操作失败');
            return;
        }
        var content = result.content.toString();
        content = content.replace(/<span>/ig, "");
        content = content.replace(/<\/span>/ig, "");
        content = content.replace(/\n/ig, "");
        var mkContent = toMarkdown(content);

        mkContent = '转载自：[' + result.title + '](' + result.url + ') \n\n' + mkContent;
        res.writeHeader(200, {
            "Content-Type": "application/x-msdownload",
            "Content-Disposition": "attachment;filename=article.md"
        });
        res.write(mkContent);
        res.end();
    });
};

//获取readList
service.getList = function (req, res, next) {
    var page = req.params.page ? req.params.page : 1;
    page--;
    var limit = 50;
    var skip = 50 * page;
    Article.schema.find({title: {"$ne": ""}, content: {"$ne": ""}})
        .skip(skip)
        .limit(limit)
        .sort({created: -1})
        .exec(function (error, result) {
            if (error) {
                service.restError(res, next, -1, 'db_error');
                return;
            }
            _.each(result, function (item) {
                if (item.title.length > 30) {
                    item.title = utils.subString(item.title, 30, true);
                }
                item.content = utils.delHtmlTag(item.content);
                item.content = item.content.substr(0, 300);
                item.content = utils.delBlank(item.content);
                if (item.content.length > 100) {
                    item.content = utils.subString(item.content, 100, true);
                }
            });
            service.restSuccess(res, result);
        });
};

function getList(articleIds, res, next) {
    Article.schema.find({_id: {"$in": articleIds}})
        .sort({created: -1})
        .select("_id title content created")
        .exec(function (error, result) {
            if (error) {
                service.restError(res, next, -1, 'db_error');
                return;
            }
            _.each(result, function (item) {
                if (item.title.length > 30) {
                    item.title = utils.subString(item.title, 30, true);
                }
                item.content = utils.delHtmlTag(item.content);
                item.content = item.content.substr(0, 300);
                item.content = utils.delBlank(item.content);
                if (item.content.length > 100) {
                    item.content = utils.subString(item.content, 100, true);
                }
            });
            service.restSuccess(res, result);
        });
}

function addArticleToDB(article, url, url_md5, user_id, next) {
    if (article && article.title && article.content) {
        var data = new Article.schema({
            user_id: user_id,
            title: article.title,
            content: article.content,
            url: url,
            url_md5: url_md5,
            from_site: article.hostname,
            site_ico: article.favico,
            created: moment().format('YYYY-MM-DD HH:mm:ss')
        });
        data.save(function (err) {
            if (err) {
                next('db_error');
                return;
            }
            UserArticle.insertIfNoExist(user_id, data._id.toString(), function (err) {
                if (err) {
                    next(err);
                    return;
                }
                next(false, data._id.toString());
            });
        });
    } else {
        next('readability_error');
    }
}

module.exports = service;
module.exports.addArticleToDB = addArticleToDB;<|endoftext|>


<commit_before>(function(){

  module.exports = {
    'ClearMaskPass': './clear-mask',
    'MaskPass': './mask',
    'RenderPass': './render',
    'ShaderPass': './shader',
    'ShaderRenderPass': './shader-render'
  };

})();<commit_msg>Derrrrrrrp.<commit_after>(function(){

  module.exports = {
    'ClearMaskPass': require('./clear-mask'),
    'MaskPass': require('./mask'),
    'RenderPass': require('./render'),
    'ShaderPass': require('./shader'),
    'ShaderRenderPass': require('./shader-render')
  };

})();<|endoftext|>


<commit_before>var Promise = typeof(global.Promise) === "undefined" ? null : global.Promise;

if (!Promise) {
    var utils = require("utils"),
        slice = Array.prototype.slice;

    var Handler = function(onFulfilled, onRejected, resolve, reject) {
        this.onFulfilled = utils.isFunction(onFulfilled) ? onFulfilled : null;
        this.onRejected = utils.isFunction(onRejected) ? onRejected : null;
        this.resolve = resolve;
        this.reject = reject;
    }

    var handleResolve = function(resolver, onFulfilled, onRejected) {
        var done = false;

        try {
            resolver(
                function(value) {
                    if (done) return;
                    done = true;
                    onFulfilled(value);
                },
                function(reason) {
                    if (done) return;
                    done = true;
                    onRejected(reason);
                }
            );
        } catch (e) {
            if (done) return
            done = true
            onRejected(e);
        }
    }

    var resolveValue = function(promise, newValue) {

        try {
            if (newValue === promise) throw new TypeError("A promise cannot be resolved with itself");

            if (newValue && (utils.isObject(newValue) || utils.isFunction(newValue))) {
                if (utils.isFunction(newValue.then)) {
                    handleResolve(
                        function resolver(resolve, reject) {
                            newValue.then(resolve, reject);
                        },
                        function resolve(newValue) {
                            resolveValue(_this, newValue);
                        },
                        function reject(newValue) {
                            rejectValue(_this, newValue);
                        }
                    );
                    return;
                }
            }
            promise._state = true;
            promise._value = newValue;
            finale(promise);
        } catch (e) {
            rejectValue(promise, e);
        }
    }

    var rejectValue = function(promise, newValue) {
        promise._state = false;
        promise._value = newValue;
        finale(promise);
    }

    var finale = function(promise) {
        var handlers = promise._handlers,
            i = 0,
            il = handlers.length;

        for (; i < il; i++) handle(promise, handlers[i]);
        handlers.length = 0;
    }

    var handle = function(promise, handler) {
        var state = promise._state;

        if (promise._state === null) {
            promise._handlers.push(handler);
            return;
        }

        process.nextTick(function nextTick() {
            var callback = state ? handler.onFulfilled : handler.onRejected,
                value = promise._value,
                out;

            if (callback === null) {
                (state ? handler.resolve : handler.reject)(value);
                return;
            }

            try {
                out = callback(value);
            } catch (e) {
                handler.reject(e);
                return;
            }

            handler.resolve(out);
        });
    }

    Promise = function Promise(resolver) {
        var _this = this;

        if (!(this instanceof Promise)) {
            throw new TypeError("Promise(resolver) \"this\" must be an instance of of Promise");
        }
        if (!utils.isFunction(resolver)) {
            throw new TypeError("Promise(resolver) You must pass a resolver function as the first argument to the promise constructor");
        }

        this._state = null;
        this._value = null;
        this._handlers = [];

        handleResolve(
            resolver,
            function resolve(newValue) {
                resolveValue(_this, newValue);
            },
            function reject(newValue) {
                rejectValue(_this, newValue);
            }
        );
    }

    Promise.prototype.then = function(onFulfilled, onRejected) {
        var _this = this;

        return new Promise(function resolver(resolve, reject) {
            handle(_this, new Handler(onFulfilled, onRejected, resolve, reject));
        })
    };

    Promise.prototype["catch"] = function(onRejected) {

        return this.then(null, onRejected);
    };

    Promise.resolve = function(value) {
        if (value instanceof Promise) return value;

        return new Promise(function resolver(resolve) {
            resolve(value);
        });
    };

    Promise.reject = function(value) {
        return new Promise(function resolver(resolve, reject) {
            reject(value);
        });
    };

    Promise.all = function(value) {
        var args = (arguments.length === 1 && utils.isArray(value)) ? value : slice.call(arguments);

        return new Promise(function resolver(resolve, reject) {
            var i = 0,
                il = args.length,
                remaining = il,
                value, then;

            if (remaining === 0) {
                resolve([]);
                return;
            }

            function resolveValue(index, value) {
                try {
                    if (value && (utils.isObject(value) || utils.isFunction(value))) {

                        if (utils.isFunction(value.then)) {
                            value.then(function(value) {
                                resolveValue(index, value);
                            }, reject);
                            return;
                        }
                    }
                    if (--remaining === 0) {
                        resolve(args);
                    }
                } catch (e) {
                    reject(e);
                }
            }

            for (; i < il; i++) resolveValue(i, args[i]);
        });
    };

    Promise.race = function(values) {
        return new Promise(function resolver(resolve, reject) {
            var i = 0,
                il = values.length;

            for (; i < il; i++) {
                values[i].then(resolve, reject);
            }
        });
    };
};

module.exports = Promise;
<commit_msg>updates<commit_after>module.exports = typeof(global.Promise) !== "undefined" ? global.Promise : (function() {
    var utils = require("utils"),
        slice = Array.prototype.slice;

    function Handler(onFulfilled, onRejected, resolve, reject) {
        this.onFulfilled = utils.isFunction(onFulfilled) ? onFulfilled : null;
        this.onRejected = utils.isFunction(onRejected) ? onRejected : null;
        this.resolve = resolve;
        this.reject = reject;
    }

    function handleResolve(resolver, onFulfilled, onRejected) {
        var done = false;

        try {
            resolver(
                function(value) {
                    if (done) return;
                    done = true;
                    onFulfilled(value);
                },
                function(reason) {
                    if (done) return;
                    done = true;
                    onRejected(reason);
                }
            );
        } catch (e) {
            if (done) return
            done = true
            onRejected(e);
        }
    }

    function resolveValue(promise, newValue) {

        try {
            if (newValue === promise) throw new TypeError("A promise cannot be resolved with itself");

            if (newValue && (utils.isObject(newValue) || utils.isFunction(newValue))) {
                if (utils.isFunction(newValue.then)) {
                    handleResolve(
                        function resolver(resolve, reject) {
                            newValue.then(resolve, reject);
                        },
                        function resolve(newValue) {
                            resolveValue(_this, newValue);
                        },
                        function reject(newValue) {
                            rejectValue(_this, newValue);
                        }
                    );
                    return;
                }
            }
            promise._state = true;
            promise._value = newValue;
            finale(promise);
        } catch (e) {
            rejectValue(promise, e);
        }
    }

    function rejectValue(promise, newValue) {
        promise._state = false;
        promise._value = newValue;
        finale(promise);
    }

    function finale(promise) {
        var handlers = promise._handlers,
            i = 0,
            il = handlers.length;

        for (; i < il; i++) handle(promise, handlers[i]);
        handlers.length = 0;
    }

    function handle(promise, handler) {
        var state = promise._state;

        if (promise._state === null) {
            promise._handlers.push(handler);
            return;
        }

        process.nextTick(function nextTick() {
            var callback = state ? handler.onFulfilled : handler.onRejected,
                value = promise._value,
                out;

            if (callback === null) {
                (state ? handler.resolve : handler.reject)(value);
                return;
            }

            try {
                out = callback(value);
            } catch (e) {
                handler.reject(e);
                return;
            }

            handler.resolve(out);
        });
    }

    function Promise(resolver) {
        var _this = this;

        if (!(this instanceof Promise)) {
            throw new TypeError("Promise(resolver) \"this\" must be an instance of of Promise");
        }
        if (!utils.isFunction(resolver)) {
            throw new TypeError("Promise(resolver) You must pass a resolver function as the first argument to the promise constructor");
        }

        this._state = null;
        this._value = null;
        this._handlers = [];

        handleResolve(
            resolver,
            function resolve(newValue) {
                resolveValue(_this, newValue);
            },
            function reject(newValue) {
                rejectValue(_this, newValue);
            }
        );
    }

    Promise.prototype.then = function(onFulfilled, onRejected) {
        var _this = this;

        return new Promise(function resolver(resolve, reject) {
            handle(_this, new Handler(onFulfilled, onRejected, resolve, reject));
        })
    };

    Promise.prototype["catch"] = function(onRejected) {

        return this.then(null, onRejected);
    };

    Promise.resolve = function(value) {
        if (value instanceof Promise) return value;

        return new Promise(function resolver(resolve) {
            resolve(value);
        });
    };

    Promise.reject = function(value) {
        return new Promise(function resolver(resolve, reject) {
            reject(value);
        });
    };

    Promise.all = function(value) {
        var args = (arguments.length === 1 && utils.isArray(value)) ? value : slice.call(arguments);

        return new Promise(function resolver(resolve, reject) {
            var i = 0,
                il = args.length,
                remaining = il,
                value, then;

            if (remaining === 0) {
                resolve([]);
                return;
            }

            function resolveValue(index, value) {
                try {
                    if (value && (utils.isObject(value) || utils.isFunction(value))) {

                        if (utils.isFunction(value.then)) {
                            value.then(function(value) {
                                resolveValue(index, value);
                            }, reject);
                            return;
                        }
                    }
                    if (--remaining === 0) {
                        resolve(args);
                    }
                } catch (e) {
                    reject(e);
                }
            }

            for (; i < il; i++) resolveValue(i, args[i]);
        });
    };

    Promise.race = function(values) {
        return new Promise(function resolver(resolve, reject) {
            var i = 0,
                il = values.length;

            for (; i < il; i++) {
                values[i].then(resolve, reject);
            }
        });
    };

    return Promise;
}());
<|endoftext|>


<commit_before>'use strict';

angular.module('myApp.route-planner', ['ngRoute'])

    .config(['$routeProvider', function ($routeProvider) {
        $routeProvider.when('/route-planner', {
            templateUrl: 'route-planner/route-planner.html',
            controller: 'RoutePlannerCtrl'
        });
    }])

    .controller('RoutePlannerCtrl', ['$scope', 'GoogleMapApi'.ns(), function ($scope, GoogleMapApi) {

        $scope.locations = [
            "Orem, UT",
            "Springville, UT",
            "Payson, UT"
        ];

        $scope.waypoints = [];

//        $scope.input = document.getElementById('searchTextField');
//        $scope.autoComplete = new google.maps.places.Autocomplete(input);
//        var markerHandler = function() {
//            $scope.maps.event.addListener($scope.map.marker, 'click', function () {
//                var place = $scope.maps.marker.getPosition();
//                $scope.selected = place.latLng;
//            });
//        }
        $scope.addWaypoint = function (placeString) {

            var numString = placeString.replace(/[()]/g,'');
            var splitLocation = numString.split(",");
            var floatLat = parseFloat(splitLocation[0]);
            var floatLong = parseFloat(splitLocation[1]);
//            var locationObject = $scope.maps.Geocoder(placeString);
            var locationObject = new $scope.maps.LatLng(floatLat, floatLong);
            var location = {location: locationObject};
            $scope.waypoints.push(location);
            $scope.calcRoute();
        };

        $scope.deleteWaypoint = function (waypoint) {

            var index = $scope.waypoints.indexOf(waypoint);
            if (index > -1) {
                $scope.waypoints.splice(index, 1);
                $scope.calcRoute()
            }
        };

        $scope.BankControl = function (controlDiv, map) {

            controlDiv.style.padding = '5px';

            var controlUI = document.createElement('div');
            controlUI.style.backgroundColor = 'white';
            controlUI.style.borderStyle = 'solid';
            controlUI.style.borderWidth = '2px';
            controlUI.style.cursor = 'pointer';
            controlUI.style.textAlign = 'center';
            controlUI.title = 'Click to find banks nearby';
            controlDiv.appendChild(controlUI);

            var controlText = document.createElement('div');
            controlText.style.fontFamily = 'Arial,sans-serif';
            controlText.style.fontSize = '12px';
            controlText.style.paddingLeft = '4px';
            controlText.style.paddingRight = '4px';
            controlText.innerHTML = 'Bank';
            controlUI.appendChild(controlText);

            $scope.maps.event.addDomListener(controlUI, 'click', function () {
                var infowindow;

                var newYork = new $scope.maps.LatLng(40.69847032728747, -73.9514422416687);
                var center = $scope.map.getCenter();
                var request = {

                    location: center,
                    radius: 50000,
                    types: ['bank']
                };
                $scope.infowindow = new $scope.maps.InfoWindow();
                var service = new $scope.maps.places.PlacesService($scope.map);
                service.nearbySearch(request, $scope.callback);

            });

            $scope.callback = function (results, status) {
                if (status == $scope.maps.places.PlacesServiceStatus.OK) {
                    for (var i = 0; i < results.length; i++) {
                        $scope.createMarker(results[i]);
                    }
                }
            };

            $scope.createMarker = function (place) {
                var placeLoc = place.geometry.location;
                var marker = new $scope.maps.Marker({
                    map: $scope.map,
                    position: place.geometry.location
                });

                var contentString =
                    place.name +
                    '
' +
                    '
' +
                    '
' +
                    '
' +
                    '<button onclick="$(\'#bodyContent\').scope().addWaypoint(\'' + place.geometry.location + '\'); console.log(\'You clicked\')">Add</button>' +
                    '
' +
                    '
';

                $scope.maps.event.addListener(marker, 'click', function () {
                    $scope.infowindow.setContent(contentString);
                    $scope.infowindow.open($scope.map, this);

                });
            }

        };

        GoogleMapApi.then(function (maps) {
            $scope.maps = maps;
            console.log(maps);
            $scope.initialize();
        });

        $scope.initialize = function () {

//            markerHandler()
            $scope.travelMode = $scope.maps.TravelMode.DRIVING;
            var newYork = new $scope.maps.LatLng(40.69847032728747, -73.9514422416687);
            $scope.start = newYork;
            $scope.end = newYork;
            $scope.rendererOptions = {
                draggable: true
            };
            $scope.directionsDisplay = new $scope.maps.DirectionsRenderer($scope.rendererOptions);
            $scope.directionsService = new $scope.maps.DirectionsService();

            $scope.travelModes = [
                {
                    name: "driving",
                    value: $scope.maps.TravelMode.DRIVING
                },
                {
                    name: "walking",
                    value: $scope.maps.TravelMode.WALKING
                },
                {
                    name: "bicycling",
                    value: $scope.maps.TravelMode.BICYCLING
                },
                {
                    name: "transit",
                    value: $scope.maps.TravelMode.TRANSIT
                }

            ];

            $scope.directionsDisplay = new $scope.maps.DirectionsRenderer($scope.rendererOptions);
//            $scope.chicago = new $scope.maps.LatLng(41.850033, -87.6500523);
            var mapOptions = {
                zoom: 7
            };
            $scope.map = new $scope.maps.Map(document.getElementById('map-canvas'), mapOptions);

            $scope.directionsDisplay.setMap($scope.map);
            $scope.directionsDisplay.setPanel(document.getElementById("directionsPanel"));
            $scope.maps.event.addListener($scope.directionsDisplay, 'directions_changed', function () {
                $scope.computeTotalDistance($scope.directionsDisplay.getDirections());

            });

            $scope.bankControlDiv;

            $scope.bankControlDiv = document.createElement('div');
            $scope.bankControl = new $scope.BankControl($scope.bankControlDiv, $scope.map);
            $scope.bankControlDiv.index = 1;
            $scope.map.controls[$scope.maps.ControlPosition.TOP_RIGHT].push($scope.bankControlDiv);

            $scope.calcRoute()
        };

        $scope.calcRoute = function () {
            var request = {
                origin: $scope.start,
                destination: $scope.end,
//                travelMode: $scope.maps.TravelMode.DRIVING
                waypoints: $scope.waypoints,
                optimizeWaypoints: true,
                travelMode: $scope.travelMode
            };
            $scope.directionsService.route(request, function (response, status) {
                if (status == $scope.maps.DirectionsStatus.OK) {
                    $scope.directionsDisplay.setDirections(response);
                    $scope.directionsDisplay.setPanel(document.getElementById("directionsPanel"));
                }
            });

            $scope.maps.event.trigger($scope.map, 'resize');
        };

        $scope.computeTotalDistance = function (result) {
            var total = 0;
            var myroute = result.routes[0];
            for (var i = 0; i < myroute.legs.length; i++) {
                total += myroute.legs[i].distance.value;
            }
            total = total / 1000.0;
            document.getElementById('total').innerHTML = total + 'km';
        }

    }]);

<commit_msg>route-planner.js changes<commit_after>'use strict';

angular.module('myApp.route-planner', ['ngRoute'])

    .config(['$routeProvider', function ($routeProvider) {
        $routeProvider.when('/route-planner', {
            templateUrl: 'route-planner/route-planner.html',
            controller: 'RoutePlannerCtrl'
        });
    }])

    .controller('RoutePlannerCtrl', ['$scope', 'GoogleMapApi'.ns(), function ($scope, GoogleMapApi) {

        $scope.locations = [
            "Orem, UT",
            "Springville, UT",
            "Payson, UT"
        ];

        $scope.waypoints = [];

//        $scope.input = document.getElementById('searchTextField');
//        $scope.autoComplete = new google.maps.places.Autocomplete(input);
//        var markerHandler = function() {
//            $scope.maps.event.addListener($scope.map.marker, 'click', function () {
//                var place = $scope.maps.marker.getPosition();
//                $scope.selected = place.latLng;
//            });
//        }
        $scope.addWaypoint = function (placeString) {

            var numString = placeString.replace(/[()]/g,'');
            var splitLocation = numString.split(",");
            var floatLat = parseFloat(splitLocation[0]);
            var floatLong = parseFloat(splitLocation[1]);
//            var locationObject = $scope.maps.Geocoder(placeString);
            var locationObject = new $scope.maps.LatLng(floatLat, floatLong);
            var location = {location: locationObject};
            $scope.waypoints.push(location);
            $scope.calcRoute();
        };

        $scope.deleteWaypoint = function (waypoint) {

            var index = $scope.waypoints.indexOf(waypoint);
            if (index > -1) {
                $scope.waypoints.splice(index, 1);
                $scope.calcRoute()
            }
        };

        $scope.BankControl = function (controlDiv, map) {

            controlDiv.style.padding = '5px';

            var controlUI = document.createElement('div');
            controlUI.style.backgroundColor = 'white';
            controlUI.style.borderStyle = 'solid';
            controlUI.style.borderWidth = '2px';
            controlUI.style.cursor = 'pointer';
            controlUI.style.textAlign = 'center';
            controlUI.title = 'Click to find banks nearby';
            controlDiv.appendChild(controlUI);

            var controlText = document.createElement('div');
            controlText.style.fontFamily = 'Arial,sans-serif';
            controlText.style.fontSize = '12px';
            controlText.style.paddingLeft = '4px';
            controlText.style.paddingRight = '4px';
            controlText.innerHTML = 'Bank';
            controlUI.appendChild(controlText);

            $scope.maps.event.addDomListener(controlUI, 'click', function () {
                var infowindow;

                var newYork = new $scope.maps.LatLng(40.69847032728747, -73.9514422416687);
                var center = $scope.map.getCenter();
                var request = {

                    location: center,
                    radius: 50000,
                    types: ['bank']
                };
                $scope.infowindow = new $scope.maps.InfoWindow();
                var service = new $scope.maps.places.PlacesService($scope.map);
                service.nearbySearch(request, $scope.callback);

            });

            $scope.callback = function (results, status) {
                if (status == $scope.maps.places.PlacesServiceStatus.OK) {
                    for (var i = 0; i < results.length; i++) {
                        $scope.createMarker(results[i]);
                    }
                }
            };

            $scope.createMarker = function (place) {
                var placeLoc = place.geometry.location;
                var marker = new $scope.maps.Marker({
                    map: $scope.map,
                    position: place.geometry.location
                });

                var contentString =
                    place.name +
                    '
' +
                    '
' +
                    '
' +
                    '
' +
                    '<button onclick="$(\'#bodyContent\').scope().addWaypoint(\'' + place.geometry.location + '\'); console.log(\'You clicked\')">Add</button>' +
                    '
' +
                    '
';

                $scope.maps.event.addListener(marker, 'click', function () {
                    $scope.infowindow.setContent(contentString);
                    $scope.infowindow.open($scope.map, this);

                });
            }

        };

        GoogleMapApi.then(function (maps) {
            $scope.maps = maps;
            console.log(maps);
            $scope.initialize();
        });

        $scope.initialize = function () {

//            markerHandler()
            $scope.travelMode = $scope.maps.TravelMode.DRIVING;
            var newYork = new $scope.maps.LatLng(40.69847032728747, -73.9514422416687);
            $scope.start = newYork;
            $scope.end = newYork;
            $scope.rendererOptions = {
                draggable: true
            };
            $scope.directionsDisplay = new $scope.maps.DirectionsRenderer($scope.rendererOptions);
            $scope.directionsService = new $scope.maps.DirectionsService();

            $scope.travelModes = [
                {
                    name: "driving",
                    value: $scope.maps.TravelMode.DRIVING
                },
                {
                    name: "walking",
                    value: $scope.maps.TravelMode.WALKING
                },
                {
                    name: "bicycling",
                    value: $scope.maps.TravelMode.BICYCLING
                },
                {
                    name: "transit",
                    value: $scope.maps.TravelMode.TRANSIT
                }

            ];

            $scope.directionsDisplay = new $scope.maps.DirectionsRenderer($scope.rendererOptions);
//            $scope.chicago = new $scope.maps.LatLng(41.850033, -87.6500523);
            var mapOptions = {
                zoom: 7
            };
            $scope.map = new $scope.maps.Map(document.getElementById('map-canvas'), mapOptions);

            $scope.directionsDisplay.setMap($scope.map);
            $scope.directionsDisplay.setPanel(document.getElementById("directionsPanel"));
            $scope.maps.event.addListener($scope.directionsDisplay, 'directions_changed', function () {
                $scope.computeTotalDistance($scope.directionsDisplay.getDirections());

            });

            $scope.bankControlDiv;

            $scope.bankControlDiv = document.createElement('div');
            $scope.bankControl = new $scope.BankControl($scope.bankControlDiv, $scope.map);
            $scope.bankControlDiv.index = 1;
            $scope.map.controls[$scope.maps.ControlPosition.TOP_RIGHT].push($scope.bankControlDiv);

            $scope.calcRoute()
        };

        $scope.calcRoute = function () {
            var request = {
                origin: $scope.start,
                destination: $scope.end,
//                travelMode: $scope.maps.TravelMode.DRIVING
                waypoints: $scope.waypoints,
                optimizeWaypoints: true,
                travelMode: $scope.travelMode
            };
            $scope.directionsService.route(request, function (response, status) {
                if (status == $scope.maps.DirectionsStatus.OK) {
                    $scope.directionsDisplay.setDirections(response);
                    $scope.directionsDisplay.setPanel(document.getElementById("directionsPanel"));
                }
            });

            $scope.maps.event.trigger($scope.map, 'resize');
        };

        $scope.computeTotalDistance = function (result) {
            var total = 0;
            var myroute = result.routes[0];
            for (var i = 0; i < myroute.legs.length; i++) {
                total += myroute.legs[i].distance.value;
            }
            total = total / 1000.0;
            document.getElementById('total').innerHTML = total + 'km';
        }

    }]);

<|endoftext|>


<commit_before>import _ from 'lodash'

function groupPlaceholdersAndQualifiers (output) {
  return _.chain(output.words)
    .filter(item => item.placeholder || item.text)
    .map(item => item.placeholder ? '\uFFFC' : `\uFFF9${item.text}\uFFFA${item.argument}\uFFFB`)
    .join('')
    .concat(`\uFFF9${output.qualifiers.join('\uFFFA')}\uFFFB`)
    .value()
}

function mapPlaceholderGroups (outputGroup) {
  const placeholders = _.chain(outputGroup)
    .map(output => {
      return _.chain(output.words)
        .filter('placeholder')
        .map('text')
        .value()
    })
    .thru(descriptorLists => _.zip(...descriptorLists))
    .map(x => _.unique(x))
    .map(x => _.filter(x))
    .value()

  const output = _.clone(_.first(outputGroup))

  _.chain(output.words)
    .filter('placeholder')
    .forEach((item, index) => {
      item.placeholderTexts = placeholders[index]
      // item.descriptors = [placeholders[index]]
    })
    .value()

  return output
}

export function combinePlaceholders (outputs, limit = 100) {
  return _.chain(outputs)
    .groupBy(groupPlaceholdersAndQualifiers)
    .map(mapPlaceholderGroups)
    .sortBy(option => -option.score)
    .take(limit)
    .value()
}

// First - group by placeholders
<commit_msg>Group by qualifiers<commit_after>import _ from 'lodash'

function groupPlaceholders (output) {
  return _.chain(output.words)
    .filter(item => item.placeholder || item.text)
    .map(item => item.placeholder ? '\uFFFC' : `\uFFF9${item.text}\uFFFA${item.argument}\uFFFB`)
    .join('')
    .value()
}

function groupPlaceholdersAndQualifiers (output) {
  return _.chain(output.words)
    .filter(item => item.placeholder || item.text)
    .map(item => item.placeholder ? '\uFFFC' : `\uFFF9${item.text}\uFFFA${item.argument}\uFFFB`)
    .join('')
    .concat(`\uFFF9${output.qualifiers.join('\uFFFA')}\uFFFB`)
    .value()
}

function mapPlaceholderGroups (outputGroup) {
  const placeholders = _.chain(outputGroup)
    .map(output => {
      return _.chain(output.words)
        .filter('placeholder')
        .map('text')
        .value()
    })
    .thru(descriptorLists => _.zip(...descriptorLists))
    .map(x => _.uniq(x))
    .map(x => _.filter(x))
    .value()

  const output = _.clone(_.first(outputGroup))

  _.chain(output.words)
    .filter('placeholder')
    .forEach((item, index) => {
      item.placeholderTexts = placeholders[index]
      // item.descriptors = [placeholders[index]]
    })
    .value()

  return output
}

function flatMapQualifiers (outputGroup) {
  // PERF
  // if there is only one item, then we don't need qualifiers
  if (outputGroup.length === 1) {
    return [_.assign({}, outputGroup[0], {qualifiers: []})]
  }

  const allQualifiers = _.map(outputGroup, 'qualifiers')
  const commonQualifiers = _.intersection(...allQualifiers)

  // PERF
  // if there are no commonQualifiers, no need to do all this work
  if (commonQualifiers.length) {
    return _.map(outputGroup, output => {
      const qualifiers = _.difference(output.qualifiers, commonQualifiers)
      return _.assign({}, output, {qualifiers})
    })
  } else {
    return outputGroup
  }
}

export function combinePlaceholders (outputs, limit = 100) {
  return _.chain(outputs)
    .groupBy(groupPlaceholdersAndQualifiers)
    .map(mapPlaceholderGroups)
    .groupBy(groupPlaceholders)
    .flatMap(flatMapQualifiers)
    .sortBy(option => -option.score)
    .take(limit)
    .value()
}

// First - group by placeholders
<|endoftext|>


<commit_before>/*
 * Licensed to Cloudkick, Inc ('Cloudkick') under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * Cloudkick licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

var path = require('path');
var fs = require('fs');

var async = require('async');

var config = require('util/config');
var misc = require('util/misc');
var Errorf = misc.Errorf;

var pluginConstants = require('plugins/constants');

/**
 * Get routes defined in the plugin endpoint module.
 *
 * @param {String} modulePath Path to the module file.
 * @param {Function} callback Callback called with (err, routes)
 */
function getEndpointRoutes(modulePath, callback) {
  var exported, routes;

  try {
    exported = require(modulePath);
  }
  catch (err) {
    callback(new Errorf('Failed to load module "%s": %s', modulePath, err));
    return;
  }

  routes = (exported.routes) ? exported.routes : null;
  callback(null, routes);
}

/**
 * Discover all the HTTP endpoints exposed by the plugin.
 *
 * @param {String} pluginPath Path to the plugin root directory.
 * @param {Function} callback Callback called with (err, routes) .Routes is an
 * object where the key is the endpoint module name and the value is an Array
 * with routes as defined by the plugin.
 */
function discoverEndpoints(pluginPath, callback) {
  var endpointRoutes = {};
  var endpointsPath = path.join(pluginPath, pluginConstants.HTTP_ENDPOINTS_DIRECTORY);

  async.waterfall([
    function pathExists(callback) {
      path.exists(endpointsPath, function onExists(exists) {
        if (!exists) {
          callback(new Error('Endpoints directory does not exist'));
          return;
        }

        callback();
      });
    },

    function listFiles(callback) {
      fs.readdir(endpointsPath, function(err, files) {
        if (err) {
          callback(err);
          return;
        }

        callback(null, files);
      });
    },

    function discoverModuleEndpoints(files, callback) {
      async.forEach(files, function(file, callback) {
        var modulePath = path.join(endpointsPath, file);
        getEndpointRoutes(modulePath, function onEnd(err, routes) {
          if (err) {
            callback();
            return;
          }

          endpointRoutes[file] = routes;
        });
      }, callback);
    }
  ],

  function(err) {
    if (err) {
      callback(err);
      return;
    }

    callback(null, endpointRoutes);
  });
}

exports.discoverEndpoints = discoverEndpoints;
<commit_msg>No need to check this here. We can just check error errno.<commit_after>/*
 * Licensed to Cloudkick, Inc ('Cloudkick') under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * Cloudkick licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

var path = require('path');
var fs = require('fs');

var async = require('async');

var config = require('util/config');
var misc = require('util/misc');
var Errorf = misc.Errorf;

var pluginConstants = require('plugins/constants');

/**
 * Get routes defined in the plugin endpoint module.
 *
 * @param {String} modulePath Path to the module file.
 * @param {Function} callback Callback called with (err, routes)
 */
function getEndpointRoutes(modulePath, callback) {
  var exported, routes;

  try {
    exported = require(modulePath);
  }
  catch (err) {
    callback(new Errorf('Failed to load module "%s": %s', modulePath, err));
    return;
  }

  routes = (exported.routes) ? exported.routes : null;
  callback(null, routes);
}

/**
 * Discover all the HTTP endpoints exposed by the plugin.
 *
 * @param {String} pluginPath Path to the plugin root directory.
 * @param {Function} callback Callback called with (err, routes) .Routes is an
 * object where the key is the endpoint module name and the value is an Array
 * with routes as defined by the plugin.
 */
function discoverEndpoints(pluginPath, callback) {
  var endpointRoutes = {};
  var endpointsPath = path.join(pluginPath, pluginConstants.HTTP_ENDPOINTS_DIRECTORY);

  async.waterfall([
    function listFiles(callback) {
      fs.readdir(endpointsPath, function(err, files) {
        if (err) {
          callback(err);
          return;
        }

        callback(null, files);
      });
    },

    function discoverModuleEndpoints(files, callback) {
      async.forEach(files, function(file, callback) {
        var modulePath = path.join(endpointsPath, file);
        getEndpointRoutes(modulePath, function onEnd(err, routes) {
          if (err) {
            callback();
            return;
          }

          endpointRoutes[file] = routes;
        });
      }, callback);
    }
  ],

  function(err) {
    if (err) {
      callback(err);
      return;
    }

    callback(null, endpointRoutes);
  });
}

exports.discoverEndpoints = discoverEndpoints;
<|endoftext|>