<commit_before>//CONFIG===============================================

/* Uses the slack button feature to offer a real time bot to multiple teams */
var Botkit = require('botkit');
var request = require('request');
var mongoUri = process.env.MONGOLAB_URI || 'mongodb://localhost/botkit_express_demo'
var botkit_mongo_storage = require('../../config/botkit-storage-mongoose')({mongoUri: mongoUri})

if (!process.env.SLACK_ID || !process.env.SLACK_SECRET || !process.env.PORT) {
    console.log('Error: Specify SLACK_ID SLACK_SECRET and PORT in environment');
    process.exit(1);
}

var controller = Botkit.slackbot({
    storage: botkit_mongo_storage
});

exports.controller = controller

//CONNECTION FUNCTIONS=====================================================
exports.connect = function(team_config) {
    var bot = controller.spawn(team_config);
    controller.trigger('create_bot', [bot, team_config]);
}

// just a simple way to make sure we don't
// connect to the RTM twice for the same team
var _bots = {};

function trackBot(bot) {
    _bots[bot.config.token] = bot;
}

exports.getExistingBot = function(token) {
    return _bots[token];
}

controller.on('create_bot',function(bot,team) {

    if (_bots[bot.config.token]) {
    // already online! do nothing.
        console.log("already online! do nothing.")
    }
    else {
        bot.startRTM(function(err) {

            if (!err) {
                trackBot(bot);

                console.log("RTM ok");

                bot.api.channels.list({
                    token: bot.config.token
                }, function(err, result) {
                    if(!err) {
                        team.channels = []
                        if (result.channels) {
                            for (var i = 0; i < result.channels.length; i++) {
                                team.channels.push({
                                    id: result.channels[i].id,
                                    name: result.channels[i].name
                                })
                            }
                        }
                        controller.saveTeam(team, function(err, id) {
                            if (err) {
                                console.log("Error saving team")
                            }
                            else {
                                console.log("Team " + team.name + " saved")
                            }
                        });
                    }
                });

                if (team.incoming_webhook) {
                    var options = {
                        headers: {
                            'Content-type': 'application/json'
                        },
                        url: team.incoming_webhook.url,
                        method: 'POST',
                        body: '{"username": "ghost-bot", "text": "BOO!", "icon_emoji": ":ghost:"}'
                    };

                    function callback(error, response, body) {
                        console.log('FIND ME!!!!!');
                        console.log(response.statusCode);
                        console.log(body);
                        if (!error && response.statusCode == 200) {
                            console.log(body);
                        }
                    }

                    request(options, callback);
                }
            }
            else{
                console.log("RTM failed");
            }

            bot.startPrivateConversation({user: team.createdBy}, function(err, convo) {
                //console.log(JSON.stringify(convo));
                if (err) {
                    console.log(err);
                } else {
                    convo.say('Hi! I\'m Johnny-Three, Human / VSTS relations');
                    convo.say('To start receiving your VSTS notifications please visit http://johnny-three.herokuapp.com/admin/' + team.id);
                }
            });

        });
    }
});

//REACTIONS TO EVENTS==========================================================

// Handle events related to the websocket connection to Slack
controller.on('rtm_open',function(bot) {
    console.log('** The RTM api just connected!');
});

controller.on('rtm_close',function(bot) {
    console.log('** The RTM api just closed');
// you may want to attempt to re-open
});

//DIALOG ======================================================================

controller.hears('hello','direct_message',function(bot,message) {
    bot.reply(message,'Hello!');
});

controller.hears('^stop','direct_message',function(bot,message) {
    bot.reply(message,'Goodbye');
    bot.rtm.close();
});

controller.on('direct_message,mention,direct_mention',function(bot,message) {
    bot.api.reactions.add({
        timestamp: message.ts,
        channel: message.channel,
        name: 'robot_face',
    }, function(err) {
        if (err) { console.log(err) }
        bot.reply(message,'I heard you loud and clear boss.');
    });
});

controller.storage.teams.all(function(err,teams) {

    console.log(teams)

    if (err) {
        throw new Error(err);
    }

// connect all teams with bots up to slack!
    for (var t  in teams) {
        if (teams[t].bot) {
            var bot = controller.spawn(teams[t]).startRTM(function(err, bot) {
                if (err) {
                    console.log('Error connecting bot to Slack:',err);
                } else {
                    trackBot(bot);
                }
            });
        }
    }

});
<commit_msg>changing things<commit_after>//CONFIG===============================================

/* Uses the slack button feature to offer a real time bot to multiple teams */
var Botkit = require('botkit');
var request = require('request');
var mongoUri = process.env.MONGOLAB_URI || 'mongodb://localhost/botkit_express_demo'
var botkit_mongo_storage = require('../../config/botkit-storage-mongoose')({mongoUri: mongoUri})

if (!process.env.SLACK_ID || !process.env.SLACK_SECRET || !process.env.PORT) {
    console.log('Error: Specify SLACK_ID SLACK_SECRET and PORT in environment');
    process.exit(1);
}

var controller = Botkit.slackbot({
    storage: botkit_mongo_storage,
    interactive_replies: true
});

controller.setupWebserver(process.env.port, function(err,webserver) {
  controller.createWebhookEndpoints(controller.webserver);
});

exports.controller = controller

//CONNECTION FUNCTIONS=====================================================
exports.connect = function(team_config) {
    var bot = controller.spawn(team_config);
    controller.trigger('create_bot', [bot, team_config]);
}

// just a simple way to make sure we don't
// connect to the RTM twice for the same team
var _bots = {};

function trackBot(bot) {
    _bots[bot.config.token] = bot;
}

exports.getExistingBot = function(token) {
    return _bots[token];
}

controller.on('create_bot',function(bot,team) {

    if (_bots[bot.config.token]) {
    // already online! do nothing.
        console.log("already online! do nothing.")
    }
    else {
        bot.startRTM(function(err) {

            if (!err) {
                trackBot(bot);

                console.log("RTM ok");

                bot.api.channels.list({
                    token: bot.config.token
                }, function(err, result) {
                    if(!err) {
                        team.channels = []
                        if (result.channels) {
                            for (var i = 0; i < result.channels.length; i++) {
                                team.channels.push({
                                    id: result.channels[i].id,
                                    name: result.channels[i].name
                                })
                            }
                        }
                        controller.saveTeam(team, function(err, id) {
                            if (err) {
                                console.log("Error saving team")
                            }
                            else {
                                console.log("Team " + team.name + " saved")
                            }
                        });
                    }
                });
            }
            else{
                console.log("RTM failed");
            }

            bot.startPrivateConversation({user: team.createdBy}, function(err, convo) {
                //console.log(JSON.stringify(convo));
                if (err) {
                    console.log(err);
                } else {
                    convo.say('Hi! I\'m Johnny-Three, Human / VSTS relations');
                    convo.say('To start receiving your VSTS notifications please visit http://johnny-three.herokuapp.com/admin/' + team.id);
                    convo.say('To being sharing your messages with other teams please ')
                }
            });

        });
    }
});

//REACTIONS TO EVENTS==========================================================

// Handle events related to the websocket connection to Slack
controller.on('rtm_open',function(bot) {
    console.log('** The RTM api just connected!');
});

controller.on('rtm_close',function(bot) {
    console.log('** The RTM api just closed');
// you may want to attempt to re-open
});

//DIALOG ======================================================================

controller.storage.teams.all(function(err,teams) {

    console.log(teams)

    if (err) {
        throw new Error(err);
    }

// connect all teams with bots up to slack!
    for (var t  in teams) {
        if (teams[t].bot) {
            var bot = controller.spawn(teams[t]).startRTM(function(err, bot) {
                if (err) {
                    console.log('Error connecting bot to Slack:',err);
                } else {
                    trackBot(bot);
                }
            });
        }
    }

});
<|endoftext|>


<commit_before>BASE_URL = 'https://portal.rainforestqa.com';

var manifest = chrome.runtime.getManifest();
console.log("Starting:", manifest.name, manifest.version, BASE_URL);

// Start disabled: require the tester to enable if they want to
// work when the browser starts
_checking_active = false;
info_hash = {
  tester_state: 'active',
  work_available_endpoint: BASE_URL + '/api/1/testers/',
  email: '',
  id: '',
  version: manifest.version
};

// Set polling interval in milliseconds (note, this is rate limted,
// so if you change agressively, it will error)
default_check_for_work_interval = 8 * 1000;
check_for_work_interval = default_check_for_work_interval;

//
// Load the initial id value from storage
//
chrome.storage.sync.get("worker_uuid", function(data) {
  // Notify that we saved.
  console.log("Data loaded", data);
  if (data['worker_uuid'] != undefined) {
    info_hash["uuid"] = data["worker_uuid"];
    console.log("Updated info hash:", info_hash);
    set_checking(_checking_active);
  } else {
    sync_tab_make_new();
  }
});

//
// Load the initial api endpoint value from storage
//
chrome.storage.sync.get("work_available_endpoint", function(data) {
  // Notify that we saved.
  console.log("Data loaded", data);
  if (data['work_available_endpoint'] != undefined) {
    info_hash["work_available_endpoint"] = data["work_available_endpoint"];
    console.log("Updated info hash:", info_hash);
    set_checking(_checking_active);
  } else {
    sync_tab_make_new();
  }
});

// Handle the icon being clicked
//
// this enables or disables checking for new work
//
chrome.browserAction.onClicked.addListener(function (event) {
  _checking_active = !_checking_active;
  console.log("browserAction", event);
  console.log("Checking is now:", _checking_active);
  set_checking(_checking_active);
});

//
// Handle data coming from the main site
//
chrome.runtime.onMessageExternal.addListener(function(request, sender, sendResponse) {
  if (request.data) {
    console.log("incoming data", request);

    if (request.data['worker_uuid'] && request.data['work_available_endpoint']) {
      info_hash["uuid"] = request.data["worker_uuid"];
      info_hash["work_available_endpoint"] = request.data["work_available_endpoint"];
      set_checking(_checking_active);
      sendResponse({ok: true});

      chrome.storage.sync.set(
        {
          'worker_uuid': request.data["worker_uuid"],
          'work_available_endpoint': request.data["work_available_endpoint"]
        },
        function() {
          // Notify that we saved.
          console.log('Worker ID and API endpoint saved');
        }
      );
    }
  }
});

//
// Set checking state
//
function set_checking(state) {
  if (!state) {
    chrome.browserAction.setBadgeBackgroundColor({color:[255, 0, 0, 230]});
    chrome.browserAction.setBadgeText({text:"OFF"});
  } else {
    check_for_work();
  }
}

//
// Open or focus the main work tab
//
work_tab = null
function open_or_focus_tab(url) {
  if (work_tab === null && typeof work_tab === "object" ) {
    work_tab_make_new(url);
  } else {
    refresh_tab_info();
  }
}

