<commit_before>function toggleClass() {
    var theme = document.getElementById("theme-area");
    theme.classList.toggle("active");
}
function setTheme(btn) {
    var newTheme = "cld-video-player-skin-" + btn.value;
    var oldTheme = "cld-video-player-skin-";
    if(btn.value == "dark") {
        oldTheme += "light";
    }
    else {
        oldTheme += "dark";
    }
    var vplayers = document.getElementsByClassName("cld-video-player");
    for(var i = 0; i < vplayers.length; i++) {
        vplayers[i].classList.remove(oldTheme);
        vplayers[i].classList.add(newTheme);
    }
    toggleClass();
}

window.addEventListener('scroll',checkPosition,false);

function checkPosition()
{
    var themeArea = document.getElementById("theme-area");
    if(window.scrollY > 50)
    {
        themeArea.classList.add("scrolling");
    } else {
        themeArea.classList.remove("scrolling");
    }
}

function showContentBlocks() {
    var contentBlocks = document.getElementsByClassName("content-block");
    for(var i = 0; i < contentBlocks.length; i++) {
        contentBlocks[i].classList.remove("hidden");
    }
}

function initScreen() {
    getTranscriptFile = true;
    transcriptComplete = false;
    progress = 0;
    autoTagProgress = 0;
    transcriptProgress = 0;
    showJSON("transcript","Waiting for automatic transcript...");
    showJSON("autotag","Waiting for automatic tagging...");
    showContentBlocks();
}

function uploadVideo(){
	cloudinary.openUploadWidget({ cloud_name: 'demo', upload_preset: 'video_autotag_transcript_sd_lambda', sources: [ 'local', 'url'], multiple: false, max_file_size: 100000000, resource_type: 'video'},
      function(error, result) { processResponse(error, result); }, false);
}

function useVideo(vid) {
    console.log("useVideo",vid.title);
    initScreen();
    publicId = vid.title + "_autotag";
    transcript = publicId + ".transcript";
    updatePlayers(vid.title + "_sd");
    updateAutoPlayers();
    progress = 20;
    updateProgress();
}

function updateAutoPlayers() {
    autoTagPlayer.source(publicId);
    transcriptPlayer.source(publicId);
}

function processResponse(error, result) {
    console.log(error, result);
    initScreen();
    publicId = result[0].public_id;
    transcript = publicId + ".transcript";
    updatePlayers(publicId);
    updateAutoPlayers();
    updateProgress();
}

function updatePlayers(pid) {
    for(var i = 0; i < players.length; i++)
        players[i].source(pid);
    var links = document.getElementsByClassName("manipulation")
    for(var j = 0; j < links.length; j++) {
            var ref = links[j].getAttribute("data-href");
            links[j].setAttribute("href",ref+pid+".mp4");
    }
}

function updateProgress() {
    progress++;
    console.log("updateProgress", progress);
    if (progress == 30)
        checkLambda();
    if(autoTagProgress < 100)
        updateAutoTagProgress()
    if(transcriptProgress < 100)
        updateTranscriptProgress()
    if (autoTagProgress < 100 || transcriptProgress < 100)
        setTimeout(updateProgress,1000);
}

function getData() {
    if(getTranscriptFile && transcriptComplete)
        getTranscript();
    else if (autoTagProgress < 100 || transcriptProgress < 100)
        checkLambda();
    else
        console.log("getData Done");
}

  function getTranscript() {
	console.log("getTranscript", transcript);
	var checkUrl = url + transcript;
    httpTranscript.open('GET', checkUrl);
	httpTranscript.send();
}

function checkLambda() {
    console.log("checkLambda", transcript);
    var checkUrl = "https://4k4smz181f.execute-api.us-east-1.amazonaws.com/Prod/" + publicId;
    httpLambda.open('GET', checkUrl);
	httpLambda.send();
}

var httpLambda = new XMLHttpRequest();
httpLambda.onreadystatechange = function() {
    if (this.readyState == 4) {
        if(this.status == 200) {
          var notify = JSON.parse(httpLambda.responseText);
          checkLambdaNotification(notify);
        }
        setTimeout(getData,4000);
    }
    else
          console.log("onreadystatechange", this.readyState, this.status);
}

var httpTranscript = new XMLHttpRequest();
httpTranscript.onreadystatechange = function() {
    if (this.readyState == 4) {
        if(this.status == 200) {
          var notify = JSON.parse(httpTranscript.responseText);
          checkTranscriptFile(notify);
        }
        setTimeout(getData,3000);
    }
    else
          console.log("onreadystatechange", this.readyState, this.status);
}

function checkLambdaNotification(notify) {
        checkTranscript(notify);
        checkTags(notify);
}

function checkTranscriptFile(notify) {
    if(transcriptComplete && getTranscriptFile && Array.isArray(notify)) {
        transcriptProgress = 99;
        showJSON("transcript",notify);
        getTranscriptFile = false;
    }
}

function checkTranscript(notify) {
    if (notify.transcript.status == "pending") {
        console.log("transcript pending");
    }
    else if (notify.transcript.status == "complete") {
        transcriptComplete = true;
        transcriptPlayer.source(publicId,{ transformation: [{crop: 'limit', width: 640}, {overlay: "subtitles:"+transcript}] }).play();
        console.log("transcript ready");
    }
    else
    {
        getTranscriptFile = false;
        transcriptComplete = true;
        transcriptPlayer.source(publicId,{ transformation: {crop: 'limit', width: 640} }).play();
        transcriptProgress = 99;
        showJSON("transcript","There is no transcript for this video");
        console.log("no transcript");
    }
}

function checkTags(notify) {
    if (notify.tags.status == "pending")
        console.log("autotag pending");
    else if (notify.tags.status == "complete") {
        autoTagProgress = 99;
        showJSON("autotag",notify.tags.data);
    }
    else
        console.log("no autotag");
}

function showJSON(id,notify) {
    var content = document.getElementById(id);
    var autoTaggingData = JSON.stringify(notify);
    content.innerText = autoTaggingData;
    Prism.highlightElement(content);
}

function updateAutoTagProgress() {
    autoTagProgress++;
    var autoTaggingBar = document.getElementById("autoTaggingBar");
    autoTaggingBar.style.width = autoTagProgress + '%';
}

function updateTranscriptProgress() {
    transcriptProgress++;
    var transcriptBar = document.getElementById("transcriptBar");
    transcriptBar.style.width = transcriptProgress + '%';
}

var url = "https://res.cloudinary.com/demo/raw/upload/";
var publicId = "sample";
var transcript = "sample.transcript"
var getTranscriptFile = true;
var transcriptComplete = false;
var progress = 0;
var autoTagProgress = 0;
var transcriptProgress = 0;

var cld = cloudinary.Cloudinary.new({ cloud_name: 'demo' });

var players = cld.videoPlayers('.demo-manipulation', {videojs: { bigPlayButton: false, controlBar: false } });

var transcriptPlayer = cld.videoPlayer('demo-transcript-player');

var autoTagPlayer = cld.videoPlayer('demo-autotag-player');

<commit_msg>Update transcode.js<commit_after>function toggleClass() {
    var theme = document.getElementById("theme-area");
    theme.classList.toggle("active");
}
function setTheme(btn) {
    var newTheme = "cld-video-player-skin-" + btn.value;
    var oldTheme = "cld-video-player-skin-";
    if(btn.value == "dark") {
        oldTheme += "light";
    }
    else {
        oldTheme += "dark";
    }
    var vplayers = document.getElementsByClassName("cld-video-player");
    for(var i = 0; i < vplayers.length; i++) {
        vplayers[i].classList.remove(oldTheme);
        vplayers[i].classList.add(newTheme);
    }
    toggleClass();
}

window.addEventListener('scroll',checkPosition,false);

function checkPosition()
{
    var themeArea = document.getElementById("theme-area");
    if(window.scrollY > 50)
    {
        themeArea.classList.add("scrolling");
    } else {
        themeArea.classList.remove("scrolling");
    }
}

function showContentBlocks() {
    var contentBlocks = document.getElementsByClassName("content-block");
    for(var i = 0; i < contentBlocks.length; i++) {
        contentBlocks[i].classList.remove("hidden");
    }
}

function initScreen() {
    getTranscriptFile = true;
    transcriptComplete = false;
    progress = 0;
    autoTagProgress = 0;
    transcriptProgress = 0;
    showJSON("transcript","Waiting for automatic transcript...");
    showJSON("autotag","Waiting for automatic tagging...");
    showContentBlocks();
}

function uploadVideo(){
	cloudinary.openUploadWidget({ cloud_name: 'demo', upload_preset: 'video_autotag_transcript_sd_lambda', sources: [ 'local', 'url'], multiple: false, max_file_size: 100000000, resource_type: 'video'},
      function(error, result) { processResponse(error, result); }, false);
}

function useVideo(vid) {
    console.log("useVideo",vid.title);
    initScreen();
    publicId = vid.title + "_autotag";
    transcript = publicId + ".transcript";
    updatePlayers(vid.title + "_sd");
    updateAutoPlayers();
    progress = 20;
    updateProgress();
}

function updateAutoPlayers() {
    autoTagPlayer.source(publicId);
    transcriptPlayer.source(publicId);
}

function processResponse(error, result) {
    console.log(error, result);
    initScreen();
    publicId = result[0].public_id;
    transcript = publicId + ".transcript";
    updatePlayers(publicId);
    updateAutoPlayers();
    updateProgress();
}

function updatePlayers(pid) {
    for(var i = 0; i < players.length; i++)
        players[i].source(pid);
    var links = document.getElementsByClassName("manipulation")
    for(var j = 0; j < links.length; j++) {
            var ref = links[j].getAttribute("data-href");
            links[j].setAttribute("href",ref+pid+".mp4");
    }
}