//
// Make sure the work tab is open and in focus
//
function refresh_tab_info() {
  chrome.tabs.get(work_tab.id, function (t) {
    if (chrome.runtime.lastError) {
      work_tab = null;
      console.log(chrome.runtime.lastError.message);
    } else {
      work_tab = t;
      console.log('checking work tab', work_tab);

      // force selection
      if (!work_tab.selected) {
        chrome.tabs.update(work_tab.id, {selected: true});
      }
    }
  });
}

//
// Open a new work tab
//
function work_tab_make_new(url) {
  // make a new tab
  chrome.tabs.create({ url: url }, function (t) {
    console.log('made new work tab', t);
    work_tab = t;
  });
}

//
// Open a sync tab
//
function sync_tab_make_new() {
  // make a new tab
  chrome.tabs.create({ url: BASE_URL + '/profile?version=' + manifest.version }, function (t) {
    console.log('made new sync tab', t);
  });
}

//
// Poll for new work
//
function check_for_work() {
  if (info_hash["uuid"] == "" || info_hash["uuid"] === undefined) {
    console.log("Info hash not set", info_hash);
    return false;
  }

  var xhr = new XMLHttpRequest();
  xhr.open("GET", info_hash["work_available_endpoint"] + info_hash["uuid"] + "/work_available?info=" + JSON.stringify(info_hash), true);
  xhr.onreadystatechange = function() {
    if (xhr.readyState == 4 && _checking_active) {
      var resp = JSON.parse(xhr.responseText);
      console.log("RESP:", resp);
      if (resp.work_available) {
        chrome.browserAction.setBadgeBackgroundColor({color:[0, 255, 0, 230]});
        chrome.browserAction.setBadgeText({text:"YES"});

        open_or_focus_tab(resp.url);
      } else {
        chrome.browserAction.setBadgeBackgroundColor({color:[0, 0, 0, 230]});
        chrome.browserAction.setBadgeText({text:"NO"});
      }
    }
  }
  xhr.send();

  if (_checking_active) {
    setTimeout(function() {
      xhr.abort();
      check_for_work();
    }, check_for_work_interval);
  }
}

//
// Get user information
//
chrome.identity.getProfileUserInfo(function(info) {
  info_hash.email = info.email
  info_hash.id = info.id

  console.log('tester info:', info_hash);
})

//
// Get idle checking - this drops the polling rate
// for "inactive" users (i.e. when AFK)
//
chrome.idle.setDetectionInterval(15);
chrome.idle.onStateChanged.addListener(function(state){
  info_hash.tester_state = state;
  console.log('tester info:', info_hash);

  if (state == 'idle') {
    check_for_work_interval = default_check_for_work_interval * 10;
  } else if (state == 'active') {
    check_for_work_interval = default_check_for_work_interval;
  }
})
<commit_msg>jules js crime comments<commit_after>BASE_URL = 'https://portal.rainforestqa.com';

var manifest = chrome.runtime.getManifest();
console.log("Starting:", manifest.name, manifest.version, BASE_URL);

// Start disabled: require the tester to enable if they want to
// work when the browser starts
var _checking_active = false;
var info_hash = {
  tester_state: 'active',
  work_available_endpoint: BASE_URL + '/api/1/testers/',
  email: '',
  id: '',
  version: manifest.version
};

// Set polling interval in milliseconds (note, this is rate limted,
// so if you change agressively, it will error)
default_check_for_work_interval = 8 * 1000;
check_for_work_interval = default_check_for_work_interval;

//
// Load the initial id value from storage
//
chrome.storage.sync.get("worker_uuid", function(data) {
  // Notify that we saved.
  console.log("Data loaded", data);
  if (data['worker_uuid'] != undefined) {
    info_hash["uuid"] = data["worker_uuid"];
    console.log("Updated info hash:", info_hash);
    set_checking(_checking_active);
  } else {
    sync_tab_make_new();
  }
});

//
// Load the initial api endpoint value from storage
//
chrome.storage.sync.get("work_available_endpoint", function(data) {
  // Notify that we saved.
  if (data['work_available_endpoint'] !== undefined) {
    info_hash["work_available_endpoint"] = data["work_available_endpoint"];
    set_checking(_checking_active);
  } else {
    sync_tab_make_new();
  }
});

// Handle the icon being clicked
//
// this enables or disables checking for new work
//
chrome.browserAction.onClicked.addListener(function (event) {
  _checking_active = !_checking_active;
  set_checking(_checking_active);
});

//
// Handle data coming from the main site
//
chrome.runtime.onMessageExternal.addListener(function(request, sender, sendResponse) {
  if (request.data) {
    if (request.data['worker_uuid'] && request.data['work_available_endpoint']) {
      info_hash.uuid = request.data.worker_uuid;
      info_hash.work_available_endpoint = request.data.work_available_endpoint;
      set_checking(_checking_active);
      sendResponse({ok: true});

      chrome.storage.sync.set(
        {
          worker_uuid: request.data["worker_uuid"],
          work_available_endpoint: request.data["work_available_endpoint"]
        },
        function() {}
      );
    }
  }
});

//
// Set checking state
//
function set_checking(state) {
  if (!state) {
    chrome.browserAction.setBadgeBackgroundColor({color:[255, 0, 0, 230]});
    chrome.browserAction.setBadgeText({text:"OFF"});
  } else {
    check_for_work();
  }
}

//
// Open or focus the main work tab
//
work_tab = null
function open_or_focus_tab(url) {
  if (work_tab === null && typeof work_tab === "object" ) {
    work_tab_make_new(url);
  } else {
    refresh_tab_info();
  }
}

//
// Make sure the work tab is open and in focus
//
function refresh_tab_info() {
  chrome.tabs.get(work_tab.id, function (t) {
    if (chrome.runtime.lastError) {
      work_tab = null;
      console.log(chrome.runtime.lastError.message);
    } else {
      work_tab = t;
      console.log('checking work tab', work_tab);

      // force selection
      if (!work_tab.selected) {
        chrome.tabs.update(work_tab.id, {selected: true});
      }
    }
  });
}

//
// Open a new work tab
//
function work_tab_make_new(url) {
  // make a new tab
  chrome.tabs.create({ url: url }, function (t) {
    console.log('made new work tab', t);
    work_tab = t;
  });
}

//
// Open a sync tab
//
function sync_tab_make_new() {
  // make a new tab
  chrome.tabs.create({ url: BASE_URL + '/profile?version=' + manifest.version }, function (t) {
    console.log('made new sync tab', t);
  });
}

//
// Poll for new work
//
function check_for_work() {
  if (info_hash["uuid"] == "" || info_hash["uuid"] === undefined) {
    console.log("Info hash not set", info_hash);
    return false;
  }

  var xhr = new XMLHttpRequest();
  xhr.open("GET", info_hash["work_available_endpoint"] + info_hash["uuid"] + "/work_available?info=" + JSON.stringify(info_hash), true);
  xhr.onreadystatechange = function() {
    if (xhr.readyState == 4 && _checking_active) {
      var resp = JSON.parse(xhr.responseText);
      console.log("RESP:", resp);
      if (resp.work_available) {
        chrome.browserAction.setBadgeBackgroundColor({color:[0, 255, 0, 230]});
        chrome.browserAction.setBadgeText({text:"YES"});

        open_or_focus_tab(resp.url);
      } else {
        chrome.browserAction.setBadgeBackgroundColor({color:[0, 0, 0, 230]});
        chrome.browserAction.setBadgeText({text:"NO"});
      }
    }
  }
  xhr.send();

  if (_checking_active) {
    setTimeout(function() {
      xhr.abort();
      check_for_work();
    }, check_for_work_interval);
  }
}

//
// Get user information
//
chrome.identity.getProfileUserInfo(function(info) {
  info_hash.email = info.email
  info_hash.id = info.id

  console.log('tester info:', info_hash);
})

//
// Get idle checking - this drops the polling rate
// for "inactive" users (i.e. when AFK)
//
chrome.idle.setDetectionInterval(15);
chrome.idle.onStateChanged.addListener(function(state){
  info_hash.tester_state = state;
  console.log('tester info:', info_hash);

  if (state == 'idle') {
    check_for_work_interval = default_check_for_work_interval * 10;
  } else if (state == 'active') {
    check_for_work_interval = default_check_for_work_interval;
  }
})
<|endoftext|>


<commit_before>System.config({
  defaultJSExtensions: true,
  transpiler: "babel",
  babelOptions: {
    "optional": [
      "runtime",
      "es7.decorators",
      "es7.classProperties"
    ]
  },
  paths: {
    "github:*": "jspm_packages/github/*",
    "npm:*": "jspm_packages/npm/*"
  },

  map: {
    "aurelia-binding": "npm:aurelia-binding@1.0.0-beta.2.0.5",
    "aurelia-bootstrapper": "npm:aurelia-bootstrapper@1.0.0-beta.1.2.1",
    "aurelia-dependency-injection": "npm:aurelia-dependency-injection@1.0.0-beta.2.1.0",
    "aurelia-framework": "npm:aurelia-framework@1.0.0-beta.2.0.0",
    "aurelia-logging": "npm:aurelia-logging@1.0.0-beta.2.0.0",
    "aurelia-logging-console": "npm:aurelia-logging-console@1.0.0-beta.2.0.0",
    "aurelia-pal": "npm:aurelia-pal@1.0.0-beta.1.3.0",
    "aurelia-pal-browser": "npm:aurelia-pal-browser@1.0.0-beta.3.0.0",
    "aurelia-path": "npm:aurelia-path@1.0.0-beta.2.0.0",
    "aurelia-polyfills": "npm:aurelia-polyfills@1.0.0-beta.2.0.0",
    "aurelia-task-queue": "npm:aurelia-task-queue@1.0.0-beta.2.0.0",
    "aurelia-templating": "npm:aurelia-templating@1.0.0-beta.3.0.2",
    "aurelia-templating-resources": "npm:aurelia-templating-resources@1.0.0-beta.3.0.0",
    "babel": "npm:babel-core@5.8.38",
    "babel-runtime": "npm:babel-runtime@5.8.38",
    "core-js": "npm:core-js@1.2.6",
    "traceur": "github:jmcriffey/bower-traceur@0.0.92",
    "traceur-runtime": "github:jmcriffey/bower-traceur-runtime@0.0.92",
    "github:jspm/nodelibs-assert@0.1.0": {
      "assert": "npm:assert@1.4.1"
    },
    "github:jspm/nodelibs-buffer@0.1.0": {
      "buffer": "npm:buffer@3.6.0"
    },
    "github:jspm/nodelibs-path@0.1.0": {
      "path-browserify": "npm:path-browserify@0.0.0"
    },
    "github:jspm/nodelibs-process@0.1.2": {
      "process": "npm:process@0.11.5"
    },
    "github:jspm/nodelibs-util@0.1.0": {
      "util": "npm:util@0.10.3"
    },
    "github:jspm/nodelibs-vm@0.1.0": {
      "vm-browserify": "npm:vm-browserify@0.0.4"
    },
    "npm:assert@1.4.1": {
      "assert": "github:jspm/nodelibs-assert@0.1.0",
      "buffer": "github:jspm/nodelibs-buffer@0.1.0",
      "process": "github:jspm/nodelibs-process@0.1.2",
      "util": "npm:util@0.10.3"
    },
    "npm:aurelia-binding@1.0.0-beta.2.0.5": {
      "aurelia-logging": "npm:aurelia-logging@1.0.0-beta.2.0.0",
      "aurelia-metadata": "npm:aurelia-metadata@1.0.0-beta.2.0.0",
      "aurelia-pal": "npm:aurelia-pal@1.0.0-beta.1.3.0",
      "aurelia-task-queue": "npm:aurelia-task-queue@1.0.0-beta.2.0.0"
    },
    "npm:aurelia-bootstrapper@1.0.0-beta.1.2.1": {
      "aurelia-event-aggregator": "npm:aurelia-event-aggregator@1.0.0-beta.2.0.0",
      "aurelia-framework": "npm:aurelia-framework@1.0.0-beta.2.0.0",
      "aurelia-history": "npm:aurelia-history@1.0.0-beta.2.0.0",
      "aurelia-history-browser": "npm:aurelia-history-browser@1.0.0-beta.2.0.0",
      "aurelia-loader-default": "npm:aurelia-loader-default@1.0.0-beta.2.0.0",
      "aurelia-logging-console": "npm:aurelia-logging-console@1.0.0-beta.2.0.0",
      "aurelia-pal": "npm:aurelia-pal@1.0.0-beta.1.3.0",
      "aurelia-pal-browser": "npm:aurelia-pal-browser@1.0.0-beta.3.0.0",
      "aurelia-polyfills": "npm:aurelia-polyfills@1.0.0-beta.2.0.0",
      "aurelia-router": "npm:aurelia-router@1.0.0-beta.2.0.0",
      "aurelia-templating": "npm:aurelia-templating@1.0.0-beta.3.0.2",
      "aurelia-templating-binding": "npm:aurelia-templating-binding@1.0.0-beta.2.0.0",
      "aurelia-templating-resources": "npm:aurelia-templating-resources@1.0.0-beta.3.0.0",
      "aurelia-templating-router": "npm:aurelia-templating-router@1.0.0-beta.2.0.0"
    },
    "npm:aurelia-dependency-injection@1.0.0-beta.2.1.0": {
      "aurelia-metadata": "npm:aurelia-metadata@1.0.0-beta.2.0.0",
      "aurelia-pal": "npm:aurelia-pal@1.0.0-beta.1.3.0"
    },
    "npm:aurelia-event-aggregator@1.0.0-beta.2.0.0": {
      "aurelia-logging": "npm:aurelia-logging@1.0.0-beta.2.0.0"
    },
    "npm:aurelia-framework@1.0.0-beta.2.0.0": {
      "aurelia-binding": "npm:aurelia-binding@1.0.0-beta.2.0.5",
      "aurelia-dependency-injection": "npm:aurelia-dependency-injection@1.0.0-beta.2.1.0",
      "aurelia-loader": "npm:aurelia-loader@1.0.0-beta.2.0.0",
      "aurelia-logging": "npm:aurelia-logging@1.0.0-beta.2.0.0",
      "aurelia-metadata": "npm:aurelia-metadata@1.0.0-beta.2.0.0",
      "aurelia-pal": "npm:aurelia-pal@1.0.0-beta.1.3.0",
      "aurelia-path": "npm:aurelia-path@1.0.0-beta.2.0.0",
      "aurelia-task-queue": "npm:aurelia-task-queue@1.0.0-beta.2.0.0",
      "aurelia-templating": "npm:aurelia-templating@1.0.0-beta.3.0.2"
    },
    "npm:aurelia-history-browser@1.0.0-beta.2.0.0": {
      "aurelia-history": "npm:aurelia-history@1.0.0-beta.2.0.0",
      "aurelia-pal": "npm:aurelia-pal@1.0.0-beta.1.3.0"
    },
    "npm:aurelia-loader-default@1.0.0-beta.2.0.0": {
      "aurelia-loader": "npm:aurelia-loader@1.0.0-beta.2.0.0",
      "aurelia-metadata": "npm:aurelia-metadata@1.0.0-beta.2.0.0",
      "aurelia-pal": "npm:aurelia-pal@1.0.0-beta.1.3.0"
    },
    "npm:aurelia-loader@1.0.0-beta.2.0.0": {
      "aurelia-metadata": "npm:aurelia-metadata@1.0.0-beta.2.0.0",
      "aurelia-path": "npm:aurelia-path@1.0.0-beta.2.0.0"
    },
    "npm:aurelia-logging-console@1.0.0-beta.2.0.0": {
      "aurelia-logging": "npm:aurelia-logging@1.0.0-beta.2.0.0"
    },
    "npm:aurelia-metadata@1.0.0-beta.2.0.0": {
      "aurelia-pal": "npm:aurelia-pal@1.0.0-beta.1.3.0"
    },
    "npm:aurelia-pal-browser@1.0.0-beta.3.0.0": {
      "aurelia-pal": "npm:aurelia-pal@1.0.0-beta.1.3.0"
    },
    "npm:aurelia-polyfills@1.0.0-beta.2.0.0": {
      "aurelia-pal": "npm:aurelia-pal@1.0.0-beta.1.3.0"
    },
    "npm:aurelia-route-recognizer@1.0.0-beta.2.0.0": {
      "aurelia-path": "npm:aurelia-path@1.0.0-beta.2.0.0"
    },
    "npm:aurelia-router@1.0.0-beta.2.0.0": {
      "aurelia-dependency-injection": "npm:aurelia-dependency-injection@1.0.0-beta.2.1.0",
      "aurelia-event-aggregator": "npm:aurelia-event-aggregator@1.0.0-beta.2.0.0",
      "aurelia-history": "npm:aurelia-history@1.0.0-beta.2.0.0",
      "aurelia-logging": "npm:aurelia-logging@1.0.0-beta.2.0.0",
      "aurelia-path": "npm:aurelia-path@1.0.0-beta.2.0.0",
      "aurelia-route-recognizer": "npm:aurelia-route-recognizer@1.0.0-beta.2.0.0"
    },
    "npm:aurelia-task-queue@1.0.0-beta.2.0.0": {
      "aurelia-pal": "npm:aurelia-pal@1.0.0-beta.1.3.0"
    },
    "npm:aurelia-templating-binding@1.0.0-beta.2.0.0": {
      "aurelia-binding": "npm:aurelia-binding@1.0.0-beta.2.0.5",
      "aurelia-logging": "npm:aurelia-logging@1.0.0-beta.2.0.0",
      "aurelia-templating": "npm:aurelia-templating@1.0.0-beta.3.0.2"
    },
    "npm:aurelia-templating-resources@1.0.0-beta.3.0.0": {
      "aurelia-binding": "npm:aurelia-binding@1.0.0-beta.2.0.5",
      "aurelia-dependency-injection": "npm:aurelia-dependency-injection@1.0.0-beta.2.1.0",
      "aurelia-loader": "npm:aurelia-loader@1.0.0-beta.2.0.0",
      "aurelia-logging": "npm:aurelia-logging@1.0.0-beta.2.0.0",
      "aurelia-metadata": "npm:aurelia-metadata@1.0.0-beta.2.0.0",
      "aurelia-pal": "npm:aurelia-pal@1.0.0-beta.1.3.0",
      "aurelia-path": "npm:aurelia-path@1.0.0-beta.2.0.0",
      "aurelia-task-queue": "npm:aurelia-task-queue@1.0.0-beta.2.0.0",
      "aurelia-templating": "npm:aurelia-templating@1.0.0-beta.3.0.2"
    },
    "npm:aurelia-templating-router@1.0.0-beta.2.0.0": {
      "aurelia-dependency-injection": "npm:aurelia-dependency-injection@1.0.0-beta.2.1.0",
      "aurelia-logging": "npm:aurelia-logging@1.0.0-beta.2.0.0",
      "aurelia-metadata": "npm:aurelia-metadata@1.0.0-beta.2.0.0",
      "aurelia-pal": "npm:aurelia-pal@1.0.0-beta.1.3.0",
      "aurelia-path": "npm:aurelia-path@1.0.0-beta.2.0.0",
      "aurelia-router": "npm:aurelia-router@1.0.0-beta.2.0.0",
      "aurelia-templating": "npm:aurelia-templating@1.0.0-beta.3.0.2"
    },
    "npm:aurelia-templating@1.0.0-beta.3.0.2": {
      "aurelia-binding": "npm:aurelia-binding@1.0.0-beta.2.0.5",
      "aurelia-dependency-injection": "npm:aurelia-dependency-injection@1.0.0-beta.2.1.0",
      "aurelia-loader": "npm:aurelia-loader@1.0.0-beta.2.0.0",
      "aurelia-logging": "npm:aurelia-logging@1.0.0-beta.2.0.0",
      "aurelia-metadata": "npm:aurelia-metadata@1.0.0-beta.2.0.0",
      "aurelia-pal": "npm:aurelia-pal@1.0.0-beta.1.3.0",
      "aurelia-path": "npm:aurelia-path@1.0.0-beta.2.0.0",
      "aurelia-task-queue": "npm:aurelia-task-queue@1.0.0-beta.2.0.0"
    },
    "npm:babel-runtime@5.8.38": {
      "process": "github:jspm/nodelibs-process@0.1.2"
    },
    "npm:buffer@3.6.0": {
      "base64-js": "npm:base64-js@0.0.8",
      "child_process": "github:jspm/nodelibs-child_process@0.1.0",
      "fs": "github:jspm/nodelibs-fs@0.1.2",
      "ieee754": "npm:ieee754@1.1.6",
      "isarray": "npm:isarray@1.0.0",
      "process": "github:jspm/nodelibs-process@0.1.2"
    },
    "npm:core-js@1.2.6": {
      "fs": "github:jspm/nodelibs-fs@0.1.2",
      "path": "github:jspm/nodelibs-path@0.1.0",
      "process": "github:jspm/nodelibs-process@0.1.2",
      "systemjs-json": "github:systemjs/plugin-json@0.1.2"
    },
    "npm:inherits@2.0.1": {
      "util": "github:jspm/nodelibs-util@0.1.0"
    },
    "npm:path-browserify@0.0.0": {
      "process": "github:jspm/nodelibs-process@0.1.2"
    },
    "npm:process@0.11.5": {
      "assert": "github:jspm/nodelibs-assert@0.1.0",
      "fs": "github:jspm/nodelibs-fs@0.1.2",
      "vm": "github:jspm/nodelibs-vm@0.1.0"
    },
    "npm:util@0.10.3": {
      "inherits": "npm:inherits@2.0.1",
      "process": "github:jspm/nodelibs-process@0.1.2"
    },
    "npm:vm-browserify@0.0.4": {
      "indexof": "npm:indexof@0.0.1"
    }
  }
});
<commit_msg>chore(all): update jspm config<commit_after>System.config({
  defaultJSExtensions: true,
  transpiler: "babel",
  babelOptions: {
    "optional": [
      "runtime",
      "es7.decorators",
      "es7.classProperties"
    ]
  },
  paths: {
    "github:*": "jspm_packages/github/*",
    "npm:*": "jspm_packages/npm/*"
  },

  map: {
    "aurelia-binding": "npm:aurelia-binding@1.0.0-rc.1.0.0",
    "aurelia-bootstrapper": "npm:aurelia-bootstrapper@1.0.0-beta.2.0.1",
    "aurelia-dependency-injection": "npm:aurelia-dependency-injection@1.0.0-rc.1.0.0",
    "aurelia-framework": "npm:aurelia-framework@1.0.0-rc.1.0.0",
    "aurelia-logging": "npm:aurelia-logging@1.0.0-rc.1.0.0",
    "aurelia-logging-console": "npm:aurelia-logging-console@1.0.0-rc.1.0.0",
    "aurelia-pal": "npm:aurelia-pal@1.0.0-rc.1.0.0",
    "aurelia-pal-browser": "npm:aurelia-pal-browser@1.0.0-rc.1.0.0",
    "aurelia-path": "npm:aurelia-path@1.0.0-rc.1.0.0",
    "aurelia-polyfills": "npm:aurelia-polyfills@1.0.0-rc.1.0.0",
    "aurelia-task-queue": "npm:aurelia-task-queue@1.0.0-rc.1.0.0",
    "aurelia-templating": "npm:aurelia-templating@1.0.0-rc.1.0.0",
    "aurelia-templating-resources": "npm:aurelia-templating-resources@1.0.0-rc.1.0.0",
    "babel": "npm:babel-core@5.8.38",
    "babel-runtime": "npm:babel-runtime@5.8.38",
    "core-js": "npm:core-js@1.2.6",
    "traceur": "github:jmcriffey/bower-traceur@0.0.92",
    "traceur-runtime": "github:jmcriffey/bower-traceur-runtime@0.0.92",
    "github:jspm/nodelibs-assert@0.1.0": {
      "assert": "npm:assert@1.4.1"
    },
    "github:jspm/nodelibs-buffer@0.1.0": {
      "buffer": "npm:buffer@3.6.0"
    },
    "github:jspm/nodelibs-path@0.1.0": {
      "path-browserify": "npm:path-browserify@0.0.0"
    },
    "github:jspm/nodelibs-process@0.1.2": {
      "process": "npm:process@0.11.5"
    },
    "github:jspm/nodelibs-util@0.1.0": {
      "util": "npm:util@0.10.3"
    },
    "github:jspm/nodelibs-vm@0.1.0": {
      "vm-browserify": "npm:vm-browserify@0.0.4"
    },
    "npm:assert@1.4.1": {
      "assert": "github:jspm/nodelibs-assert@0.1.0",
      "buffer": "github:jspm/nodelibs-buffer@0.1.0",
      "process": "github:jspm/nodelibs-process@0.1.2",
      "util": "npm:util@0.10.3"
    },
    "npm:aurelia-binding@1.0.0-rc.1.0.0": {
      "aurelia-logging": "npm:aurelia-logging@1.0.0-rc.1.0.0",
      "aurelia-metadata": "npm:aurelia-metadata@1.0.0-rc.1.0.0",
      "aurelia-pal": "npm:aurelia-pal@1.0.0-rc.1.0.0",
      "aurelia-task-queue": "npm:aurelia-task-queue@1.0.0-rc.1.0.0"
    },
    "npm:aurelia-bootstrapper@1.0.0-beta.2.0.1": {
      "aurelia-event-aggregator": "npm:aurelia-event-aggregator@1.0.0-rc.1.0.0",
      "aurelia-framework": "npm:aurelia-framework@1.0.0-rc.1.0.0",
      "aurelia-history": "npm:aurelia-history@1.0.0-rc.1.0.0",
      "aurelia-history-browser": "npm:aurelia-history-browser@1.0.0-rc.1.0.0",
      "aurelia-loader-default": "npm:aurelia-loader-default@1.0.0-rc.1.0.0",
      "aurelia-logging-console": "npm:aurelia-logging-console@1.0.0-rc.1.0.0",
      "aurelia-pal": "npm:aurelia-pal@1.0.0-rc.1.0.0",
      "aurelia-pal-browser": "npm:aurelia-pal-browser@1.0.0-rc.1.0.0",
      "aurelia-polyfills": "npm:aurelia-polyfills@1.0.0-rc.1.0.0",
      "aurelia-router": "npm:aurelia-router@1.0.0-rc.1.0.0",
      "aurelia-templating": "npm:aurelia-templating@1.0.0-rc.1.0.0",
      "aurelia-templating-binding": "npm:aurelia-templating-binding@1.0.0-rc.1.0.0",
      "aurelia-templating-resources": "npm:aurelia-templating-resources@1.0.0-rc.1.0.0",
      "aurelia-templating-router": "npm:aurelia-templating-router@1.0.0-rc.1.0.0"
    },
    "npm:aurelia-dependency-injection@1.0.0-rc.1.0.0": {
      "aurelia-metadata": "npm:aurelia-metadata@1.0.0-rc.1.0.0",
      "aurelia-pal": "npm:aurelia-pal@1.0.0-rc.1.0.0"
    },
    "npm:aurelia-event-aggregator@1.0.0-rc.1.0.0": {
      "aurelia-logging": "npm:aurelia-logging@1.0.0-rc.1.0.0"
    },
    "npm:aurelia-framework@1.0.0-rc.1.0.0": {
      "aurelia-binding": "npm:aurelia-binding@1.0.0-rc.1.0.0",
      "aurelia-dependency-injection": "npm:aurelia-dependency-injection@1.0.0-rc.1.0.0",
      "aurelia-loader": "npm:aurelia-loader@1.0.0-rc.1.0.0",
      "aurelia-logging": "npm:aurelia-logging@1.0.0-rc.1.0.0",
      "aurelia-metadata": "npm:aurelia-metadata@1.0.0-rc.1.0.0",
      "aurelia-pal": "npm:aurelia-pal@1.0.0-rc.1.0.0",
      "aurelia-path": "npm:aurelia-path@1.0.0-rc.1.0.0",
      "aurelia-task-queue": "npm:aurelia-task-queue@1.0.0-rc.1.0.0",
      "aurelia-templating": "npm:aurelia-templating@1.0.0-rc.1.0.0"
    },
    "npm:aurelia-history-browser@1.0.0-rc.1.0.0": {
      "aurelia-history": "npm:aurelia-history@1.0.0-rc.1.0.0",
      "aurelia-pal": "npm:aurelia-pal@1.0.0-rc.1.0.0"
    },
    "npm:aurelia-loader-default@1.0.0-rc.1.0.0": {
      "aurelia-loader": "npm:aurelia-loader@1.0.0-rc.1.0.0",
      "aurelia-metadata": "npm:aurelia-metadata@1.0.0-rc.1.0.0",
      "aurelia-pal": "npm:aurelia-pal@1.0.0-rc.1.0.0"
    },
    "npm:aurelia-loader@1.0.0-rc.1.0.0": {
      "aurelia-metadata": "npm:aurelia-metadata@1.0.0-rc.1.0.0",
      "aurelia-path": "npm:aurelia-path@1.0.0-rc.1.0.0"
    },
    "npm:aurelia-logging-console@1.0.0-rc.1.0.0": {
      "aurelia-logging": "npm:aurelia-logging@1.0.0-rc.1.0.0"
    },
    "npm:aurelia-metadata@1.0.0-rc.1.0.0": {
      "aurelia-pal": "npm:aurelia-pal@1.0.0-rc.1.0.0"
    },
    "npm:aurelia-pal-browser@1.0.0-rc.1.0.0": {
      "aurelia-pal": "npm:aurelia-pal@1.0.0-rc.1.0.0"
    },
    "npm:aurelia-polyfills@1.0.0-rc.1.0.0": {
      "aurelia-pal": "npm:aurelia-pal@1.0.0-rc.1.0.0"
    },
    "npm:aurelia-route-recognizer@1.0.0-rc.1.0.0": {
      "aurelia-path": "npm:aurelia-path@1.0.0-rc.1.0.0"
    },
    "npm:aurelia-router@1.0.0-rc.1.0.0": {
      "aurelia-dependency-injection": "npm:aurelia-dependency-injection@1.0.0-rc.1.0.0",
      "aurelia-event-aggregator": "npm:aurelia-event-aggregator@1.0.0-rc.1.0.0",
      "aurelia-history": "npm:aurelia-history@1.0.0-rc.1.0.0",
      "aurelia-logging": "npm:aurelia-logging@1.0.0-rc.1.0.0",
      "aurelia-path": "npm:aurelia-path@1.0.0-rc.1.0.0",
      "aurelia-route-recognizer": "npm:aurelia-route-recognizer@1.0.0-rc.1.0.0"
    },
    "npm:aurelia-task-queue@1.0.0-rc.1.0.0": {
      "aurelia-pal": "npm:aurelia-pal@1.0.0-rc.1.0.0"
    },
    "npm:aurelia-templating-binding@1.0.0-rc.1.0.0": {
      "aurelia-binding": "npm:aurelia-binding@1.0.0-rc.1.0.0",
      "aurelia-logging": "npm:aurelia-logging@1.0.0-rc.1.0.0",
      "aurelia-templating": "npm:aurelia-templating@1.0.0-rc.1.0.0"
    },
    "npm:aurelia-templating-resources@1.0.0-rc.1.0.0": {
      "aurelia-binding": "npm:aurelia-binding@1.0.0-rc.1.0.0",
      "aurelia-dependency-injection": "npm:aurelia-dependency-injection@1.0.0-rc.1.0.0",
      "aurelia-loader": "npm:aurelia-loader@1.0.0-rc.1.0.0",
      "aurelia-logging": "npm:aurelia-logging@1.0.0-rc.1.0.0",
      "aurelia-metadata": "npm:aurelia-metadata@1.0.0-rc.1.0.0",
      "aurelia-pal": "npm:aurelia-pal@1.0.0-rc.1.0.0",
      "aurelia-path": "npm:aurelia-path@1.0.0-rc.1.0.0",
      "aurelia-task-queue": "npm:aurelia-task-queue@1.0.0-rc.1.0.0",
      "aurelia-templating": "npm:aurelia-templating@1.0.0-rc.1.0.0"
    },
    "npm:aurelia-templating-router@1.0.0-rc.1.0.0": {
      "aurelia-dependency-injection": "npm:aurelia-dependency-injection@1.0.0-rc.1.0.0",
      "aurelia-logging": "npm:aurelia-logging@1.0.0-rc.1.0.0",
      "aurelia-metadata": "npm:aurelia-metadata@1.0.0-rc.1.0.0",
      "aurelia-pal": "npm:aurelia-pal@1.0.0-rc.1.0.0",
      "aurelia-path": "npm:aurelia-path@1.0.0-rc.1.0.0",
      "aurelia-router": "npm:aurelia-router@1.0.0-rc.1.0.0",
      "aurelia-templating": "npm:aurelia-templating@1.0.0-rc.1.0.0"
    },
    "npm:aurelia-templating@1.0.0-rc.1.0.0": {
      "aurelia-binding": "npm:aurelia-binding@1.0.0-rc.1.0.0",
      "aurelia-dependency-injection": "npm:aurelia-dependency-injection@1.0.0-rc.1.0.0",
      "aurelia-loader": "npm:aurelia-loader@1.0.0-rc.1.0.0",
      "aurelia-logging": "npm:aurelia-logging@1.0.0-rc.1.0.0",
      "aurelia-metadata": "npm:aurelia-metadata@1.0.0-rc.1.0.0",
      "aurelia-pal": "npm:aurelia-pal@1.0.0-rc.1.0.0",
      "aurelia-path": "npm:aurelia-path@1.0.0-rc.1.0.0",
      "aurelia-task-queue": "npm:aurelia-task-queue@1.0.0-rc.1.0.0"
    },
    "npm:babel-runtime@5.8.38": {
      "process": "github:jspm/nodelibs-process@0.1.2"
    },
    "npm:buffer@3.6.0": {
      "base64-js": "npm:base64-js@0.0.8",
      "child_process": "github:jspm/nodelibs-child_process@0.1.0",
      "fs": "github:jspm/nodelibs-fs@0.1.2",
      "ieee754": "npm:ieee754@1.1.6",
      "isarray": "npm:isarray@1.0.0",
      "process": "github:jspm/nodelibs-process@0.1.2"
    },
    "npm:core-js@1.2.6": {
      "fs": "github:jspm/nodelibs-fs@0.1.2",
      "path": "github:jspm/nodelibs-path@0.1.0",
      "process": "github:jspm/nodelibs-process@0.1.2",
      "systemjs-json": "github:systemjs/plugin-json@0.1.2"
    },
    "npm:inherits@2.0.1": {
      "util": "github:jspm/nodelibs-util@0.1.0"
    },
    "npm:path-browserify@0.0.0": {
      "process": "github:jspm/nodelibs-process@0.1.2"
    },
    "npm:process@0.11.5": {
      "assert": "github:jspm/nodelibs-assert@0.1.0",
      "fs": "github:jspm/nodelibs-fs@0.1.2",
      "vm": "github:jspm/nodelibs-vm@0.1.0"
    },
    "npm:util@0.10.3": {
      "inherits": "npm:inherits@2.0.1",
      "process": "github:jspm/nodelibs-process@0.1.2"
    },
    "npm:vm-browserify@0.0.4": {
      "indexof": "npm:indexof@0.0.1"
    }
  }
});
<|endoftext|>