function updateProgress() {
    progress++;
    console.log("updateProgress", progress);
    if (progress == 30)
        checkLambda();
    if(autoTagProgress < 100)
        updateAutoTagProgress()
    if(transcriptProgress < 100)
        updateTranscriptProgress()
    if (autoTagProgress < 100 || transcriptProgress < 100)
        setTimeout(updateProgress,1000);
}

function getData() {
    if(getTranscriptFile && transcriptComplete)
        getTranscript();
    else if (autoTagProgress < 100 || transcriptProgress < 100)
        checkLambda();
    else
        console.log("getData Done");
}

  function getTranscript() {
	console.log("getTranscript", transcript);
	var checkUrl = url + transcript;
    httpTranscript.open('GET', checkUrl);
	httpTranscript.send();
}

function checkLambda() {
    console.log("checkLambda", transcript);
    var checkUrl = "https://4k4smz181f.execute-api.us-east-1.amazonaws.com/Prod/" + publicId;
    httpLambda.open('GET', checkUrl);
	httpLambda.send();
}

var httpLambda = new XMLHttpRequest();
httpLambda.onreadystatechange = function() {
    if (this.readyState == 4) {
        if(this.status == 200) {
          var notify = JSON.parse(httpLambda.responseText);
          checkLambdaNotification(notify);
        }
        setTimeout(getData,4000);
    }
    else
          console.log("onreadystatechange", this.readyState, this.status);
}

var httpTranscript = new XMLHttpRequest();
httpTranscript.onreadystatechange = function() {
    if (this.readyState == 4) {
        if(this.status == 200) {
          var notify = JSON.parse(httpTranscript.responseText);
          checkTranscriptFile(notify);
        }
        setTimeout(getData,3000);
    }
    else
          console.log("onreadystatechange", this.readyState, this.status);
}

function checkLambdaNotification(notify) {
        checkTranscript(notify);
        checkTags(notify);
}

function checkTranscriptFile(notify) {
    if(transcriptComplete && getTranscriptFile && Array.isArray(notify)) {
        transcriptProgress = 99;
        showJSON("transcript",notify);
        getTranscriptFile = false;
    }
}

function checkTranscript(notify) {
    if (notify.transcript.status == "pending") {
        console.log("transcript pending");
    }
    else if (notify.transcript.status == "complete") {
        transcriptComplete = true;
        transcriptPlayer.source(publicId,{ transformation: {overlay: "subtitles:"+transcript}}).play();
        console.log("transcript ready");
    }
    else
    {
        getTranscriptFile = false;
        transcriptComplete = true;
        transcriptPlayer.source(publicId).play();
        transcriptProgress = 99;
        showJSON("transcript","There is no transcript for this video");
        console.log("no transcript");
    }
}

function checkTags(notify) {
    if (notify.tags.status == "pending")
        console.log("autotag pending");
    else if (notify.tags.status == "complete") {
        autoTagProgress = 99;
        showJSON("autotag",notify.tags.data);
    }
    else
        console.log("no autotag");
}

function showJSON(id,notify) {
    var content = document.getElementById(id);
    var autoTaggingData = JSON.stringify(notify);
    content.innerText = autoTaggingData;
    Prism.highlightElement(content);
}

function updateAutoTagProgress() {
    autoTagProgress++;
    var autoTaggingBar = document.getElementById("autoTaggingBar");
    autoTaggingBar.style.width = autoTagProgress + '%';
}

function updateTranscriptProgress() {
    transcriptProgress++;
    var transcriptBar = document.getElementById("transcriptBar");
    transcriptBar.style.width = transcriptProgress + '%';
}

var url = "https://res.cloudinary.com/demo/raw/upload/";
var publicId = "sample";
var transcript = "sample.transcript"
var getTranscriptFile = true;
var transcriptComplete = false;
var progress = 0;
var autoTagProgress = 0;
var transcriptProgress = 0;

var cld = cloudinary.Cloudinary.new({ cloud_name: 'demo' });

var players = cld.videoPlayers('.demo-manipulation', {videojs: { bigPlayButton: false, controlBar: false } });

var transcriptPlayer = cld.videoPlayer('demo-transcript-player');

var autoTagPlayer = cld.videoPlayer('demo-autotag-player');

<|endoftext|>


<commit_before>var path = require('path')
var webpack = require('webpack')

module.exports = {
  cache: true
, debug: true
, devtool: 'source-map'
, entry: {
    app: './public/js/app'
  }
, devServer: {
    contentBase: './public'
  }
, output: {
    path: path.join(__dirname, 'public/dist')
  , publicPath: '/dist'
  , filename: '[name].js'
  , chunkFilename: '[chunkhash].js'
  , sourceMapFilename: 'debugging/[file].map'
  , hotUpdateChunkFilename: 'hot/[id].[hash].hot-update.js'
  , hotUpdateMainFilename: 'hot/[hash].hot-update.json'
  }
, module: {
    loaders: [
    , { test: /\.js$/, exclude:[/node_modules/, /public\/components/], loader: '6to5-loader?experimental&runtime' }
    , { test: /\.jsx$/, exclude:[/node_modules/, /public\/components/], loaders: ['6to5-loader?experimental&runtime', 'react-hot', 'jsx'] }
    , { test: /\.css$/, loader: 'style-loader!css-loader' }
    ]
  , noParse: /\.min\.js/
  }
, resolve: {
    modulesDirectories: ['public/components', 'node_modules'],
    extensions: ['', '.js', '.jsx', '.json']
  }
, plugins: [
    new webpack.HotModuleReplacementPlugin()
  , new webpack.ProvidePlugin({
      to5Runtime: "imports?global=>{}!exports?global.to5Runtime!6to5/runtime"
    })
  , new webpack.ProvidePlugin({
      jQuery: 'jquery'
    , $: 'jquery'
    })
  ]
}
<commit_msg>Update webpack.config.js<commit_after>var path = require('path')
var webpack = require('webpack')

module.exports = {
  cache: true
, debug: true
, devtool: 'source-map'
, entry: {
    app: './public/js/app'
  }
, devServer: {
    contentBase: './public'
  }
, output: {
    path: path.join(__dirname, 'public/dist')
  , publicPath: '/dist'
  , filename: '[name].js'
  , chunkFilename: '[chunkhash].js'
  , sourceMapFilename: 'debugging/[file].map'
  , hotUpdateChunkFilename: 'hot/[id].[hash].hot-update.js'
  , hotUpdateMainFilename: 'hot/[hash].hot-update.json'
  }
, module: {
    loaders: [
      {test: /\.js$/, exclude:[/node_modules/, /public\/components/], loader: '6to5-loader?experimental&runtime'}
    , {test: /\.jsx$/, exclude:[/node_modules/, /public\/components/], loaders: ['6to5-loader?experimental&runtime', 'react-hot', 'jsx']}
    , {test: /\.css$/, loader: 'style-loader!css-loader'}
    ]
  , noParse: /\.min\.js/
  }
, resolve: {
    modulesDirectories: ['public/components', 'node_modules']
  , extensions: ['', '.js', '.jsx', '.json']
  }
, plugins: [
    new webpack.HotModuleReplacementPlugin()
  , new webpack.ProvidePlugin({
      to5Runtime: "imports?global=>{}!exports?global.to5Runtime!6to5/runtime"
    })
  , new webpack.ProvidePlugin({
      jQuery: 'jquery'
    , $: 'jquery'
    })
  ]
}
<|endoftext|>


<commit_before>"use strict";

var express = require('express')
  , ChatroomCtrl = require('../../controller/chatroom')
  ;

var chatroom = express();

module.exports = chatroom;

<commit_msg>v1_chatroom<commit_after>"use strict";

var express = require('express')
//  , UserCtrl = require('../../controller/user')
  , ChatroomCtrl = require('../../controller/chatroom')
  ;

var chatroom = express();

//chatroom.use('/chatrooms', UserCtrl.requireUser);

/**
 * @api {get} /chatrooms/:chatroomId Get individual chatroom
 * @apiName GetChatroom
 * @apiGroup Chatroom
 *
 * @apiPermission owner
 * @apiPermission admin
 *
 * @apiParam {ObjectId} :chatroomId Chatroom's unique ID.
 *
 * @apiUse SuccessCode
 *
 * @apiUse ChatroomNotFound
 * @apiUse GetChatroomsFailed
 * @apiUse MembersOfChatroomGottenFailed
 * @apiUse MemberEnterChatroomFailed
 * @apiUse MemberLeaveFailed
 * @apiUse MemberPermissionNotAllowed
 * @apiUse NotAcceptedAddress
 * @apiUse TextsInChatroomGottenFailed
 * @apiUse TextNotFound
 * @apiUse UserAuthRequired
 * @apiUse UserPermissionNotAllowed
 *
 */

//chatroom.get('/chatrooms/:chatroomId', ChatroomCtrl.loadChatroom, UserCtrl.permitOwnerOrAdmin, ChatroomCtrl.getChatroom);

/**
 * @api {post} /chatrooms add chatroom
 * @apiName AddChatroom
 * @apiGroup Chatroom
 *
 * @apiPermission admin
 *
 * @apiParam {ObjectId} :chatroomId Chatroom's unique ID.
 * @apiParam {String[]} :userId Chatroom's participants unique ID Array.
 * @apiParam {String} targetGender Chatroom's target gender(Male or Female or Both)
 * @apiParam {String} :dramaId Chatroom's drama unique ID.
 * @apiParam {String} address IPv4 Address with Port
 * @apiParam {Boolean} isUp Chatroom is exist or not
 *
 * @apiUse SuccessCode
 *
 * @apiUse ChatroomAddFailed
 * @apiUse AddTextToChatroomFailed
 * @apiUse InsertTextInDBFailed
 * @apiUse EmptyText
 * @apiUse UserAuthRequired
 * @apiUse UserPermissionNotAllowed
 *
 */

//chatroom.post('/chatrooms', ChatroomCtrl.findDrama, UserCtrl.permitAdmin, ChatroomCtrl.addChatroom);