<commit_before>define([], function() {
  var tools = get_tools(),
      Emitter = get_emitter(),
      config = {
        base: '/api',
        idGetter: '_id',
        protocol: '',
        credentials: false,
        getData: function(d) { return d && d.data; },
        getError: function(d) { return d && d.error; }
      };

  function crud() {
    var c;
    if (!(this instanceof crud)) {
      c = new crud();
      c.path = tools.join.apply(c, arguments);
      return c;
    }

    if (arguments.length) this.path = tools.join.apply(this, arguments);

    Emitter(this);
  }

  // Configure =================================================================

  crud.configure = crud.config = function(obj) {
    return tools.merge(config, obj || {});
  }

  // crud.prototype ============================================================
  crud.prototype = Emitter.prototype;

  crud.prototype.create = crud.prototype.c = function() {
    var self = this,
        args = tools.xhr_args.apply(this, arguments),
        url = config.protocol + tools.join(config.base, this.path);

    tools.request('POST', url, args.data, function(e, d) {
      if (e && cb) self.emit('error', e);
      if (!e && d) {
        tools.defineProperty(d, '_crud', crud(self.path, d[config.idGetter]));
        self.emit('create', d);
      }
      args.cb && args.cb.call(self, e, d);
    });

    return this;
  };

  crud.prototype.read = crud.prototype.r = function() {
    var self = this,
        args = tools.xhr_args.apply(this, arguments),
        url = config.protocol + tools.join(config.base, this.path);

    tools.request('GET', url, null, function(e, d) {
      self.data = d;
      tools.defineProperty(d, '_crud', self);
      if (e && !args.cb) self.emit('error', e);
      if (!e && d instanceof Array) {
        self.each(function(d, i) {
          this.data = d;
          self.emitCtx('each', this, d, i);
        });
      }
      if (!e && d) self.emit('read', d);
      args.cb && args.cb.call(self, e, d);
    });
    return this;
  };

  crud.prototype.update = crud.prototype.u = function() {
    var self = this,
        args = tools.xhr_args.apply(this, arguments),
        url = config.protocol + tools.join(config.base, this.path);

    tools.request('PUT', url, args.data, function(e, d) {
      if (e && !args.cb) self.emit('error', e);
      if (!e && d) {
        tools.defineProperty(d, '_crud', crud(self.path, d[config.idGetter]));
        self.emit('update', d);
      }
      args.cb && args.cb.call(self, e, d);
    });

    return this;
  };

  crud.prototype.del = crud.prototype.d = function() {
    var self = this,
        args = tools.xhr_args.apply(this, arguments),
        url = config.protocol + tools.join(config.base, this.path);

    tools.request('DELETE', url, args.data, function(e, d) {
      if (e && !args.cb) self.emit('error', e);
      if (!e && d) self.emit('delete', d);
      args.cb && args.cb.call(self, e, d);
    });

    return this;
  };

  crud.prototype.each = function(fn) {
    var fn = fn || Function(),
        data = this.data || [],
        c, i;

    if (!(data instanceof Array)) return;

    for (i = 0; i < data.length; i++) {
      c = crud(this.path, data[i][config.idGetter]);
      tools.defineProperty(data[i], '_crud', c);
      fn.call(crud(this.path, data[i][config.idGetter]), data[i], i);
    }
  };

  // crud fns ==================================================================

  crud.parallel = function(obj, cb) {
    var obj = obj || {},
        n = Object.keys(obj).length,
        result = {},
        count = 0,
        cb = cb || Function(),
        done, k;

    tools.forEach(obj, function(path, name) {
      crud(path).read(function(e, d) {
        if (e) {
          done = true;
          return cb(e, result);
        }

        result[name] = d;
        if (++count == n && !done) {
          done = true;
          setTimeout(function() { cb(null, result); }, 0);
        }
      });
    });
  };

  return crud;

  // tools =====================================================================

  function get_tools() {
    var tools = {};

    tools.noop = Function();
    tools.id = function(d) { return d; }

    tools.argArray = function(args) {
      return Array.prototype.slice.call(args, 0);
    }

    tools.join = function() {
      return tools.argArray(arguments).join('/')
                  .replace(/\/+/g, '/');
    }

    tools.merge = function(a, b) {
      for (var k in b) a[k] = b[k];
      return a;
    }

    tools.xhr_args = function(d, cb) {
      if (typeof(d) === 'function') return { data: {}, cb: d };
      else return { data: d || {}, cb: cb };
    }

    tools.request = function(method, url, data, cb) {
      var req = typeof(XMLHttpRequest) != 'undefined'
                  ? new XMLHttpRequest()
                  : new ActiveXObject('Microsoft.XMLHTTP'),
          isjson = typeof(FormData) === 'undefined' ||
                        !(data instanceof FormData);

      if (config.credentials) req.withCredentials = true;
      req.open(method, url, true);

      if (isjson) req.setRequestHeader('Content-type', 'application/json');
      req.onreadystatechange = function() {
        var status, data, error;
        if (req.readyState == 4) {  // done
          status = req.status;
          if (status >= 200 && status < 300) {
            try {
              data = JSON.parse(req.responseText || '{}');
              error = config.getError(data);
              data = config.getData(data);
            } catch (e) { error = 'invalid json response' };
          } else {
            error = { code: status, message: 'invalid status code' };
          }
          return cb && cb(error, data);
        }
      }
      if (!isjson) req.send(data);
      else if (data) req.send(JSON.stringify(data));
      else req.send();
    };

    tools.forEach = function(obj, cb) {
      var k;
      for (k in obj) cb(obj[k], k);
    };

    tools.defineProperty = function(obj, key, val) {
      if (!(obj instanceof Object)) return;
      Object.defineProperty(obj, key, {
        enumerable: false,
        value: val
      });
    };

    return tools;
  };

  // emitter ===================================================================
  function get_emitter() {
    function Emitter(n) {
      n = n || this;
      n.__events = n.__events || {};
      n.__once = n.__once || {};
    }

    Emitter.prototype.on = function(name, fn) {
      fn = fn || Function();
      this.__events[name] = (this.__events[name] || [])
      this.__events[name].push(fn);
    }

    Emitter.prototype.once = function(name, fn) {
      fn = fn || Function();
      this.__once[name] = (this.__once[name] || []);
      this.__once[name].push(fn);
    }

    Emitter.prototype.off = function(name, fn) {
      var i;

      fn = fn || Function();
      i = (this.__events[name] || []).indexOf(fn);
      if (~i) this.__events[name].splice(i, 1);

      i = (this.__once[name] || []).indexOf(fn);
      if (~i) this.__once[name].splice(i, 1);
    }

    Emitter.prototype.emit = function(name) {
      var args = [].slice.call(arguments),
          es = (this.__events[name] || []).slice(),
          i, self = this;

      args.shift();
      es.push.apply(es, this.__once[name] || []);

      for (i = 0; i < es.length; i++) {
        es[i].apply(self, args);
      }

      this.__once[name] = [];
    }

    Emitter.prototype.emitCtx = function(name, ctx) {
      var args = [].slice.call(arguments),
          es = (this.__events[name] || []).slice(),
          i, self = this;

      args.shift(); args.shift();
      es.push.apply(es, this.__once[name] || []);

      for (i = 0; i < es.length; i++) {
        es[i].apply(ctx, args);
      }

      this.__once[name] = [];
    }

    return Emitter;
  }
});
<commit_msg>Fix bug from browser compatibility<commit_after>define([], function() {
  var tools = get_tools(),
      Emitter = get_emitter(),
      config = {
        base: '/api',
        idGetter: '_id',
        protocol: '',
        credentials: false,
        getData: function(d) { return d && d.data; },
        getError: function(d) { return d && d.error; }
      };

  function crud() {
    var c;
    if (!(this instanceof crud)) {
      c = new crud();
      c.path = tools.join.apply(c, arguments);
      return c;
    }

    if (arguments.length) this.path = tools.join.apply(this, arguments);

    Emitter(this);
  }

  // Configure =================================================================

  crud.configure = crud.config = function(obj) {
    return tools.merge(config, obj || {});
  }

  // crud.prototype ============================================================
  crud.prototype = Emitter.prototype;

  crud.prototype.create = crud.prototype.c = function() {
    var self = this,
        args = tools.xhr_args.apply(this, arguments),
        url = config.protocol + tools.join(config.base, this.path);

    tools.request('POST', url, args.data, function(e, d) {
      if (e) self.emit('error', e);
      if (!e && d) {
        tools.defineProperty(d, '_crud', crud(self.path, d[config.idGetter]));
        self.emit('create', d);
      }
      args.cb && args.cb.call(self, e, d);
    });

    return this;
  };

  crud.prototype.read = crud.prototype.r = function() {
    var self = this,
        args = tools.xhr_args.apply(this, arguments),
        url = config.protocol + tools.join(config.base, this.path);

    tools.request('GET', url, null, function(e, d) {
      self.data = d;
      tools.defineProperty(d, '_crud', self);
      if (e && !args.cb) self.emit('error', e);
      if (!e && d instanceof Array) {
        self.each(function(d, i) {
          this.data = d;
          self.emitCtx('each', this, d, i);
        });
      }
      if (!e && d) self.emit('read', d);
      args.cb && args.cb.call(self, e, d);
    });
    return this;
  };

  crud.prototype.update = crud.prototype.u = function() {
    var self = this,
        args = tools.xhr_args.apply(this, arguments),
        url = config.protocol + tools.join(config.base, this.path);

    tools.request('PUT', url, args.data, function(e, d) {
      if (e && !args.cb) self.emit('error', e);
      if (!e && d) {
        tools.defineProperty(d, '_crud', crud(self.path, d[config.idGetter]));
        self.emit('update', d);
      }
      args.cb && args.cb.call(self, e, d);
    });

    return this;
  };

  crud.prototype.del = crud.prototype.d = function() {
    var self = this,
        args = tools.xhr_args.apply(this, arguments),
        url = config.protocol + tools.join(config.base, this.path);

    tools.request('DELETE', url, args.data, function(e, d) {
      if (e && !args.cb) self.emit('error', e);
      if (!e && d) self.emit('delete', d);
      args.cb && args.cb.call(self, e, d);
    });

    return this;
  };

  crud.prototype.each = function(fn) {
    var fn = fn || Function(),
        data = this.data || [],
        c, i;

    if (!(data instanceof Array)) return;

    for (i = 0; i < data.length; i++) {
      c = crud(this.path, data[i][config.idGetter]);
      tools.defineProperty(data[i], '_crud', c);
      fn.call(crud(this.path, data[i][config.idGetter]), data[i], i);
    }
  };

  // crud fns ==================================================================

  crud.parallel = function(obj, cb) {
    var obj = obj || {},
        n = Object.keys(obj).length,
        result = {},
        count = 0,
        cb = cb || Function(),
        done, k;

    tools.forEach(obj, function(path, name) {
      crud(path).read(function(e, d) {
        if (e) {
          done = true;
          return cb(e, result);
        }

        result[name] = d;
        if (++count == n && !done) {
          done = true;
          setTimeout(function() { cb(null, result); }, 0);
        }
      });
    });
  };

  return crud;

  // tools =====================================================================

  function get_tools() {
    var tools = {};

    tools.noop = Function();
    tools.id = function(d) { return d; }

    tools.argArray = function(args) {
      return Array.prototype.slice.call(args, 0);
    }

    tools.join = function() {
      return tools.argArray(arguments).join('/')
                  .replace(/\/+/g, '/');
    }

    tools.merge = function(a, b) {
      for (var k in b) a[k] = b[k];
      return a;
    }

    tools.xhr_args = function(d, cb) {
      if (typeof(d) === 'function') return { data: {}, cb: d };
      else return { data: d || {}, cb: cb };
    }

    tools.request = function(method, url, data, cb) {
      var req = typeof(XMLHttpRequest) != 'undefined'
                  ? new XMLHttpRequest()
                  : new ActiveXObject('Microsoft.XMLHTTP'),
          isjson = typeof(FormData) === 'undefined' ||
                        !(data instanceof FormData);

      if (config.credentials) req.withCredentials = true;
      req.open(method, url, true);

      if (isjson) req.setRequestHeader('Content-type', 'application/json');
      req.onreadystatechange = function() {
        var status, data, error;
        if (req.readyState == 4) {  // done
          status = req.status;
          if (status >= 200 && status < 300) {
            try {
              data = JSON.parse(req.responseText || '{}');
              error = config.getError(data);
              data = config.getData(data);
            } catch (e) { error = 'invalid json response' };
          } else {
            error = { code: status, message: 'invalid status code' };
          }
          return cb && cb(error, data);
        }
      }
      if (!isjson) req.send(data);
      else if (data) req.send(JSON.stringify(data));
      else req.send();
    };

    tools.forEach = function(obj, cb) {
      var k;
      for (k in obj) cb(obj[k], k);
    };

    tools.defineProperty = function(obj, key, val) {
      if (!(obj instanceof Object)) return;
      Object.defineProperty(obj, key, {
        enumerable: false,
        value: val
      });
    };

    return tools;
  };

  // emitter ===================================================================
  function get_emitter() {
    function Emitter(n) {
      n = n || this;
      n.__events = n.__events || {};
      n.__once = n.__once || {};
    }

    Emitter.prototype.on = function(name, fn) {
      fn = fn || Function();
      this.__events[name] = (this.__events[name] || [])
      this.__events[name].push(fn);
    }

    Emitter.prototype.once = function(name, fn) {
      fn = fn || Function();
      this.__once[name] = (this.__once[name] || []);
      this.__once[name].push(fn);
    }

    Emitter.prototype.off = function(name, fn) {
      var i;

      fn = fn || Function();
      i = (this.__events[name] || []).indexOf(fn);
      if (~i) this.__events[name].splice(i, 1);

      i = (this.__once[name] || []).indexOf(fn);
      if (~i) this.__once[name].splice(i, 1);
    }

    Emitter.prototype.emit = function(name) {
      var args = [].slice.call(arguments),
          es = (this.__events[name] || []).slice(),
          i, self = this;

      args.shift();
      es.push.apply(es, this.__once[name] || []);

      for (i = 0; i < es.length; i++) {
        es[i].apply(self, args);
      }

      this.__once[name] = [];
    }

    Emitter.prototype.emitCtx = function(name, ctx) {
      var args = [].slice.call(arguments),
          es = (this.__events[name] || []).slice(),
          i, self = this;

      args.shift(); args.shift();
      es.push.apply(es, this.__once[name] || []);

      for (i = 0; i < es.length; i++) {
        es[i].apply(ctx, args);
      }

      this.__once[name] = [];
    }

    return Emitter;
  }
});
<|endoftext|>