/**
 * @api {put} chatrooms/:chatroomId Update chatroom information
 * @apiName UpdateChatroom
 * @apiGroup Chatroom
 *
 * @apiPermission admin
 *
 * @apiParam {ObjectId} :chatroomId Chatroom's unique ID.
 * @apiParam {String[]} :userId Chatroom's participants unique ID List.
 * @apiParam {String} targetGender Chatroom's target gender(Male or Female or Both)
 * @apiParam {String} :dramaId Chatroom's drama unique ID.
 * @apiParam {String} address IPv4 Address with Port
 * @apiParam {Boolean} isUp Chatroom is exist or not
 *
 * @apiUse SuccessCode
 *
 * @apiUse ChatroomUpdateFailed
 * @apiUse UserAuthRequired
 * @apiUse UserPermissionNotAllowed
 *
 */

//chatroom.put('chatrooms/:chatroomId', UserCtrl.permitAdmin, ChatroomCtrl.updateChatroom);

/**
 * @api {delete} chatrooms/:chatroomId Remove chatroom information
 * @apiName RemoveChatroom
 * @apiGroup Chatroom
 *
 * @apiPermission admin
 *
 * @apiParam {ObjectId} :chatroomId Chatroom's unique ID.
 *
 * @apiUse SuccessCode
 *
 * @apiUse ChatroomRemoveFailed
 * @apiUse UserAuthRequired
 * @apiUse UserPermissionNotAllowed
 *
 */

//chatroom.delete('chatrooms/:chatroomId', ChatroomCtrl.removeChatroom);

module.exports = chatroom;<|endoftext|>


<commit_before>const UglifyJSPlugin = require('uglifyjs-webpack-plugin');

module.exports = {
  entry: './app/main.js',
  output: {
    path: __dirname,
    filename: 'browser/noflo-ui.min.js',
    sourceMapFilename: 'browser/noflo-ui.min.js.map',
  },
  devtool: 'source-map',
  plugins: [
    new UglifyJSPlugin({
      sourceMap: true,
    })
  ],
  module: {
    rules: [
      {
        test: /noflo([\\]+|\/)lib([\\]+|\/)loader([\\]+|\/)register.js$/,
        use: [
          {
            loader: 'noflo-component-loader',
            options: {
              graph: 'ui/main',
              debug: false,
              baseDir: __dirname,
              manifest: {
                runtimes: ['noflo'],
                discover: true,
                recursive: true,
              },
              runtimes: [
                'noflo',
                'noflo-browser'
              ],
            }
          }
        ]
      },
      {
        test: /noflo([\\]+|\/)lib([\\]+|\/)(.*)\.js$|noflo([\\]+|\/)components([\\]+|\/)(.*)\.js$|fbp-graph([\\]+|\/)lib([\\]+|\/)(.*)\.js$|noflo-runtime-([a-z]+)([\\]+|\/)(.*).js$/,
        use: [
          {
            loader: 'babel-loader',
            options: {
              presets: ['es2015'],
            }
          }
        ]
      },
      {
        test: /\.coffee$/,
        use: [
          {
            loader: 'coffee-loader',
            options: {
              sourceMap: true,
              transpile: {
                presets: ['es2015']
              }
            }
          }
        ]
      },
      {
        test: /\.fbp$/,
        use: [
          'fbp-loader'
        ]
      },
      {
        test: /\.json$/,
        use: [
          'json-loader'
        ]
      },
      {
        test: /\.yaml$/,
        use: [
          'json-loader',
          'yaml-loader'
        ]
      }
    ]
  },
  externals: {
    'canvas': 'commonjs canvas', // Required by noflo-image
    'coffee-script': 'commonjs coffee-script', // Required by noflo-image
  },
  resolve: {
    extensions: [".coffee", ".js"],
  },
  node: {
    child_process: 'empty',
    fs: 'empty',
  },
};
<commit_msg>We no longer need to deal with CoffeeScript 1 here<commit_after>const UglifyJSPlugin = require('uglifyjs-webpack-plugin');

module.exports = {
  entry: './app/main.js',
  output: {
    path: __dirname,
    filename: 'browser/noflo-ui.min.js',
    sourceMapFilename: 'browser/noflo-ui.min.js.map',
  },
  devtool: 'source-map',
  plugins: [
    new UglifyJSPlugin({
      sourceMap: true,
    })
  ],
  module: {
    rules: [
      {
        test: /noflo([\\]+|\/)lib([\\]+|\/)loader([\\]+|\/)register.js$/,
        use: [
          {
            loader: 'noflo-component-loader',
            options: {
              graph: 'ui/main',
              debug: false,
              baseDir: __dirname,
              manifest: {
                runtimes: ['noflo'],
                discover: true,
                recursive: true,
              },
              runtimes: [
                'noflo',
                'noflo-browser'
              ],
            }
          }
        ]
      },
      {
        test: /noflo([\\]+|\/)lib([\\]+|\/)(.*)\.js$|noflo([\\]+|\/)components([\\]+|\/)(.*)\.js$|fbp-graph([\\]+|\/)lib([\\]+|\/)(.*)\.js$|noflo-runtime-([a-z]+)([\\]+|\/)(.*).js$/,
        use: [
          {
            loader: 'babel-loader',
            options: {
              presets: ['es2015'],
            }
          }
        ]
      },
      {
        test: /\.coffee$/,
        use: [
          {
            loader: 'coffee-loader',
            options: {
              sourceMap: true,
              transpile: {
                presets: ['es2015']
              }
            }
          }
        ]
      },
      {
        test: /\.fbp$/,
        use: [
          'fbp-loader'
        ]
      },
      {
        test: /\.json$/,
        use: [
          'json-loader'
        ]
      },
      {
        test: /\.yaml$/,
        use: [
          'json-loader',
          'yaml-loader'
        ]
      }
    ]
  },
  externals: {
    'canvas': 'commonjs canvas', // Required by noflo-image
  },
  resolve: {
    extensions: [".coffee", ".js"],
  },
  node: {
    child_process: 'empty',
    fs: 'empty',
  },
};
<|endoftext|>


<commit_before>// Copyright (c) 2012, Joyent, Inc. All rights reserved.

var assert = require('assert');
var dns = require('dns');
var url = require('url');

var named = require('named');
var uuid = require('node-uuid');
var zkplus = require('zkplus');

var zk = require('./zk');

///--- Globals

var ARecord = named.ARecord;

///--- Helpers

// Fisher-Yates shuffle
// http://sedition.com/perl/javascript-fy.html
function shuffle(arr) {
        if (arr.length === 0)
                return (arr);

        var i = arr.length;
        while (--i > 0) {
                var j = Math.floor(Math.random() * (i + 1));
                var tmp = arr[i];
                arr[i] = arr[j];
                arr[j] = tmp;
        }

        return (arr);
}

function resolve(options, query) {
        var domain = query.name();
        var id = uuid.v1();
        var req = {
                cache: options.cache,
                id: id,
                log: options.log.child({id: id}, true),
                query: query,
                zkClient: options.zkClient
        };

        req.log.debug({query: query}, 'resolve: new request');
        zk.resolveName(req, function (err, record) {
                if (err && err.code !== zkplus.ZNONODE) {
                        req.log.debug({
                                err: err,
                                query: query
                        }, 'Error talking to ZK');

                } else if (err && err.code === zkplus.ZNONODE) {
                        req.log.debug({
                                query: query
                        }, 'Node not found in ZK; trying external DNS');
                        dns.resolve4(domain, function (err2, addresses) {
                                if (err2) {
                                        req.log.debug({
                                                query: query,
                                                err: err2
                                        }, 'domain not found externally');
                                } else {
                                        addresses.forEach(function (a) {
                                                var r = new ARecord(a);
                                                query.addAnswer(domain, r);
                                        });
                                }

                                req.log.debug({
                                        query: query,
                                        answers: query.answers()
                                }, 'resolve: sending query back to client');
                                return (query.respond());
                        });
                        return (undefined);

                } else {
                        var addr;
                        var ttl = record.ttl;
                        switch (record.type) {
                        case 'database':
                                var _u = url.parse(record.database.primary);
                                addr = _u.hostname;
                                query.addAnswer(domain, new ARecord(addr), ttl);
                                break;

                        case 'db_host':
                        case 'host':
                        case 'load_balancer':
                                addr = record[record.type].address;
                                query.addAnswer(domain, new ARecord(addr), ttl);
                                break;

                        case 'service':
                                // Inefficient, but easy to reason about.
                                shuffle(record.children.filter(function (sub) {
                                        return (sub.type === 'load_balancer');
                                }).map(function (lb) {
                                        return (lb.load_balancer.address);
                                })).forEach(function (a) {
                                        var ar = new ARecord(a);
                                        query.addAnswer(domain, ar, ttl);
                                });
                                break;

                        default:
                                req.log.debug({
                                        query: query,
                                        record: record
                                }, 'Not a known record type');
                                break;
                        }
                }

                req.log.debug({
                        query: query,
                        answers: query.answers()
                }, 'resolve: sending query back to client');
                return (query.respond());
        });
}

///--- API

function createServer(options) {
        assert.ok(options);

        var server = named.createServer({
                name: options.name || 'binder',
                log: options.log
        });

        server.on('query', function onQuery(query) {
                switch (query.type()) {
                case 'A':
                        resolve(options, query);
                        break;

                default:
                        // Anything unsupported we just respond empty
                        query.respond();
                        break;
                }
        });

        server.on('after', function (query, bytes) {
                options.log.info({
                        query: query,
                        answers: query.answers()
                }, 'DNS query');
        });

        server.start = function start(callback) {
                server.on('listening', function () {
                        options.log.info({
                                host: options.host,
                                port: options.port
                        }, 'DNS service started');

                        if (typeof (callback) === 'function')
                                callback();
                });
                server.listen(options.port, options.host);
        };

        server.stop = function stop(callback) {
                server.close(callback);
        };

        return (server);
}

///--- Exports

module.exports = {

        createServer: createServer

};<commit_msg>MANTA-22: add redis_host type to binder resolution<commit_after>// Copyright (c) 2012, Joyent, Inc. All rights reserved.

var assert = require('assert');
var dns = require('dns');
var url = require('url');

var named = require('named');
var uuid = require('node-uuid');
var zkplus = require('zkplus');

var zk = require('./zk');

///--- Globals

var ARecord = named.ARecord;

///--- Helpers

// Fisher-Yates shuffle
// http://sedition.com/perl/javascript-fy.html
function shuffle(arr) {
        if (arr.length === 0)
                return (arr);

        var i = arr.length;
        while (--i > 0) {
                var j = Math.floor(Math.random() * (i + 1));
                var tmp = arr[i];
                arr[i] = arr[j];
                arr[j] = tmp;
        }

        return (arr);
}

function resolve(options, query) {
        var domain = query.name();
        var id = uuid.v1();
        var req = {
                cache: options.cache,
                id: id,
                log: options.log.child({id: id}, true),
                query: query,
                zkClient: options.zkClient
        };

        req.log.debug({query: query}, 'resolve: new request');
        zk.resolveName(req, function (err, record) {
                if (err && err.code !== zkplus.ZNONODE) {
                        req.log.debug({
                                err: err,
                                query: query
                        }, 'Error talking to ZK');

                } else if (err && err.code === zkplus.ZNONODE) {
                        req.log.debug({
                                query: query
                        }, 'Node not found in ZK; trying external DNS');
                        dns.resolve4(domain, function (err2, addresses) {
                                if (err2) {
                                        req.log.debug({
                                                query: query,
                                                err: err2
                                        }, 'domain not found externally');
                                } else {
                                        addresses.forEach(function (a) {
                                                var r = new ARecord(a);
                                                query.addAnswer(domain, r);
                                        });
                                }

                                req.log.debug({
                                        query: query,
                                        answers: query.answers()
                                }, 'resolve: sending query back to client');
                                return (query.respond());
                        });
                        return (undefined);

                } else {
                        var addr;
                        var ttl = record.ttl;
                        switch (record.type) {
                        case 'database':
                                var _u = url.parse(record.database.primary);
                                addr = _u.hostname;
                                query.addAnswer(domain, new ARecord(addr), ttl);
                                break;

                        case 'db_host':
                        case 'host':
                        case 'load_balancer':
                        case 'redis_host':
                                addr = record[record.type].address;
                                query.addAnswer(domain, new ARecord(addr), ttl);
                                break;

                        case 'service':
                                // Inefficient, but easy to reason about.
                                shuffle(record.children.filter(function (sub) {
                                        return (sub.type === 'load_balancer');
                                }).map(function (lb) {
                                        return (lb.load_balancer.address);
                                })).forEach(function (a) {
                                        var ar = new ARecord(a);
                                        query.addAnswer(domain, ar, ttl);
                                });
                                break;

                        default:
                                req.log.debug({
                                        query: query,
                                        record: record
                                }, 'Not a known record type');
                                break;
                        }
                }

                req.log.debug({
                        query: query,
                        answers: query.answers()
                }, 'resolve: sending query back to client');
                return (query.respond());
        });
}

///--- API

function createServer(options) {
        assert.ok(options);

        var server = named.createServer({
                name: options.name || 'binder',
                log: options.log
        });

        server.on('query', function onQuery(query) {
                switch (query.type()) {
                case 'A':
                        resolve(options, query);
                        break;

                default:
                        // Anything unsupported we just respond empty
                        query.respond();
                        break;
                }
        });

        server.on('after', function (query, bytes) {
                options.log.info({
                        query: query,
                        answers: query.answers()
                }, 'DNS query');
        });

        server.start = function start(callback) {
                server.on('listening', function () {
                        options.log.info({
                                host: options.host,
                                port: options.port
                        }, 'DNS service started');

                        if (typeof (callback) === 'function')
                                callback();
                });
                server.listen(options.port, options.host);
        };

        server.stop = function stop(callback) {
                server.close(callback);
        };

        return (server);
}

///--- Exports

module.exports = {

        createServer: createServer

};<|endoftext|>


<commit_before>var Utils     = require("./../utils")
  , DataTypes = require('./../data-types')
  , Helpers   = require('./helpers')

var HasManySingleLinked = require("./has-many-single-linked")
  , HasManyMultiLinked  = require("./has-many-double-linked")

module.exports = (function() {
  var HasMany = function(srcDAO, targetDAO, options) {
    this.associationType = 'HasMany'
    this.source = srcDAO
    this.target = targetDAO
    this.options = options
    this.useJunctionTable = this.options.useJunctionTable === undefined ? true : this.options.useJunctionTable
    this.isSelfAssociation = (this.source.tableName === this.target.tableName)
    this.hasJoinTableModel = !!this.options.joinTableModel

    var combinedTableName;
    if (this.hasJoinTableModel) {
      combinedTableName = this.options.joinTableModel.tableName
    } else if (this.options.joinTableName) {
      combinedTableName = this.options.joinTableName
    } else {
      combinedTableName = Utils.combineTableNames(
        this.source.tableName,
        this.isSelfAssociation ? (this.options.as || this.target.tableName) : this.target.tableName
      )
    }
    this.options.tableName = this.associationAccessor = this.combinedName = combinedTableName

    var as = (this.options.as || Utils.pluralize(this.target.tableName, this.target.options.language))

    this.accessors = {
      get: Utils._.camelize('get_' + as),
      set: Utils._.camelize('set_' + as),
      add: Utils._.camelize(Utils.singularize('add_' + as, this.target.options.language)),
      remove: Utils._.camelize(Utils.singularize('remove_' + as, this.target.options.language)),
      hasSingle: Utils._.camelize(Utils.singularize('has_' + as, this.target.options.language)),
      hasAll: Utils._.camelize('has_' + as)
    }
  }

  // the id is in the target table
  // or in an extra table which connects two tables
  HasMany.prototype.injectAttributes = function() {
    var multiAssociation = this.target.associations.hasOwnProperty(this.associationAccessor)
    this.identifier = this.options.foreignKey || Utils._.underscoredIf(Utils.singularize(this.source.tableName, this.source.options.language) + "Id", this.options.underscored)

    // is there already a single sided association between the source and the target?
    // or is the association on the model itself?
    if ((this.isSelfAssociation && this.useJunctionTable) || multiAssociation) {
      // remove the obsolete association identifier from the source
      if (this.isSelfAssociation) {
        this.foreignIdentifier = Utils._.underscoredIf((this.options.as || this.target.tableName) + 'Id', this.options.underscored)
      } else {
        this.foreignIdentifier = this.target.associations[this.associationAccessor].identifier
        this.target.associations[this.associationAccessor].foreignIdentifier = this.identifier
        delete this.source.rawAttributes[this.foreignIdentifier]
        delete this.target.associations[this.associationAccessor].source.rawAttributes[this.identifier]
      }

      // define a new model, which connects the models
      var combinedTableAttributes = {}
      var sourceKeys = Object.keys(this.source.primaryKeys);
      var sourceKeyType = ((!this.source.hasPrimaryKeys || sourceKeys.length !== 1) ? DataTypes.INTEGER : this.source.rawAttributes[sourceKeys[0]].type)
      var targetKeys = Object.keys(this.target.primaryKeys);
      var targetKeyType = ((!this.target.hasPrimaryKeys || targetKeys.length !== 1) ? DataTypes.INTEGER : this.target.rawAttributes[targetKeys[0]].type)
      combinedTableAttributes[this.identifier] = {type: sourceKeyType, primaryKey: true}
      combinedTableAttributes[this.foreignIdentifier] = {type: targetKeyType, primaryKey: true}

      if (this.hasJoinTableModel === true) {
        this.connectorDAO = this.options.joinTableModel
        this.connectorDAO.rawAttributes = Utils._.merge(this.connectorDAO.rawAttributes, combinedTableAttributes)

        // FIXME - Should we do this? Normally join tables does not have an id, and it is not needed,
        // but users might wonder what hapened if they defined the ID themselves
        delete this.connectorDAO.rawAttributes.id
        this.connectorDAO.init(this.connectorDAO.daoFactoryManager)
      } else {
        this.connectorDAO = this.source.daoFactoryManager.sequelize.define(this.combinedName, combinedTableAttributes, this.options)
      }

      if (!this.isSelfAssociation) {
        this.target.associations[this.associationAccessor].connectorDAO = this.connectorDAO
      }

      if (this.options.syncOnAssociation) {
        this.connectorDAO.sync()
      }
    } else {
      var newAttributes = {}
      newAttributes[this.identifier] = { type: this.options.keyType || DataTypes.INTEGER }
      Helpers.addForeignKeyConstraints(newAttributes[this.identifier], this.source, this.target, this.options)
      Utils._.defaults(this.target.rawAttributes, newAttributes)
    }

    // Sync attributes to DAO proto each time a new assoc is added
    this.target.DAO.prototype.attributes = Object.keys(this.target.DAO.prototype.rawAttributes);
    this.source.DAO.prototype.attributes = Object.keys(this.source.DAO.prototype.rawAttributes);

    return this
  }

  HasMany.prototype.injectGetter = function(obj) {
    var self = this

    obj[this.accessors.get] = function(options) {
      var Class = self.connectorDAO ? HasManyMultiLinked : HasManySingleLinked
      return new Class(self, this).injectGetter(options)
    }

    obj[this.accessors.hasAll] = function(objects) {
      var instance = this;
      var customEventEmitter = new Utils.CustomEventEmitter(function() {
        instance[self.accessors.get]()
        .error(function(err){ customEventEmitter.emit('error', err)})
        .success(function(associatedObjects) {
          customEventEmitter.emit('success',
            Utils._.all(objects, function(o) {
              return Utils._.any(associatedObjects, function(associatedObject) {
                return Utils._.all(associatedObject.identifiers, function(key, identifier) {
                  return o[identifier] == associatedObject[identifier];
                });
              })
            })
          )
        })
      })
      return customEventEmitter.run()
    }

    obj[this.accessors.hasSingle] = function(o) {
      var instance = this;
      var customEventEmitter = new Utils.CustomEventEmitter(function() {
        instance[self.accessors.get]()
        .error(function(err){ customEventEmitter.emit('error', err)})
        .success(function(associatedObjects) {
          customEventEmitter.emit('success',
            Utils._.any(associatedObjects, function(associatedObject) {
              return Utils._.all(associatedObject.identifiers, function(key, identifier) {
                return o[identifier] == associatedObject[identifier];
              });
            })
          )
        })
      })
      return customEventEmitter.run()
    }
    return this
  }

  HasMany.prototype.injectSetter = function(obj) {
    var self = this

    obj[this.accessors.set] = function(newAssociatedObjects, defaultAttributes) {
      if (newAssociatedObjects === null) {
        newAssociatedObjects = []
      }

      var instance = this

      // define the returned customEventEmitter, which will emit the success event once everything is done
      return new Utils.CustomEventEmitter(function(emitter) {
        instance[self.accessors.get]()
          .success(function(oldAssociatedObjects) {
            var Class = self.connectorDAO ? HasManyMultiLinked : HasManySingleLinked
            new Class(self, instance).injectSetter(emitter, oldAssociatedObjects, newAssociatedObjects, defaultAttributes)
          })
          .error(function(err) {
            emitter.emit('error', err)
          })
          .on('sql', function(sql) {
            emitter.emit('sql', sql)
          })
      }).run()
    }

    obj[this.accessors.add] = function(newAssociatedObject, additionalAttributes) {
      var instance = this
        , primaryKeys = Object.keys(newAssociatedObject.daoFactory.primaryKeys || {})
        , primaryKey = primaryKeys.length === 1 ? primaryKeys[0] : 'id'
        , where = {}

      where[newAssociatedObject.daoFactory.tableName+'.'+primaryKey] = newAssociatedObject[primaryKey]
      return new Utils.CustomEventEmitter(function(emitter) {
        instance[self.accessors.get]({ where: where })
          .error(function(err){ emitter.emit('error', err)})
          .success(function(currentAssociatedObjects) {
            if (currentAssociatedObjects.length === 0 || self.hasJoinTableModel === true) {
              var Class = self.connectorDAO ? HasManyMultiLinked : HasManySingleLinked
              new Class(self, instance).injectAdder(emitter, newAssociatedObject, additionalAttributes, !!currentAssociatedObjects.length)
            } else {
              emitter.emit('success', newAssociatedObject);
            }
          })
      }).run()
    }

    obj[this.accessors.remove] = function(oldAssociatedObject) {
      var instance = this
      var customEventEmitter = new Utils.CustomEventEmitter(function() {
        instance[self.accessors.get]().success(function(currentAssociatedObjects) {
          var newAssociations = []

          currentAssociatedObjects.forEach(function(association) {
            if (!Utils._.isEqual(oldAssociatedObject.identifiers, association.identifiers)) {
              newAssociations.push(association)
            }
          })

          instance[self.accessors.set](newAssociations)
            .success(function() { customEventEmitter.emit('success', null) })
            .error(function(err) { customEventEmitter.emit('error', err) })
        })
      })
      return customEventEmitter.run()
    }

    return this
  }

  return HasMany
})()
<commit_msg>Don't delete the pk of join table model<commit_after>var Utils     = require("./../utils")
  , DataTypes = require('./../data-types')
  , Helpers   = require('./helpers')