<commit_before>var List = require('css-tree').List;
var walk = require('css-tree').walk;
var utils = require('./utils');

function calcSelectorLength(list) {
    var length = 0;

    list.forEach(function(data) {
        length += data.id.length + 1;
    });

    return length - 1;
}

function calcDeclarationsLength(tokens) {
    var length = 0;

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

    return (
        length +          // declarations
        tokens.length - 1 // delimeters
    );
}

function processRule(node, item, list) {
    var avoidRulesMerge = this.block !== null ? this.block.avoidRulesMerge : false;
    var selectors = node.prelude.children;
    var block = node.block;
    var disallowDownMarkers = Object.create(null);
    var allowMergeUp = true;
    var allowMergeDown = true;

    list.prevUntil(item.prev, function(prev, prevItem) {
        var prevBlock = prev.block;
        var prevType = prev.type;

        if (prevType !== 'Rule') {
            var unsafe = utils.unsafeToSkipNode.call(selectors, prev);

            if (!unsafe && prevType === 'Atrule' && prevBlock) {
                walk(prevBlock, {
                    visit: 'Rule',
                    enter: function(node) {
                        node.prelude.children.each(function(data) {
                            disallowDownMarkers[data.compareMarker] = true;
                        });
                    }
                });
            }

            return unsafe;
        }

        var prevSelectors = prev.prelude.children;

        if (node.pseudoSignature !== prev.pseudoSignature) {
            return true;
        }

        allowMergeDown = !prevSelectors.some(function(selector) {
            return selector.compareMarker in disallowDownMarkers;
        });

        // try prev ruleset if simpleselectors has no equal specifity and element selector
        if (!allowMergeDown && !allowMergeUp) {
            return true;
        }

        // try to join by selectors
        if (allowMergeUp && utils.isEqualSelectors(prevSelectors, selectors)) {
            prevBlock.children.appendList(block.children);
            list.remove(item);
            return true;
        }

        // try to join by properties
        var diff = utils.compareDeclarations(block.children, prevBlock.children);

        // console.log(diff.eq, diff.ne1, diff.ne2);

        if (diff.eq.length) {
            if (!diff.ne1.length && !diff.ne2.length) {
                // equal blocks
                if (allowMergeDown) {
                    utils.addSelectors(selectors, prevSelectors);
                    list.remove(prevItem);
                }

                return true;
            } else if (!avoidRulesMerge) { /* probably we don't need to prevent those merges for @keyframes
                                              TODO: need to be checked */

                if (diff.ne1.length && !diff.ne2.length) {
                    // prevBlock is subset block
                    var selectorLength = calcSelectorLength(selectors);
                    var blockLength = calcDeclarationsLength(diff.eq); // declarations length

                    if (allowMergeUp && selectorLength < blockLength) {
                        utils.addSelectors(prevSelectors, selectors);
                        block.children = new List().fromArray(diff.ne1);
                    }
                } else if (!diff.ne1.length && diff.ne2.length) {
                    // node is subset of prevBlock
                    var selectorLength = calcSelectorLength(prevSelectors);
                    var blockLength = calcDeclarationsLength(diff.eq); // declarations length

                    if (allowMergeDown && selectorLength < blockLength) {
                        utils.addSelectors(selectors, prevSelectors);
                        prevBlock.children = new List().fromArray(diff.ne2);
                    }
                } else {
                    // diff.ne1.length && diff.ne2.length
                    // extract equal block
                    var newSelector = {
                        type: 'SelectorList',
                        loc: null,
                        children: utils.addSelectors(prevSelectors.copy(), selectors)
                    };
                    var newBlockLength = calcSelectorLength(newSelector.children) + 2; // selectors length + curly braces length
                    var blockLength = calcDeclarationsLength(diff.eq); // declarations length

                    // create new ruleset if declarations length greater than
                    // ruleset description overhead
                    if (blockLength >= newBlockLength) {
                        var newItem = list.createItem({
                            type: 'Rule',
                            loc: null,
                            prelude: newSelector,
                            block: {
                                type: 'Block',
                                loc: null,
                                children: new List().fromArray(diff.eq)
                            },
                            pseudoSignature: node.pseudoSignature
                        });

                        block.children = new List().fromArray(diff.ne1);
                        prevBlock.children = new List().fromArray(diff.ne2overrided);

                        if (allowMergeUp) {
                            list.insert(newItem, prevItem);
                        } else {
                            list.insert(newItem, item);
                        }

                        return true;
                    }
                }
            }
        }

        if (allowMergeUp) {
            // TODO: disallow up merge only if any property interception only (i.e. diff.ne2overrided.length > 0);
            // await property families to find property interception correctly
            allowMergeUp = !prevSelectors.some(function(prevSelector) {
                return selectors.some(function(selector) {
                    return selector.compareMarker === prevSelector.compareMarker;
                });
            });
        }

        prevSelectors.forEach(function(data) {
            disallowDownMarkers[data.compareMarker] = true;
        });
    });
}