var HasManySingleLinked = require("./has-many-single-linked")
  , HasManyMultiLinked  = require("./has-many-double-linked")

module.exports = (function() {
  var HasMany = function(srcDAO, targetDAO, options) {
    this.associationType = 'HasMany'
    this.source = srcDAO
    this.target = targetDAO
    this.options = options
    this.useJunctionTable = this.options.useJunctionTable === undefined ? true : this.options.useJunctionTable
    this.isSelfAssociation = (this.source.tableName === this.target.tableName)
    this.hasJoinTableModel = !!this.options.joinTableModel

    var combinedTableName;
    if (this.hasJoinTableModel) {
      combinedTableName = this.options.joinTableModel.tableName
    } else if (this.options.joinTableName) {
      combinedTableName = this.options.joinTableName
    } else {
      combinedTableName = Utils.combineTableNames(
        this.source.tableName,
        this.isSelfAssociation ? (this.options.as || this.target.tableName) : this.target.tableName
      )
    }
    this.options.tableName = this.associationAccessor = this.combinedName = combinedTableName

    var as = (this.options.as || Utils.pluralize(this.target.tableName, this.target.options.language))

    this.accessors = {
      get: Utils._.camelize('get_' + as),
      set: Utils._.camelize('set_' + as),
      add: Utils._.camelize(Utils.singularize('add_' + as, this.target.options.language)),
      remove: Utils._.camelize(Utils.singularize('remove_' + as, this.target.options.language)),
      hasSingle: Utils._.camelize(Utils.singularize('has_' + as, this.target.options.language)),
      hasAll: Utils._.camelize('has_' + as)
    }
  }

  // the id is in the target table
  // or in an extra table which connects two tables
  HasMany.prototype.injectAttributes = function() {
    var multiAssociation = this.target.associations.hasOwnProperty(this.associationAccessor)
    this.identifier = this.options.foreignKey || Utils._.underscoredIf(Utils.singularize(this.source.tableName, this.source.options.language) + "Id", this.options.underscored)

    // is there already a single sided association between the source and the target?
    // or is the association on the model itself?
    if ((this.isSelfAssociation && this.useJunctionTable) || multiAssociation) {
      // remove the obsolete association identifier from the source
      if (this.isSelfAssociation) {
        this.foreignIdentifier = Utils._.underscoredIf((this.options.as || this.target.tableName) + 'Id', this.options.underscored)
      } else {
        this.foreignIdentifier = this.target.associations[this.associationAccessor].identifier
        this.target.associations[this.associationAccessor].foreignIdentifier = this.identifier
        delete this.source.rawAttributes[this.foreignIdentifier]
        delete this.target.associations[this.associationAccessor].source.rawAttributes[this.identifier]
      }

      // define a new model, which connects the models
      var combinedTableAttributes = {}
      var sourceKeys = Object.keys(this.source.primaryKeys);
      var sourceKeyType = ((!this.source.hasPrimaryKeys || sourceKeys.length !== 1) ? DataTypes.INTEGER : this.source.rawAttributes[sourceKeys[0]].type)
      var targetKeys = Object.keys(this.target.primaryKeys);
      var targetKeyType = ((!this.target.hasPrimaryKeys || targetKeys.length !== 1) ? DataTypes.INTEGER : this.target.rawAttributes[targetKeys[0]].type)
      combinedTableAttributes[this.identifier] = {type: sourceKeyType, primaryKey: true}
      combinedTableAttributes[this.foreignIdentifier] = {type: targetKeyType, primaryKey: true}

      if (this.hasJoinTableModel === true) {
        this.connectorDAO = this.options.joinTableModel
        this.connectorDAO.rawAttributes = Utils._.merge(this.connectorDAO.rawAttributes, combinedTableAttributes)
        this.connectorDAO.init(this.connectorDAO.daoFactoryManager)
      } else {
        this.connectorDAO = this.source.daoFactoryManager.sequelize.define(this.combinedName, combinedTableAttributes, this.options)
      }

      if (!this.isSelfAssociation) {
        this.target.associations[this.associationAccessor].connectorDAO = this.connectorDAO
      }

      if (this.options.syncOnAssociation) {
        this.connectorDAO.sync()
      }
    } else {
      var newAttributes = {}
      newAttributes[this.identifier] = { type: this.options.keyType || DataTypes.INTEGER }
      Helpers.addForeignKeyConstraints(newAttributes[this.identifier], this.source, this.target, this.options)
      Utils._.defaults(this.target.rawAttributes, newAttributes)
    }

    // Sync attributes to DAO proto each time a new assoc is added
    this.target.DAO.prototype.attributes = Object.keys(this.target.DAO.prototype.rawAttributes);
    this.source.DAO.prototype.attributes = Object.keys(this.source.DAO.prototype.rawAttributes);

    return this
  }

  HasMany.prototype.injectGetter = function(obj) {
    var self = this

    obj[this.accessors.get] = function(options) {
      var Class = self.connectorDAO ? HasManyMultiLinked : HasManySingleLinked
      return new Class(self, this).injectGetter(options)
    }

    obj[this.accessors.hasAll] = function(objects) {
      var instance = this;
      var customEventEmitter = new Utils.CustomEventEmitter(function() {
        instance[self.accessors.get]()
        .error(function(err){ customEventEmitter.emit('error', err)})
        .success(function(associatedObjects) {
          customEventEmitter.emit('success',
            Utils._.all(objects, function(o) {
              return Utils._.any(associatedObjects, function(associatedObject) {
                return Utils._.all(associatedObject.identifiers, function(key, identifier) {
                  return o[identifier] == associatedObject[identifier];
                });
              })
            })
          )
        })
      })
      return customEventEmitter.run()
    }

    obj[this.accessors.hasSingle] = function(o) {
      var instance = this;
      var customEventEmitter = new Utils.CustomEventEmitter(function() {
        instance[self.accessors.get]()
        .error(function(err){ customEventEmitter.emit('error', err)})
        .success(function(associatedObjects) {
          customEventEmitter.emit('success',
            Utils._.any(associatedObjects, function(associatedObject) {
              return Utils._.all(associatedObject.identifiers, function(key, identifier) {
                return o[identifier] == associatedObject[identifier];
              });
            })
          )
        })
      })
      return customEventEmitter.run()
    }
    return this
  }

  HasMany.prototype.injectSetter = function(obj) {
    var self = this

    obj[this.accessors.set] = function(newAssociatedObjects, defaultAttributes) {
      if (newAssociatedObjects === null) {
        newAssociatedObjects = []
      }

      var instance = this

      // define the returned customEventEmitter, which will emit the success event once everything is done
      return new Utils.CustomEventEmitter(function(emitter) {
        instance[self.accessors.get]()
          .success(function(oldAssociatedObjects) {
            var Class = self.connectorDAO ? HasManyMultiLinked : HasManySingleLinked
            new Class(self, instance).injectSetter(emitter, oldAssociatedObjects, newAssociatedObjects, defaultAttributes)
          })
          .error(function(err) {
            emitter.emit('error', err)
          })
          .on('sql', function(sql) {
            emitter.emit('sql', sql)
          })
      }).run()
    }

    obj[this.accessors.add] = function(newAssociatedObject, additionalAttributes) {
      var instance = this
        , primaryKeys = Object.keys(newAssociatedObject.daoFactory.primaryKeys || {})
        , primaryKey = primaryKeys.length === 1 ? primaryKeys[0] : 'id'
        , where = {}

      where[newAssociatedObject.daoFactory.tableName+'.'+primaryKey] = newAssociatedObject[primaryKey]
      return new Utils.CustomEventEmitter(function(emitter) {
        instance[self.accessors.get]({ where: where })
          .error(function(err){ emitter.emit('error', err)})
          .success(function(currentAssociatedObjects) {
            if (currentAssociatedObjects.length === 0 || self.hasJoinTableModel === true) {
              var Class = self.connectorDAO ? HasManyMultiLinked : HasManySingleLinked
              new Class(self, instance).injectAdder(emitter, newAssociatedObject, additionalAttributes, !!currentAssociatedObjects.length)
            } else {
              emitter.emit('success', newAssociatedObject);
            }
          })
      }).run()
    }

    obj[this.accessors.remove] = function(oldAssociatedObject) {
      var instance = this
      var customEventEmitter = new Utils.CustomEventEmitter(function() {
        instance[self.accessors.get]().success(function(currentAssociatedObjects) {
          var newAssociations = []

          currentAssociatedObjects.forEach(function(association) {
            if (!Utils._.isEqual(oldAssociatedObject.identifiers, association.identifiers)) {
              newAssociations.push(association)
            }
          })

          instance[self.accessors.set](newAssociations)
            .success(function() { customEventEmitter.emit('success', null) })
            .error(function(err) { customEventEmitter.emit('error', err) })
        })
      })
      return customEventEmitter.run()
    }

    return this
  }

  return HasMany
})()
<|endoftext|>


<commit_before>var phantom = require('phantom')
  , http = require('http')
  , url = require('url')
  , _ = require('lodash');

var DEFAULT_PHANTOM_ARGUMENTS = ['--load-images=false', '--ignore-ssl-errors=true'];

var DEFAULT_PHANTOM_BASE_PORT = 12300;

var PAGE_DONE_CHECK_TIMEOUT = process.env.PAGE_DONE_CHECK_TIMEOUT || 50;

var RESOURCE_DOWNLOAD_TIMEOUT = process.env.RESOURCE_DOWNLOAD_TIMEOUT || 10 * 1000;

var WAIT_AFTER_LAST_REQUEST = process.env.WAIT_AFTER_LAST_REQUEST || 500;

var JS_CHECK_TIMEOUT = process.env.JS_CHECK_TIMEOUT || 50;

var JS_TIMEOUT = process.env.JS_TIMEOUT || 10 * 1000;

var EVALUATE_JAVASCRIPT_CHECK_TIMEOUT = process.env.EVALUATE_JAVASCRIPT_CHECK_TIMEOUT || 50;

// Normalizes unimportant differences in URLs - e.g. ensures
// http://google.com/ and http://google.com normalize to the same string
function normalizeUrl(u) {
    return url.format(url.parse(u, true));
}

var server = exports = module.exports = {};

server.init = function(options) {
    this.plugins = this.plugins || [];
    this.options = options;

    return this;
};

server.start = function() {
    if(!this.options.isMaster) {
        this.createPhantom();
    }
};

server.use = function(plugin) {
    this.plugins.push(plugin);
    if (typeof plugin.init === 'function') plugin.init(this);
};

server._pluginEvent = function(methodName, args, callback) {
    var _this = this
      , index = 0
      , next;

    next = function() {
        var layer = _this.plugins[index++];
        if (!layer) return callback();

        var method = layer[methodName];

        if (method) {
            method.apply(layer, args);
        } else {
            next();
        }
    }

    args.push(next);
    next();
};

server.createPhantom = function() {
    var _this = this;
    console.log('starting phantom')

    var port = this.options.phantomBasePort || DEFAULT_PHANTOM_BASE_PORT;
    phantom.create( this.options.phantomArguments || DEFAULT_PHANTOM_ARGUMENTS, {
        binary: require('phantomjs').path,
        port: port + this.options.worker.id + 1,
        onExit: function() {
            _this.phantom = null;
            console.log('phantom crashed, restarting...')
            process.nextTick(_.bind(_this.createPhantom, _this));
        }
    }, _.bind(this.onPhantomCreate, this));
};

server.onPhantomCreate = function(phantom) {
    console.log('started phantom')
    this.phantom = phantom;
    this.phantom.id = Math.random().toString(36);
};

// Gets the URL to prerender from a request, stripping out unnecessary parts
function getUrl(req) {
    var decodedUrl
      , parts;

    try {
        decodedUrl = decodeURIComponent(req.url);
    } catch (e) {
        decodedUrl = req.url;
    }

    parts = url.parse(decodedUrl, true);

    // Remove the _escaped_fragment_ query parameter
    if (parts.query.hasOwnProperty('_escaped_fragment_')) {
        if(parts.query['_escaped_fragment_']) parts.hash = '#!' + parts.query['_escaped_fragment_'];
        delete parts.query['_escaped_fragment_'];
        delete parts.search;
    }

    var newUrl = url.format(parts);
    if(newUrl[0] == '/') newUrl = newUrl.substr(1);
    return newUrl;
};

server.onRequest = function(req, res) {
    var _this = this;

    // Create a partial out of the _send method for the convenience of plugins
    res.send = _.bind(this._send, this, req, res);

    req.prerender = {
        url: getUrl(req),
        start: new Date()
    };

    console.log('getting', req.prerender.url);

    this._pluginEvent("beforePhantomRequest", [req, res], function() {
        _this.createPage(req, res);
    });
};

server.createPage = function(req, res) {
    var _this = this;

    if(!this.phantom) {
        setTimeout(function(){
            _this.createPage(req, res);
        }, 50);
    } else {
        req.prerender.phantomId = this.phantom.id;
        this.phantom.createPage(function(page){
            req.prerender.page = page;
            _this.onPhantomPageCreate(req, res);
        });
    }
};

server.onPhantomPageCreate = function(req, res) {
    var _this = this;

    req.prerender.stage = 0;
    req.prerender.pendingRequests = 1;

    // Listen for updates on resource downloads
    req.prerender.page.set('onResourceRequested', _.bind(_this.onResourceRequested, _this, req, res));
    req.prerender.page.set('onResourceReceived', _.bind(_this.onResourceReceived, _this, req, res));
    req.prerender.page.set('onResourceError', _.bind(_this.onResourceError, _this, req, res));
    req.prerender.page.set('onResourceTimeout', _.bind(_this.onResourceTimeout, _this, req, res));

    req.prerender.page.get('settings.userAgent', function(userAgent) {
        req.prerender.page.set('settings.userAgent', userAgent + ' Prerender (+https://github.com/prerender/prerender)');

        // Fire off a middleware event, then download all of the assets
        _this._pluginEvent("onPhantomPageCreate", [req, res], function() {
            req.prerender.downloadStarted = req.prerender.lastResourceReceived = new Date();

            req.prerender.downloadChecker = setInterval(function() {
                _this.checkIfPageIsDoneLoading(req, res, req.prerender.status === 'fail');
            }, _this.options.pageDoneCheckTimeout || PAGE_DONE_CHECK_TIMEOUT);

            req.prerender.page.open(encodeURI(req.prerender.url.replace(/%20/g, ' ')), function(status) {
                req.prerender.status = status;
            });
        });
    });
};

// Increment the number of pending requests left to download when a new
// resource is requested
server.onResourceRequested = function (req, res, requestData) {
    req.prerender.pendingRequests++;
};

// Decrement the number of pending requests left to download after a resource
// is downloaded
server.onResourceReceived = function (req, res, response) {
    req.prerender.lastResourceReceived = new Date;

    //sometimes on redirects, phantomjs doesnt fire the 'end' stage of the original request, so we need to check it here
    if(normalizeUrl(req.prerender.url) === normalizeUrl(response.url) && response.status >= 300 && response.status <= 399) {
        if (response.redirectURL) req.prerender.redirectURL = response.redirectURL;

        req.prerender.statusCode = response.status;
        //force the response now
        return this.checkIfPageIsDoneLoading(req, res, true);
    }

    if ('end' === response.stage) {
        req.prerender.pendingRequests--;

        if (normalizeUrl(req.prerender.url) === normalizeUrl(response.url)) {
            req.prerender.pendingRequests--;

            req.prerender.statusCode = response.status;
        }
    }
};

// Decrement the number of pending requests to download when there's an error
// fetching a resource
server.onResourceError = function(req, res, resourceError) {
    req.prerender.pendingRequests--;
};

// Decrement the number of pending requests to download when there's a timeout
// fetching a resource
server.onResourceTimeout = function(req, res, request) {
    req.prerender.pendingRequests--;
};