module.exports = function restructRule(ast) {
    walk(ast, {
        visit: 'Rule',
        reverse: true,
        enter: processRule
    });
};
<commit_msg>After master merge fixes<commit_after>var List = require('css-tree').List;
var walk = require('css-tree').walk;
var utils = require('./utils');

function calcSelectorLength(list) {
    var length = 0;

    list.forEach(function(data) {
        length += data.id.length + 1;
    });

    return length - 1;
}

function calcDeclarationsLength(tokens) {
    var length = 0;

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

    return (
        length +          // declarations
        tokens.length - 1 // delimeters
    );
}

function processRule(node, item, list) {
    var avoidRulesMerge = this.block !== null ? this.block.avoidRulesMerge : false;
    var selectors = node.prelude.children;
    var block = node.block;
    var disallowDownMarkers = Object.create(null);
    var allowMergeUp = true;
    var allowMergeDown = true;

    list.prevUntil(item.prev, function(prev, prevItem) {
        var prevBlock = prev.block;
        var prevType = prev.type;

        if (prevType !== 'Rule') {
            var unsafe = utils.unsafeToSkipNode.call(selectors, prev);

            if (!unsafe && prevType === 'Atrule' && prevBlock) {
                walk(prevBlock, {
                    visit: 'Rule',
                    enter: function(node) {
                        node.prelude.children.forEach(function(data) {
                            disallowDownMarkers[data.compareMarker] = true;
                        });
                    }
                });
            }

            return unsafe;
        }

        var prevSelectors = prev.prelude.children;

        if (node.pseudoSignature !== prev.pseudoSignature) {
            return true;
        }

        allowMergeDown = !prevSelectors.some(function(selector) {
            return selector.compareMarker in disallowDownMarkers;
        });

        // try prev ruleset if simpleselectors has no equal specifity and element selector
        if (!allowMergeDown && !allowMergeUp) {
            return true;
        }

        // try to join by selectors
        if (allowMergeUp && utils.isEqualSelectors(prevSelectors, selectors)) {
            prevBlock.children.appendList(block.children);
            list.remove(item);
            return true;
        }

        // try to join by properties
        var diff = utils.compareDeclarations(block.children, prevBlock.children);

        // console.log(diff.eq, diff.ne1, diff.ne2);

        if (diff.eq.length) {
            if (!diff.ne1.length && !diff.ne2.length) {
                // equal blocks
                if (allowMergeDown) {
                    utils.addSelectors(selectors, prevSelectors);
                    list.remove(prevItem);
                }

                return true;
            } else if (!avoidRulesMerge) { /* probably we don't need to prevent those merges for @keyframes
                                              TODO: need to be checked */

                if (diff.ne1.length && !diff.ne2.length) {
                    // prevBlock is subset block
                    var selectorLength = calcSelectorLength(selectors);
                    var blockLength = calcDeclarationsLength(diff.eq); // declarations length

                    if (allowMergeUp && selectorLength < blockLength) {
                        utils.addSelectors(prevSelectors, selectors);
                        block.children.fromArray(diff.ne1);
                    }
                } else if (!diff.ne1.length && diff.ne2.length) {
                    // node is subset of prevBlock
                    var selectorLength = calcSelectorLength(prevSelectors);
                    var blockLength = calcDeclarationsLength(diff.eq); // declarations length

                    if (allowMergeDown && selectorLength < blockLength) {
                        utils.addSelectors(selectors, prevSelectors);
                        prevBlock.children.fromArray(diff.ne2);
                    }
                } else {
                    // diff.ne1.length && diff.ne2.length
                    // extract equal block
                    var newSelector = {
                        type: 'SelectorList',
                        loc: null,
                        children: utils.addSelectors(prevSelectors.copy(), selectors)
                    };
                    var newBlockLength = calcSelectorLength(newSelector.children) + 2; // selectors length + curly braces length
                    var blockLength = calcDeclarationsLength(diff.eq); // declarations length

                    // create new ruleset if declarations length greater than
                    // ruleset description overhead
                    if (blockLength >= newBlockLength) {
                        var newItem = list.createItem({
                            type: 'Rule',
                            loc: null,
                            prelude: newSelector,
                            block: {
                                type: 'Block',
                                loc: null,
                                children: new List().fromArray(diff.eq)
                            },
                            pseudoSignature: node.pseudoSignature
                        });

                        block.children.fromArray(diff.ne1);
                        prevBlock.children.fromArray(diff.ne2overrided);

                        if (allowMergeUp) {
                            list.insert(newItem, prevItem);
                        } else {
                            list.insert(newItem, item);
                        }

                        return true;
                    }
                }
            }
        }

        if (allowMergeUp) {
            // TODO: disallow up merge only if any property interception only (i.e. diff.ne2overrided.length > 0);
            // await property families to find property interception correctly
            allowMergeUp = !prevSelectors.some(function(prevSelector) {
                return selectors.some(function(selector) {
                    return selector.compareMarker === prevSelector.compareMarker;
                });
            });
        }

        prevSelectors.forEach(function(data) {
            disallowDownMarkers[data.compareMarker] = true;
        });
    });
}