// Called occasionally to check if a page is completely loaded
server.checkIfPageIsDoneLoading = function(req, res, force) {
    var timedOut = new Date().getTime() - req.prerender.downloadStarted.getTime() > (this.options.resourceDownloadTimeout || RESOURCE_DOWNLOAD_TIMEOUT)
      , timeSinceLastRequest = new Date().getTime() - req.prerender.lastResourceReceived.getTime();

    // Check against the current stage to make sure we don't finish more than
    // once, and check against a bunch of states that would signal finish - if
    // resource downloads have timed out, if the page has errored out, or if
    // there are no pending requests left
    if(req.prerender.stage < 1 && (force || (req.prerender.pendingRequests <= 0 && timeSinceLastRequest > (this.options.waitAfterLastRequest || WAIT_AFTER_LAST_REQUEST)) || timedOut)) {
        req.prerender.stage = 1;
        clearInterval(req.prerender.downloadTimeout);
        req.prerender.downloadTimeout = null;

        if(req.prerender.statusCode && req.prerender.statusCode >= 300 && req.prerender.statusCode <= 399) {
            // Finish up if we got a redirect status code
            res.send(req.prerender.statusCode);
        } else {
            // Now evaluate the javascript
            req.prerender.timeoutChecker = setInterval(_.bind(this.checkIfJavascriptTimedOut, this, req, res), (this.options.jsCheckTimeout || JS_CHECK_TIMEOUT));
            this.evaluateJavascriptOnPage(req, res);
        }
    }
};

// Checks to see if the execution of javascript has timed out
server.checkIfJavascriptTimedOut = function(req, res) {

    var timeout = new Date().getTime() - req.prerender.downloadStarted.getTime() > (this.options.jsTimeout || JS_TIMEOUT);

    if (timeout && req.prerender.lastJavascriptExecution && new Date().getTime() - req.prerender.lastJavascriptExecution.getTime() < 2000) {
        console.log('Timed out. Sending request with HTML on the page');
        clearInterval(req.prerender.timeoutChecker);
        req.prerender.timeoutChecker = null;

        this.onPageEvaluate(req, res);
    } else if (timeout && req.prerender.stage < 2) {
        console.log('Experiencing infinite javascript loop. Killing phantomjs...');
        res.send(504, {abort: true});
    }
};

// Evaluates the javascript on the page
server.evaluateJavascriptOnPage = function(req, res) {
    var _this = this;

    if(req.prerender.stage >= 2) return;

    req.prerender.page.evaluate(this.javascriptToExecuteOnPage, function(obj) {
        // Update the evaluated HTML
        req.prerender.documentHTML = obj.html;
        req.prerender.lastJavascriptExecution = new Date;

        if(!obj.shouldWaitForPrerenderReady || (obj.shouldWaitForPrerenderReady && obj.prerenderReady)) {
            clearInterval(req.prerender.timeoutChecker);
            req.prerender.timeoutChecker = null;

            _this.onPageEvaluate(req, res);
        } else {
            setTimeout(_.bind(_this.evaluateJavascriptOnPage, _this, req, res), (this.evaluateJavascriptCheckTimout || EVALUATE_JAVASCRIPT_CHECK_TIMEOUT));
        }
    });
};

// Fetches the html on the page
server.javascriptToExecuteOnPage = function() {
    try {
        var doctype = ''
          , html = document && document.getElementsByTagName('html');

        if(document.doctype) {
            doctype = "<!DOCTYPE "
                 + document.doctype.name
                 + (document.doctype.publicId ? ' PUBLIC "' + document.doctype.publicId + '"' : '')
                 + (!document.doctype.publicId && document.doctype.systemId ? ' SYSTEM' : '')
                 + (document.doctype.systemId ? ' "' + document.doctype.systemId + '"' : '')
                 + '>';
        }

        if (html && html[0]) {
            return {
                html: doctype + html[0].outerHTML,
                shouldWaitForPrerenderReady: typeof window.prerenderReady === 'boolean',
                prerenderReady: window.prerenderReady
            };
        }

    } catch (e) { }

    return  {
        html: '',
        shouldWaitForPrerenderReady: false,
        prerenderReady: window.prerenderReady
    };
};

// Called when we're done evaluating the javascript on the page
server.onPageEvaluate = function(req, res) {
    var _this = this;

    if(req.prerender.stage >= 2) return;

    req.prerender.stage = 2;

    if (!req.prerender.documentHTML) {
        res.send(req.prerender.statusCode || 404);
    } else {
        this._pluginEvent("afterPhantomRequest", [req, res], function() {
            res.send(req.prerender.statusCode || 200);
        });
    }
};

server._send = function(req, res, statusCode, options) {
    var _this = this;

    if(req.prerender.page) {
        req.prerender.page.close();
        req.prerender.page = null;
    }
    req.prerender.stage = 2;

    req.prerender.documentHTML = options || req.prerender.documentHTML;
    req.prerender.statusCode = statusCode || req.prerender.statusCode;

    if (options && typeof options === 'object' && !Buffer.isBuffer(options)) {
        req.prerender.documentHTML = options.documentHTML;
        req.prerender.redirectURL = options.redirectURL;
    }

    this._pluginEvent("beforeSend", [req, res], function() {
        if (req.prerender.redirectURL) {
            res.setHeader('Location', req.prerender.redirectURL);
        }

        if (req.prerender.documentHTML) {
            if(Buffer.isBuffer(req.prerender.documentHTML)) {
                res.setHeader('Content-Length', req.prerender.documentHTML.length);
            } else {
                res.setHeader('Content-Length', Buffer.byteLength(req.prerender.documentHTML, 'utf8'));
            }
        }

        res.writeHead(req.prerender.statusCode || 504, {
            'Content-Type': 'text/html;charset=UTF-8'
        });

        if (req.prerender.documentHTML) res.write(req.prerender.documentHTML);

        res.end();

        var ms = new Date().getTime() - req.prerender.start.getTime();
        console.log('got', req.prerender.statusCode, 'in', ms + 'ms', 'for', req.prerender.url);

        if(options && options.abort) {

            process.nextTick(function(){ process.exit(0) });
           //  try {
           //     //not happy with this... but when phantomjs is hanging, it can't exit any normal way
           //     console.log('Experiencing infinite javascript loop. Killing phantomjs...');
           //     require('child_process').spawn('pkill', ['phantomjs']);
           //     _this.phantom = null;
           // } catch(e) {
           //     console.log('Error killing phantomjs from javascript infinite loop:', e);
           // }
        }
    });
};

<commit_msg>Revert "making sure we are using different phantom processes with how cluster works with ports"<commit_after>var phantom = require('phantom')
  , http = require('http')
  , url = require('url')
  , _ = require('lodash');

var DEFAULT_PHANTOM_ARGUMENTS = ['--load-images=false', '--ignore-ssl-errors=true'];

var PAGE_DONE_CHECK_TIMEOUT = process.env.PAGE_DONE_CHECK_TIMEOUT || 50;

var RESOURCE_DOWNLOAD_TIMEOUT = process.env.RESOURCE_DOWNLOAD_TIMEOUT || 10 * 1000;

var WAIT_AFTER_LAST_REQUEST = process.env.WAIT_AFTER_LAST_REQUEST || 500;

var JS_CHECK_TIMEOUT = process.env.JS_CHECK_TIMEOUT || 50;

var JS_TIMEOUT = process.env.JS_TIMEOUT || 10 * 1000;

var EVALUATE_JAVASCRIPT_CHECK_TIMEOUT = process.env.EVALUATE_JAVASCRIPT_CHECK_TIMEOUT || 50;

// Normalizes unimportant differences in URLs - e.g. ensures
// http://google.com/ and http://google.com normalize to the same string
function normalizeUrl(u) {
    return url.format(url.parse(u, true));
}

var server = exports = module.exports = {};

server.init = function(options) {
    this.plugins = this.plugins || [];
    this.options = options;

    return this;
};

server.start = function() {
    if(!this.options.isMaster) {
        this.createPhantom();
    }
};

server.use = function(plugin) {
    this.plugins.push(plugin);
    if (typeof plugin.init === 'function') plugin.init(this);
};

server._pluginEvent = function(methodName, args, callback) {
    var _this = this
      , index = 0
      , next;

    next = function() {
        var layer = _this.plugins[index++];
        if (!layer) return callback();

        var method = layer[methodName];

        if (method) {
            method.apply(layer, args);
        } else {
            next();
        }
    }

    args.push(next);
    next();
};

server.createPhantom = function() {
    var _this = this;
    console.log('starting phantom')

    phantom.create( this.options.phantomArguments || DEFAULT_PHANTOM_ARGUMENTS, {
        binary: require('phantomjs').path,
        onExit: function() {
            _this.phantom = null;
            console.log('phantom crashed, restarting...')
            process.nextTick(_.bind(_this.createPhantom, _this));
        }
    }, _.bind(this.onPhantomCreate, this));
};

server.onPhantomCreate = function(phantom) {
    console.log('started phantom')
    this.phantom = phantom;
    this.phantom.id = Math.random().toString(36);
};

// Gets the URL to prerender from a request, stripping out unnecessary parts
function getUrl(req) {
    var decodedUrl
      , parts;

    try {
        decodedUrl = decodeURIComponent(req.url);
    } catch (e) {
        decodedUrl = req.url;
    }

    parts = url.parse(decodedUrl, true);

    // Remove the _escaped_fragment_ query parameter
    if (parts.query.hasOwnProperty('_escaped_fragment_')) {
        if(parts.query['_escaped_fragment_']) parts.hash = '#!' + parts.query['_escaped_fragment_'];
        delete parts.query['_escaped_fragment_'];
        delete parts.search;
    }

    var newUrl = url.format(parts);
    if(newUrl[0] == '/') newUrl = newUrl.substr(1);
    return newUrl;
};

server.onRequest = function(req, res) {
    var _this = this;

    // Create a partial out of the _send method for the convenience of plugins
    res.send = _.bind(this._send, this, req, res);

    req.prerender = {
        url: getUrl(req),
        start: new Date()
    };

    console.log('getting', req.prerender.url);

    this._pluginEvent("beforePhantomRequest", [req, res], function() {
        _this.createPage(req, res);
    });
};

server.createPage = function(req, res) {
    var _this = this;

    if(!this.phantom) {
        setTimeout(function(){
            _this.createPage(req, res);
        }, 50);
    } else {
        req.prerender.phantomId = this.phantom.id;
        this.phantom.createPage(function(page){
            req.prerender.page = page;
            _this.onPhantomPageCreate(req, res);
        });
    }
};

server.onPhantomPageCreate = function(req, res) {
    var _this = this;

    req.prerender.stage = 0;
    req.prerender.pendingRequests = 1;

    // Listen for updates on resource downloads
    req.prerender.page.set('onResourceRequested', _.bind(_this.onResourceRequested, _this, req, res));
    req.prerender.page.set('onResourceReceived', _.bind(_this.onResourceReceived, _this, req, res));
    req.prerender.page.set('onResourceError', _.bind(_this.onResourceError, _this, req, res));
    req.prerender.page.set('onResourceTimeout', _.bind(_this.onResourceTimeout, _this, req, res));

    req.prerender.page.get('settings.userAgent', function(userAgent) {
        req.prerender.page.set('settings.userAgent', userAgent + ' Prerender (+https://github.com/prerender/prerender)');

        // Fire off a middleware event, then download all of the assets
        _this._pluginEvent("onPhantomPageCreate", [req, res], function() {
            req.prerender.downloadStarted = req.prerender.lastResourceReceived = new Date();

            req.prerender.downloadChecker = setInterval(function() {
                _this.checkIfPageIsDoneLoading(req, res, req.prerender.status === 'fail');
            }, _this.options.pageDoneCheckTimeout || PAGE_DONE_CHECK_TIMEOUT);

            req.prerender.page.open(encodeURI(req.prerender.url.replace(/%20/g, ' ')), function(status) {
                req.prerender.status = status;
            });
        });
    });
};

// Increment the number of pending requests left to download when a new
// resource is requested
server.onResourceRequested = function (req, res, requestData) {
    req.prerender.pendingRequests++;
};

// Decrement the number of pending requests left to download after a resource
// is downloaded
server.onResourceReceived = function (req, res, response) {
    req.prerender.lastResourceReceived = new Date;

    //sometimes on redirects, phantomjs doesnt fire the 'end' stage of the original request, so we need to check it here
    if(normalizeUrl(req.prerender.url) === normalizeUrl(response.url) && response.status >= 300 && response.status <= 399) {
        if (response.redirectURL) req.prerender.redirectURL = response.redirectURL;

        req.prerender.statusCode = response.status;
        //force the response now
        return this.checkIfPageIsDoneLoading(req, res, true);
    }

    if ('end' === response.stage) {
        req.prerender.pendingRequests--;

        if (normalizeUrl(req.prerender.url) === normalizeUrl(response.url)) {
            req.prerender.pendingRequests--;

            req.prerender.statusCode = response.status;
        }
    }
};

// Decrement the number of pending requests to download when there's an error
// fetching a resource
server.onResourceError = function(req, res, resourceError) {
    req.prerender.pendingRequests--;
};

// Decrement the number of pending requests to download when there's a timeout
// fetching a resource
server.onResourceTimeout = function(req, res, request) {
    req.prerender.pendingRequests--;
};

// Called occasionally to check if a page is completely loaded
server.checkIfPageIsDoneLoading = function(req, res, force) {
    var timedOut = new Date().getTime() - req.prerender.downloadStarted.getTime() > (this.options.resourceDownloadTimeout || RESOURCE_DOWNLOAD_TIMEOUT)
      , timeSinceLastRequest = new Date().getTime() - req.prerender.lastResourceReceived.getTime();

    // Check against the current stage to make sure we don't finish more than
    // once, and check against a bunch of states that would signal finish - if
    // resource downloads have timed out, if the page has errored out, or if
    // there are no pending requests left
    if(req.prerender.stage < 1 && (force || (req.prerender.pendingRequests <= 0 && timeSinceLastRequest > (this.options.waitAfterLastRequest || WAIT_AFTER_LAST_REQUEST)) || timedOut)) {
        req.prerender.stage = 1;
        clearInterval(req.prerender.downloadTimeout);
        req.prerender.downloadTimeout = null;

        if(req.prerender.statusCode && req.prerender.statusCode >= 300 && req.prerender.statusCode <= 399) {
            // Finish up if we got a redirect status code
            res.send(req.prerender.statusCode);
        } else {
            // Now evaluate the javascript
            req.prerender.timeoutChecker = setInterval(_.bind(this.checkIfJavascriptTimedOut, this, req, res), (this.options.jsCheckTimeout || JS_CHECK_TIMEOUT));
            this.evaluateJavascriptOnPage(req, res);
        }
    }
};

// Checks to see if the execution of javascript has timed out
server.checkIfJavascriptTimedOut = function(req, res) {

    var timeout = new Date().getTime() - req.prerender.downloadStarted.getTime() > (this.options.jsTimeout || JS_TIMEOUT);

    if (timeout && req.prerender.lastJavascriptExecution && new Date().getTime() - req.prerender.lastJavascriptExecution.getTime() < 2000) {
        console.log('Timed out. Sending request with HTML on the page');
        clearInterval(req.prerender.timeoutChecker);
        req.prerender.timeoutChecker = null;

        this.onPageEvaluate(req, res);
    } else if (timeout && req.prerender.stage < 2) {
        console.log('Experiencing infinite javascript loop. Killing phantomjs...');
        res.send(504, {abort: true});
    }
};

// Evaluates the javascript on the page
server.evaluateJavascriptOnPage = function(req, res) {
    var _this = this;

    if(req.prerender.stage >= 2) return;

    req.prerender.page.evaluate(this.javascriptToExecuteOnPage, function(obj) {
        // Update the evaluated HTML
        req.prerender.documentHTML = obj.html;
        req.prerender.lastJavascriptExecution = new Date;

        if(!obj.shouldWaitForPrerenderReady || (obj.shouldWaitForPrerenderReady && obj.prerenderReady)) {
            clearInterval(req.prerender.timeoutChecker);
            req.prerender.timeoutChecker = null;

            _this.onPageEvaluate(req, res);
        } else {
            setTimeout(_.bind(_this.evaluateJavascriptOnPage, _this, req, res), (this.evaluateJavascriptCheckTimout || EVALUATE_JAVASCRIPT_CHECK_TIMEOUT));
        }
    });
};

// Fetches the html on the page
server.javascriptToExecuteOnPage = function() {
    try {
        var doctype = ''
          , html = document && document.getElementsByTagName('html');

        if(document.doctype) {
            doctype = "<!DOCTYPE "
                 + document.doctype.name
                 + (document.doctype.publicId ? ' PUBLIC "' + document.doctype.publicId + '"' : '')
                 + (!document.doctype.publicId && document.doctype.systemId ? ' SYSTEM' : '')
                 + (document.doctype.systemId ? ' "' + document.doctype.systemId + '"' : '')
                 + '>';
        }

        if (html && html[0]) {
            return {
                html: doctype + html[0].outerHTML,
                shouldWaitForPrerenderReady: typeof window.prerenderReady === 'boolean',
                prerenderReady: window.prerenderReady
            };
        }

    } catch (e) { }

    return  {
        html: '',
        shouldWaitForPrerenderReady: false,
        prerenderReady: window.prerenderReady
    };
};

// Called when we're done evaluating the javascript on the page
server.onPageEvaluate = function(req, res) {
    var _this = this;

    if(req.prerender.stage >= 2) return;

    req.prerender.stage = 2;

    if (!req.prerender.documentHTML) {
        res.send(req.prerender.statusCode || 404);
    } else {
        this._pluginEvent("afterPhantomRequest", [req, res], function() {
            res.send(req.prerender.statusCode || 200);
        });
    }
};

server._send = function(req, res, statusCode, options) {
    var _this = this;

    if(req.prerender.page) {
        req.prerender.page.close();
        req.prerender.page = null;
    }
    req.prerender.stage = 2;

    req.prerender.documentHTML = options || req.prerender.documentHTML;
    req.prerender.statusCode = statusCode || req.prerender.statusCode;

    if (options && typeof options === 'object' && !Buffer.isBuffer(options)) {
        req.prerender.documentHTML = options.documentHTML;
        req.prerender.redirectURL = options.redirectURL;
    }

    this._pluginEvent("beforeSend", [req, res], function() {
        if (req.prerender.redirectURL) {
            res.setHeader('Location', req.prerender.redirectURL);
        }

        if (req.prerender.documentHTML) {
            if(Buffer.isBuffer(req.prerender.documentHTML)) {
                res.setHeader('Content-Length', req.prerender.documentHTML.length);
            } else {
                res.setHeader('Content-Length', Buffer.byteLength(req.prerender.documentHTML, 'utf8'));
            }
        }

        res.writeHead(req.prerender.statusCode || 504, {
            'Content-Type': 'text/html;charset=UTF-8'
        });

        if (req.prerender.documentHTML) res.write(req.prerender.documentHTML);

        res.end();

        var ms = new Date().getTime() - req.prerender.start.getTime();
        console.log('got', req.prerender.statusCode, 'in', ms + 'ms', 'for', req.prerender.url);

        if(options && options.abort) {
            _this.options.worker.kill();
           //  try {
           //     //not happy with this... but when phantomjs is hanging, it can't exit any normal way
           //     console.log('Experiencing infinite javascript loop. Killing phantomjs...');
           //     require('child_process').spawn('pkill', ['phantomjs']);
           //     _this.phantom = null;
           // } catch(e) {
           //     console.log('Error killing phantomjs from javascript infinite loop:', e);
           // }
        }
    });
};

<|endoftext|>