module.exports = function restructRule(ast) {
    walk(ast, {
        visit: 'Rule',
        reverse: true,
        enter: processRule
    });
};
<|endoftext|>


<commit_before>var Thread     = require('../models/thread');
var Post       = require('../models/post');
var Read       = require('../models/read');
var Guild      = require('../models/guild');
var request    = require('../request');
var __         = require('lodash');
var color      = require('colors');
var permission = require('../permissions');

function getTopic(topic, guildName) {
    var title       = '';
    var description = '';

    switch(topic) {
        case 'announcements':
            title       = 'Announcements';
            description = 'The latest happenings of ' + guildName + '.';
            break;
        case 'general':
            title       = 'General Discussion';
            description = 'For all your off topic discussion.';
            break;
        case 'pve':
            title       = 'PvP, Dungeons, & Raids';
            description = 'Discuss strategies and plan groups.';
            break;
        case 'pvp':
            title       = 'Player Versus Player';
            description = 'Form teams, talk strategies, get rekt.';
            break;
        case 'officer':
            title       = 'Officer';
            description = 'Officer only threads. Hidden to non-officers.';
            break;
        default:
            return false;
    }

    return { title: title, description: description };
}

module.exports = {

    createThread: function(req, res) {
        var user  = req.user;
        var topic = req.params.topic;
        var body  = req.body;

        // permissions
        if (!permission.forums.canCreateThread(topic, req)) {
            res.redirect('/unauthorized');
            return;
        }

        var thread = new Thread();

        thread.created = new Date().getTime();
        thread.author  = user._id;
        thread.title   = body.title;
        thread.topic   = topic;
        thread.views   = 1;
        thread.replies = 0;
        thread.sticky  = body.sticky == 1;
        thread.locked  = body.locked == 1;

        thread.save(function (err) {
            if (err) throw err;

            var post = new Post();

            post.created = new Date().getTime();
            post.author  = user._id;
            post.thread  = thread._id;
            post.content = encodeURIComponent(body.content);

            post.save(function (err) {
                if (err) throw err;

                var read = new Read(); // mark thread as read

                read.thread = thread._id;
                read.author = user._id;

                read.save(function(err) {
                    if(err) throw err;
                    res.redirect('/thread/' + thread._id);
                });
            });
        });
    },

    getThread: function(req, res) {
        Thread
        .findOne({ '_id' : req.params.id })
        .populate('author')
        .exec(function(err, thread) {
            if (err) throw err;
            if (thread === null) res.redirect('/500');

            // permissions
            if (!permission.forums.canViewTopic(thread.topic, req)) {
                res.redirect('/unauthorized');
                return;
            }

            Post
            .find({ 'thread': req.params.id })
            .populate('author')
            .populate('edited.user')
            .exec(function (err, posts) {
                if(err) throw err;

                var guildName = 'AXION';
                var topicData = getTopic(thread.topic, guildName);

                posts = __.sortBy(posts, 'created');

                res.render('thread', {
                    user: req.user,
                    topic: thread.topic,
                    topicTitle: topicData.title,
                    thread: thread,
                    posts: posts
                });

                thread.views++;
                thread.save();

                var read = new Read(); // mark thread as read

                read.thread = thread._id;
                read.author = req.user._id;

                read.save(function (err) {
                    if(err) throw err;
                });
            });
        });
    },

    getThreads: function(req, res) {
        var topic     = req.params.topic;
        var guildName = 'AXION';

        if(topic === undefined) {
            res.redirect('/topics');
            return;
        }

        var topicData = getTopic(topic, guildName);
        if(topicData === false) {
            res.redirect('/404');
            return;
        }

        // permissions
        if (!permission.forums.canViewTopic(topic, req)) {
            res.redirect('/unauthorized');
            return;
        }

        Thread
        .find({ 'topic' : topic })
        .populate('author')
        .sort({ sticky: -1, created: -1 })
        .exec(function(err, threads) {
            if(err) throw err;

            var _render = function() {
                res.render('threads', {
                    user: req.isAuthenticated() ? req.user : null,
                    topic: topic,
                    topicTitle: topicData.title,
                    topicDescription: topicData.description,
                    threads: threads
                });
            };

            if(req.isAuthenticated()) {
                Read
                .find({ 'author': req.user._id })
                .exec(function (err, reads) {
                    if (err) throw err;

                    for (var x = 0; x < threads.length; x++) {
                        for (var y = 0; y < reads.length; y++) {
                            if (threads[x]._id.equals(reads[y].thread)) {
                                threads[x].read = reads[y];
                            }
                        }
                    }

                    _render();
                });
            }
            else {
                _render();
            }
        });
    },

    updateThread: function(req, res) {
        Thread
        .findOne({ '_id': req.body.id })
        .exec(function (err, thread) {
            if(err) throw err;
            if(thread === null) res.redirect('/500');

            thread.title  = req.body.title;
            thread.sticky = req.body.sticky == 1;
            thread.locked = req.body.locked == 1;

            thread.save(function(err) {
                if(err) throw err;
                res.redirect('/thread/'+thread._id)
            });
        });
    },

    deleteThread: function() {

    },

    createPost: function(req, res) {
        var user     = req.user;
        var body     = req.body;
        var post     = new Post();
        var threadId = req.params.id;

        Thread.findOne({ '_id' : threadId }, function(err, thread) {
            if (err) throw err;
            if (thread === null) res.redirect('/500');

            // permissions
            if (!permission.forums.canCreatePost(thread.topic, req)) {
                res.redirect('/unauthorized');
                return;
            }

            thread.replies++;

            thread.save(function(err) {
                if (err) throw err;

                post.created = new Date().getTime();
                post.author  = user._id;
                post.thread  = threadId;
                post.content = encodeURIComponent(body.content);

                post.save(function (err) {
                    if (err) throw err;

                    Read
                    .find({ 'thread': thread._id })
                    .remove();

                    var read = new Read(); // mark thread as read

                    read.thread = thread._id;
                    read.author = user._id;

                    read.save(function (err) {
                        if (err) throw err;
                        res.redirect('/thread/' + threadId + '/#' + post._id);
                    });
                });
            });
        });
    },

    getPost: function(req, res) {
        var user = req.user;

        Post
        .findOne({ '_id': req.params.id })
        .populate('author')
        .exec(function (err, post) {
            if(err) throw err;
            if(post === null) res.redirect('/500');

            Thread
            .findOne({ '_id' : post.thread })
            .populate('author')
            .exec(function(err, thread) {
                if (err) throw err;
                if(thread === null) res.redirect('/500');

                var guildName = 'AXION';
                var topicData = getTopic(thread.topic, guildName);

                // permissions
                if (!permission.forums.canViewTopic(thread.topic, req)) {
                    res.redirect('/unauthorized');
                    return;
                }

                res.render('post', {
                    user: req.user,
                    topicTitle: topicData.title,
                    thread: thread,
                    post: post
                });
            });
        });
    },

    updatePost: function(req, res) {
        Post
        .findOne({ '_id': req.body.id })
        .populate('author')
        .exec(function (err, post) {
            if(err) throw err;
            if(post === null) res.redirect('/500');

            post.content = req.body.content;
            post.edited  = {
                user: req.user._id,
                date: new Date().getTime()
            };

            post.save(function(err) {
                if(err) throw err;
                res.redirect('/thread/' + post.thread + '/#' + post._id);
            })
        });
    },

    deletePost: function(req, res) {
        Post
        .findOne({ '_id': req.body.id })
        .populate('author')
        .exec(function (err, post) {
            if(err) throw err;
            if(post === null) res.redirect('/500');

            post.deleted = true;

            post.save(function(err) {
                if(err) throw err;
                res.redirect('/thread/' + post.thread + '/#' + post._id);
            })
        });
    },

    getTopics: function(req, res) {
        Thread.aggregate([
        { $group: { '_id' : "$topic", count : { $sum: 1 }}}
        ], function(err, groups) {
            if(err) throw err;

            var topics = {
                announcements: 0,
                officer: 0,
                general: 0,
                pve: 0,
                pvp: 0
            };

            for(var i = 0; i < groups.length; i++) {
                for(var key in topics) {
                    if(groups[i]._id === key) topics[key] = groups[i].count;
                }
            }

            res.render('topics', {
                user: req.user,
                threadCount: topics
            });
        });
    }

};<commit_msg>fixing read post bug<commit_after>var Thread     = require('../models/thread');
var Post       = require('../models/post');
var Read       = require('../models/read');
var Guild      = require('../models/guild');
var request    = require('../request');
var __         = require('lodash');
var color      = require('colors');
var permission = require('../permissions');

function getTopic(topic, guildName) {
    var title       = '';
    var description = '';

    switch(topic) {
        case 'announcements':
            title       = 'Announcements';
            description = 'The latest happenings of ' + guildName + '.';
            break;
        case 'general':
            title       = 'General Discussion';
            description = 'For all your off topic discussion.';
            break;
        case 'pve':
            title       = 'PvP, Dungeons, & Raids';
            description = 'Discuss strategies and plan groups.';
            break;
        case 'pvp':
            title       = 'Player Versus Player';
            description = 'Form teams, talk strategies, get rekt.';
            break;
        case 'officer':
            title       = 'Officer';
            description = 'Officer only threads. Hidden to non-officers.';
            break;
        default:
            return false;
    }

    return { title: title, description: description };
}

module.exports = {

    createThread: function(req, res) {
        var user  = req.user;
        var topic = req.params.topic;
        var body  = req.body;

        // permissions
        if (!permission.forums.canCreateThread(topic, req)) {
            res.redirect('/unauthorized');
            return;
        }

        var thread = new Thread();

        thread.created = new Date().getTime();
        thread.author  = user._id;
        thread.title   = body.title;
        thread.topic   = topic;
        thread.views   = 1;
        thread.replies = 0;
        thread.sticky  = body.sticky == 1;
        thread.locked  = body.locked == 1;

        thread.save(function (err) {
            if (err) throw err;

            var post = new Post();

            post.created = new Date().getTime();
            post.author  = user._id;
            post.thread  = thread._id;
            post.content = encodeURIComponent(body.content);

            post.save(function (err) {
                if (err) throw err;

                var read = new Read(); // mark thread as read

                read.thread = thread._id;
                read.author = user._id;

                read.save(function(err) {
                    if(err) throw err;
                    res.redirect('/thread/' + thread._id);
                });
            });
        });
    },

    getThread: function(req, res) {
        Thread
        .findOne({ '_id' : req.params.id })
        .populate('author')
        .exec(function(err, thread) {
            if (err) throw err;
            if (thread === null) res.redirect('/500');

            // permissions
            if (!permission.forums.canViewTopic(thread.topic, req)) {
                res.redirect('/unauthorized');
                return;
            }

            Post
            .find({ 'thread': req.params.id })
            .populate('author')
            .populate('edited.user')
            .exec(function (err, posts) {
                if(err) throw err;

                var guildName = 'AXION';
                var topicData = getTopic(thread.topic, guildName);

                posts = __.sortBy(posts, 'created');

                res.render('thread', {
                    user: req.user,
                    topic: thread.topic,
                    topicTitle: topicData.title,
                    thread: thread,
                    posts: posts
                });

                thread.views++;
                thread.save();

                var read = new Read(); // mark thread as read

                read.thread = thread._id;
                read.author = req.user._id;

                read.save(function (err) {
                    if(err) throw err;
                });
            });
        });
    },

    getThreads: function(req, res) {
        var topic     = req.params.topic;
        var guildName = 'AXION';

        if(topic === undefined) {
            res.redirect('/topics');
            return;
        }

        var topicData = getTopic(topic, guildName);
        if(topicData === false) {
            res.redirect('/404');
            return;
        }

        // permissions
        if (!permission.forums.canViewTopic(topic, req)) {
            res.redirect('/unauthorized');
            return;
        }

        Thread
        .find({ 'topic' : topic })
        .populate('author')
        .sort({ sticky: -1, created: -1 })
        .exec(function(err, threads) {
            if(err) throw err;

            var _render = function() {
                res.render('threads', {
                    user: req.isAuthenticated() ? req.user : null,
                    topic: topic,
                    topicTitle: topicData.title,
                    topicDescription: topicData.description,
                    threads: threads
                });
            };

            if(req.isAuthenticated()) {
                Read
                .find({ 'author': req.user._id })
                .exec(function (err, reads) {
                    if (err) throw err;

                    for (var x = 0; x < threads.length; x++) {
                        for (var y = 0; y < reads.length; y++) {
                            if (threads[x]._id.equals(reads[y].thread)) {
                                threads[x].read = reads[y];
                            }
                        }
                    }

                    _render();
                });
            }
            else {
                _render();
            }
        });
    },

    updateThread: function(req, res) {
        Thread
        .findOne({ '_id': req.body.id })
        .exec(function (err, thread) {
            if(err) throw err;
            if(thread === null) res.redirect('/500');

            thread.title  = req.body.title;
            thread.sticky = req.body.sticky == 1;
            thread.locked = req.body.locked == 1;

            thread.save(function(err) {
                if(err) throw err;
                res.redirect('/thread/'+thread._id)
            });
        });
    },

    deleteThread: function() {

    },

    createPost: function(req, res) {
        var user     = req.user;
        var body     = req.body;
        var post     = new Post();
        var threadId = req.params.id;

        Thread.findOne({ '_id' : threadId }, function(err, thread) {
            if (err) throw err;
            if (thread === null) res.redirect('/500');

            // permissions
            if (!permission.forums.canCreatePost(thread.topic, req)) {
                res.redirect('/unauthorized');
                return;
            }

            thread.replies++;

            thread.save(function(err) {
                if (err) throw err;

                post.created = new Date().getTime();
                post.author  = user._id;
                post.thread  = threadId;
                post.content = encodeURIComponent(body.content);

                post.save(function (err) {
                    if (err) throw err;

                    Read
                    .find({ 'thread': thread._id })
                    .exec(function(err, reads) {

                        // remove all read records for this thread
                        for(var i = 0; i < reads.length; i++) {
                            reads[i].remove();
                        }

                        var read = new Read(); // mark thread as read

                        read.thread = thread._id;
                        read.author = user._id;

                        read.save(function (err) {
                            if (err) throw err;
                            res.redirect('/thread/' + threadId + '/#' + post._id);
                        });
                    });
                });
            });
        });
    },

    getPost: function(req, res) {
        var user = req.user;

        Post
        .findOne({ '_id': req.params.id })
        .populate('author')
        .exec(function (err, post) {
            if(err) throw err;
            if(post === null) res.redirect('/500');

            Thread
            .findOne({ '_id' : post.thread })
            .populate('author')
            .exec(function(err, thread) {
                if (err) throw err;
                if(thread === null) res.redirect('/500');

                var guildName = 'AXION';
                var topicData = getTopic(thread.topic, guildName);

                // permissions
                if (!permission.forums.canViewTopic(thread.topic, req)) {
                    res.redirect('/unauthorized');
                    return;
                }

                res.render('post', {
                    user: req.user,
                    topicTitle: topicData.title,
                    thread: thread,
                    post: post
                });
            });
        });
    },

    updatePost: function(req, res) {
        Post
        .findOne({ '_id': req.body.id })
        .populate('author')
        .exec(function (err, post) {
            if(err) throw err;
            if(post === null) res.redirect('/500');

            post.content = req.body.content;
            post.edited  = {
                user: req.user._id,
                date: new Date().getTime()
            };

            post.save(function(err) {
                if(err) throw err;
                res.redirect('/thread/' + post.thread + '/#' + post._id);
            })
        });
    },

    deletePost: function(req, res) {
        Post
        .findOne({ '_id': req.body.id })
        .populate('author')
        .exec(function (err, post) {
            if(err) throw err;
            if(post === null) res.redirect('/500');

            post.deleted = true;

            post.save(function(err) {
                if(err) throw err;
                res.redirect('/thread/' + post.thread + '/#' + post._id);
            })
        });
    },

    getTopics: function(req, res) {
        Thread.aggregate([
        { $group: { '_id' : "$topic", count : { $sum: 1 }}}
        ], function(err, groups) {
            if(err) throw err;

            var topics = {
                announcements: 0,
                officer: 0,
                general: 0,
                pve: 0,
                pvp: 0
            };

            for(var i = 0; i < groups.length; i++) {
                for(var key in topics) {
                    if(groups[i]._id === key) topics[key] = groups[i].count;
                }
            }

            res.render('topics', {
                user: req.user,
                threadCount: topics
            });
        });
    }

};<|endoftext|>