<commit_before>// ==UserScript==
// @name Monster Minigame Room Monitor
// @namespace https://github.com/spAnser/steamSummerMinigame_newRooms
// @version 1.2.3
// @description Monitors new Rooms for the Monster Minigame
// @author spAnser
// @match *://steamcommunity.com/minigame/
// @match *://steamcommunity.com/minigame
// @updateURL https://raw.githubusercontent.com/spAnser/steamSummerMinigame_newRooms/master/newRooms.user.js
// @downloadURL https://raw.githubusercontent.com/spAnser/steamSummerMinigame_newRooms/master/newRooms.user.js
// @grant GM_xmlhttpRequest
// ==/UserScript==

var lastRoomCheck = 47410 // This is the room the scripts starts of looking from
var refreshSpeed = 2000 // 2000 is 2 seconds and should be fine
var levelThreshhold = 100 // Any game that is over this level will be hidden automatically
var checkSize = 10 // 10 rooms should be plenty to check at one time

$J.fn.sortDivs = function sortDivs() {
    $J("> div", this[0]).sort(dec_sort).appendTo(this[0]);
    function dec_sort(a, b){ return ($J(b).data("sort")) < ($J(a).data("sort")) ? 1 : -1 }
}

function addGlobalStyle(css) {
    var head, style;
    head = document.getElementsByTagName('head')[0];
    if (!head) { return; }
    style = document.createElement('style');
    style.type = 'text/css';
    style.innerHTML = css;
    head.appendChild(style);
}

addGlobalStyle('#newRooms{position:absolute;top:6px;left:0;width:100%;z-index:10;font-family:\'Press Start 2P\',arial,sans-serif;font-size:8px;line-height:14px}.room{width:90px;display:inline-block;text-align:center;margin:0 2px 2px;color:#34495e;cursor:default}.room .id{font-size:16px;line-height:24px}.room.filling{background-color:#f1c40f}.room.started{background-color:#2ecc71}.room.full{background-color:#e74c3c}.room.soon{background-color:#95a5a6}')

addGlobalStyle('#newRooms .main_btn{background-color: #884691;color: #ee7aff;border: 2px solid #a554b1;font-size:8px;line-height:16px;width:66px;margin:2px 0 5px;text-align:center;cursor:pointer;display:inline-block;text-shadow:1px 1px 0 rgba(0,0,0,.5)}')

addGlobalStyle('.room.started{background-color:#8aaf05;color:#d9ff54;border:2px solid #a3cf06;text-shadow:1px 1px 0 rgba(0,0,0,.5)}')
addGlobalStyle('.room.full{background-color:#af0535;color:#ff5483;border:2px solid #cf063e;text-shadow:1px 1px 0 rgba(0,0,0,.5)}')
addGlobalStyle('.room.filling{background-color:#af7f05;color:#ffd054;border:2px solid #cf9706;text-shadow:1px 1px 0 rgba(0,0,0,.5)}')
addGlobalStyle('.room.soon{background-color:#5c5b58;color:#b0aca3;border:2px solid #6d6b68;text-shadow:1px 1px 0 rgba(0,0,0,.5)}')
addGlobalStyle('#scanningRooms{font-size: 13px;color: #fff;text-align: center;font-family: Arial, Helvetica, Verdana, sans-serif;line-height: 17px;text-shadow: 1px 1px 0px rgba(0, 0, 0, 0.3);}')

$J('.section_overview').prepend(
    $J('

')
)

$J('.section_play').append(
    $J('

')
)

function addRoom(room, size, level, watch) {
    if ($J('#'+room))
        $J('#'+room).remove()

    if (watch) {
        $J('#watchedRooms').append(
            $J('

')
        )
    } else {
        $J('#newRooms').append(
            $J('

')
        )
    }

    _room = $J('#'+room)

    _room.append(
        $J('
'+room+'
')
    )
    _room.append(
        $J('
'+size+'
')
    )
    if (level > 0) {
        _room.append(
            $J('
lvl '+FormatNumberForDisplay(level)+'
')
        )
    } else {
        _room.append(
            $J('
-
')
        )
    }
    _room.append(
        $J('Join')
    )

    if (size >= 1500)
        _room.addClass('full')
    else if (level < 0)
        _room.addClass('soon')
    else if (level < 1)
        _room.addClass('filling')
    else
        _room.addClass('started')
     if (level > levelThreshhold)
        if (!watch)
            _room.remove()
}

function checkRoom(room, watch) {
    watch = typeof watch !== 'undefined' ? watch : false

    GM_xmlhttpRequest({
        method: "GET",
        url: 'http://steamapi-a.akamaihd.net/ITowerAttackMiniGameService/GetGameData/v0001/?gameid='+room+'&include_stats=1',
        onload: function(response) {
            json = $J.parseJSON(response.responseText);
            try {
                playerCount = json.response.stats.num_players
                level = json.response.game_data.level

                addRoom(room, playerCount, level, watch)
                $J('h2>small').html(lastRoomCheck)
                if (room > lastRoomCheck)
                    lastRoomCheck = room
            } catch (e) {
                if (room < lastRoomCheck + 6) {
                    addRoom(room, '-', -1, watch)
                }
            }
            $J('#watchedRooms').sortDivs()
            $J('#newRooms').sortDivs()
        }
    });
    $J('#scanningRooms').html('Scanning Room: ' + lastRoomCheck)
}

function checkRooms(start, range) {
    for (var i = start; i <= start+range; i++) {
        checkRoom(i)
    }
    setTimeout(function(){
        if (!$J('#newRooms>div').length)
            checkRooms(lastRoomCheck, checkSize/2)
    }, 150)
}

function refreshWatchedRooms() {
    $J.each($J('#watchedRooms>div'), function(index) {
        checkRoom($J(this).attr('id'), true)
    });
}

function refreshNewRooms() {
    /*
    $J.each($J('#newRooms>div'), function(index) {
        if ($J(this).hasClass('started')) {
            $J(this).remove()
        }
    });
    */
    $J.each($J('#newRooms>div'), function(index) {
        if (!$J(this).hasClass('soon'))
            checkRoom($J(this).attr('id'))
    });
    checkRooms(lastRoomCheck, checkSize)
}

function findNewRooms() {
    for (var i = lastRoomCheck; i <= lastRoomCheck+1000; i+=10) {
        isValid = validRoom(i)
        if (!isValid)
            break
    }
    lastRoomCheck = i-10
    refreshNewRooms()
    setInterval(refreshNewRooms, refreshSpeed)
}

function validRoom(room) {
    GM_xmlhttpRequest({
        method: "GET",
        url: 'http://steamapi-a.akamaihd.net/ITowerAttackMiniGameService/GetGameData/v0001/?gameid='+room+'&include_stats=1',
        onload: function(response) {
            json = $J.parseJSON(response.responseText);
            try {
                if (json.response.stats.num_players)
                    return true
                else
                    return false
            } catch (e) {}
        }
    });

    return false
}

setTimeout(findNewRooms, 250)
<commit_msg>another starting room update<commit_after>// ==UserScript==
// @name Monster Minigame Room Monitor
// @namespace https://github.com/spAnser/steamSummerMinigame_newRooms
// @version 1.2.4
// @description Monitors new Rooms for the Monster Minigame
// @author spAnser
// @match *://steamcommunity.com/minigame/
// @match *://steamcommunity.com/minigame
// @updateURL https://raw.githubusercontent.com/spAnser/steamSummerMinigame_newRooms/master/newRooms.user.js
// @downloadURL https://raw.githubusercontent.com/spAnser/steamSummerMinigame_newRooms/master/newRooms.user.js
// @grant GM_xmlhttpRequest
// ==/UserScript==

var lastRoomCheck = 47810 // This is the room the scripts starts of looking from
var refreshSpeed = 2000 // 2000 is 2 seconds and should be fine
var levelThreshhold = 100 // Any game that is over this level will be hidden automatically
var checkSize = 10 // 10 rooms should be plenty to check at one time

$J.fn.sortDivs = function sortDivs() {
    $J("> div", this[0]).sort(dec_sort).appendTo(this[0]);
    function dec_sort(a, b){ return ($J(b).data("sort")) < ($J(a).data("sort")) ? 1 : -1 }
}

function addGlobalStyle(css) {
    var head, style;
    head = document.getElementsByTagName('head')[0];
    if (!head) { return; }
    style = document.createElement('style');
    style.type = 'text/css';
    style.innerHTML = css;
    head.appendChild(style);
}

addGlobalStyle('#newRooms{position:absolute;top:6px;left:0;width:100%;z-index:10;font-family:\'Press Start 2P\',arial,sans-serif;font-size:8px;line-height:14px}.room{width:90px;display:inline-block;text-align:center;margin:0 2px 2px;color:#34495e;cursor:default}.room .id{font-size:16px;line-height:24px}.room.filling{background-color:#f1c40f}.room.started{background-color:#2ecc71}.room.full{background-color:#e74c3c}.room.soon{background-color:#95a5a6}')

addGlobalStyle('#newRooms .main_btn{background-color: #884691;color: #ee7aff;border: 2px solid #a554b1;font-size:8px;line-height:16px;width:66px;margin:2px 0 5px;text-align:center;cursor:pointer;display:inline-block;text-shadow:1px 1px 0 rgba(0,0,0,.5)}')

addGlobalStyle('.room.started{background-color:#8aaf05;color:#d9ff54;border:2px solid #a3cf06;text-shadow:1px 1px 0 rgba(0,0,0,.5)}')
addGlobalStyle('.room.full{background-color:#af0535;color:#ff5483;border:2px solid #cf063e;text-shadow:1px 1px 0 rgba(0,0,0,.5)}')
addGlobalStyle('.room.filling{background-color:#af7f05;color:#ffd054;border:2px solid #cf9706;text-shadow:1px 1px 0 rgba(0,0,0,.5)}')
addGlobalStyle('.room.soon{background-color:#5c5b58;color:#b0aca3;border:2px solid #6d6b68;text-shadow:1px 1px 0 rgba(0,0,0,.5)}')
addGlobalStyle('#scanningRooms{font-size: 13px;color: #fff;text-align: center;font-family: Arial, Helvetica, Verdana, sans-serif;line-height: 17px;text-shadow: 1px 1px 0px rgba(0, 0, 0, 0.3);}')

$J('.section_overview').prepend(
    $J('

')
)

$J('.section_play').append(
    $J('

')
)

function addRoom(room, size, level, watch) {
    if ($J('#'+room))
        $J('#'+room).remove()

    if (watch) {
        $J('#watchedRooms').append(
            $J('

')
        )
    } else {
        $J('#newRooms').append(
            $J('

')
        )
    }

    _room = $J('#'+room)

    _room.append(
        $J('
'+room+'
')
    )
    _room.append(
        $J('
'+size+'
')
    )
    if (level > 0) {
        _room.append(
            $J('
lvl '+FormatNumberForDisplay(level)+'
')
        )
    } else {
        _room.append(
            $J('
-
')
        )
    }
    _room.append(
        $J('Join')
    )

    if (size >= 1500)
        _room.addClass('full')
    else if (level < 0)
        _room.addClass('soon')
    else if (level < 1)
        _room.addClass('filling')
    else
        _room.addClass('started')
     if (level > levelThreshhold)
        if (!watch)
            _room.remove()
}

function checkRoom(room, watch) {
    watch = typeof watch !== 'undefined' ? watch : false

    GM_xmlhttpRequest({
        method: "GET",
        url: 'http://steamapi-a.akamaihd.net/ITowerAttackMiniGameService/GetGameData/v0001/?gameid='+room+'&include_stats=1',
        onload: function(response) {
            json = $J.parseJSON(response.responseText);
            try {
                playerCount = json.response.stats.num_players
                level = json.response.game_data.level

                addRoom(room, playerCount, level, watch)
                $J('h2>small').html(lastRoomCheck)
                if (room > lastRoomCheck)
                    lastRoomCheck = room
            } catch (e) {
                if (room < lastRoomCheck + 6) {
                    addRoom(room, '-', -1, watch)
                }
            }
            $J('#watchedRooms').sortDivs()
            $J('#newRooms').sortDivs()
        }
    });
    $J('#scanningRooms').html('Scanning Room: ' + lastRoomCheck)
}

function checkRooms(start, range) {
    for (var i = start; i <= start+range; i++) {
        checkRoom(i)
    }
    setTimeout(function(){
        if (!$J('#newRooms>div').length)
            checkRooms(lastRoomCheck, checkSize/2)
    }, 150)
}

function refreshWatchedRooms() {
    $J.each($J('#watchedRooms>div'), function(index) {
        checkRoom($J(this).attr('id'), true)
    });
}

function refreshNewRooms() {
    /*
    $J.each($J('#newRooms>div'), function(index) {
        if ($J(this).hasClass('started')) {
            $J(this).remove()
        }
    });
    */
    $J.each($J('#newRooms>div'), function(index) {
        if (!$J(this).hasClass('soon'))
            checkRoom($J(this).attr('id'))
    });
    checkRooms(lastRoomCheck, checkSize)
}

function findNewRooms() {
    for (var i = lastRoomCheck; i <= lastRoomCheck+1000; i+=10) {
        isValid = validRoom(i)
        if (!isValid)
            break
    }
    lastRoomCheck = i-10
    refreshNewRooms()
    setInterval(refreshNewRooms, refreshSpeed)
}

function validRoom(room) {
    GM_xmlhttpRequest({
        method: "GET",
        url: 'http://steamapi-a.akamaihd.net/ITowerAttackMiniGameService/GetGameData/v0001/?gameid='+room+'&include_stats=1',
        onload: function(response) {
            json = $J.parseJSON(response.responseText);
            try {
                if (json.response.stats.num_players)
                    return true
                else
                    return false
            } catch (e) {}
        }
    });

    return false
}

setTimeout(findNewRooms, 250)
<|endoftext|>


<commit_before>'use strict';

import React, { Component } from 'react';
import { Actions, Router, Scene } from 'react-native-router-flux';
import { connect, Provider } from 'react-redux';
import { createStore } from 'redux';

const RouterWithRedux = connect()(Router);
import allReducers from './reducers';

const store = createStore(allReducers);

import { styles } from './styles';

import Home from './components/Home';
import NewSentence from './components/NewSentence';
import RecordSentence from './components/RecordSentence';

export default class App extends Component {

  render() {
    return (
      <Provider store={store}>
        <RouterWithRedux>
          <Scene key="root">
            <Scene
              key="home"
              component={Home}
              title="Sentence Mosaics"
              type="reset" // Clear navigation stack
              initial={true} />
            <Scene
              key="newSentence"
              component={NewSentence}
              title="New Sentence"
              onRight={() => Actions.recordSentence()}
              rightTitle="Submit" />
            <Scene
              key="recordSentence"
              component={RecordSentence}
              title="Record Sentence"
              onRight={() => Actions.home()}
              rightTitle="Done" />
          </Scene>
        </RouterWithRedux>
      </Provider>
    );
  }
}<commit_msg>record sentence -> read sentence fix #9<commit_after>'use strict';

import React, { Component } from 'react';
import { Actions, Router, Scene } from 'react-native-router-flux';
import { connect, Provider } from 'react-redux';
import { createStore } from 'redux';

const RouterWithRedux = connect()(Router);
import allReducers from './reducers';

const store = createStore(allReducers);

import { styles } from './styles';

import Home from './components/Home';
import NewSentence from './components/NewSentence';
import RecordSentence from './components/RecordSentence';

export default class App extends Component {

  render() {
    return (
      <Provider store={store}>
        <RouterWithRedux>
          <Scene key="root">
            <Scene
              key="home"
              component={Home}
              title="Sentence Mosaics"
              type="reset" // Clear navigation stack
              initial={true} />
            <Scene
              key="newSentence"
              component={NewSentence}
              title="New Sentence"
              onRight={() => Actions.recordSentence()}
              rightTitle="Submit" />
            <Scene
              key="recordSentence"
              component={RecordSentence}
              title="Read Sentence" // Temp title until we add recording
              onRight={() => Actions.home()}
              rightTitle="Done" />
          </Scene>
        </RouterWithRedux>
      </Provider>
    );
  }
}<|endoftext|>


<commit_before>
'use strict';
// Storing multiple constant values inside of an object
// Keep in mind the values in the object mean they can be modified
// Which makes no sense for a constant, use wisely if you do this
var app = angular.module('retailerStock', ['ui.router', 'ngStorage', 'ngCookies', 'oitozero.ngSweetAlert', 'ui.bootstrap', /*'ui.calendar',
    */'ngAnimate', 'ngMessages'/*,'ngValidate','ngFileUpload', 'angular-confirm','textAngular' ,'chart.js' ,'angularMoment','uiSwitch'*/])
        .constant({
          'SERVER_URL': (location.hostname === "localhost" || location.hostname === "127.0.0.1") ? 'http://localhost:3000/' : 'https://retailerstock.herokuapp.com/'

});

app.run(['$rootScope', 'SERVER_URL', '$templateCache',
  function($rootScope, SERVER_URL, $templateCache) {

    $rootScope.TEMPLATE_URL = SERVER_URL+'views/';
    $rootScope.IMG_URL      = SERVER_URL+'images/';
    $rootScope.FILE_URL     = SERVER_URL+'files/';

    var $injector2 = window.angular.injector(['ng']);
    $injector2.invoke(['$qProvider', function($qProvider) {
      $qProvider.errorOnUnhandledRejections(false);
    }]);

    var clearTemplateCache = function () {
      $templateCache.removeAll();
    };

    $rootScope.$on('$viewContentLoaded', clearTemplateCache);
    $rootScope.$on('$stateChangeStart', clearTemplateCache);

}]);
<commit_msg>Additional Changes<commit_after>
'use strict';
// Storing multiple constant values inside of an object
// Keep in mind the values in the object mean they can be modified
// Which makes no sense for a constant, use wisely if you do this
var app = angular.module('retailerStock', ['ui.router', 'ngStorage', 'ngCookies', 'oitozero.ngSweetAlert', 'ui.bootstrap', /*'ui.calendar',
    */'ngAnimate', 'ngMessages'/*,'ngValidate','ngFileUpload', 'angular-confirm','textAngular' ,'chart.js' ,'angularMoment','uiSwitch'*/])
        .constant({
          'SERVER_URL': (location.hostname === "localhost" || location.hostname === "127.0.0.1") ? 'http://localhost:3000/' : 'https://retailerstock.herokuapp.com/'

});

app.run(['$rootScope', 'SERVER_URL', '$templateCache', '$qProvider',
  function($rootScope, SERVER_URL, $templateCache, $qProvider) {

    $rootScope.TEMPLATE_URL = SERVER_URL+'views/';
    $rootScope.IMG_URL      = SERVER_URL+'images/';
    $rootScope.FILE_URL     = SERVER_URL+'files/';

    var $injector2 = window.angular.injector(['ng']);
    $injector2.invoke(['$qProvider', function($qProvider) {
      $qProvider.errorOnUnhandledRejections(false);
    }]);

    var clearTemplateCache = function () {
      $templateCache.removeAll();
    };

    $rootScope.$on('$viewContentLoaded', clearTemplateCache);
    $rootScope.$on('$stateChangeStart', clearTemplateCache);

}]);
<|endoftext|>


<commit_before>/*! JSON.minify()
  v0.1 (c) Kyle Simpson
  MIT License
*/

(function(global){
  if (typeof global.JSON == "undefined" || !global.JSON) {
    global.JSON = {};
  }

  global.JSON.minify = function(json) {

    var tokenizer = /"|(\/\*)|(\*\/)|(\/\/)|\n|\r/g,
      in_string = false,
      in_multiline_comment = false,
      in_singleline_comment = false,
      tmp, tmp2, new_str = [], ns = 0, from = 0, lc, rc
    ;

    tokenizer.lastIndex = 0;

    while (tmp = tokenizer.exec(json)) {
      lc = RegExp.leftContext;
      rc = RegExp.rightContext;
      if (!in_multiline_comment && !in_singleline_comment) {
        tmp2 = lc.substring(from);
        if (!in_string) {
          tmp2 = tmp2.replace(/(\n|\r|\s)*/g,"");
        }
        new_str[ns++] = tmp2;
      }
      from = tokenizer.lastIndex;

      if (tmp[0] == "\"" && !in_multiline_comment && !in_singleline_comment) {
        tmp2 = lc.match(/(\\)*$/);
        if (!in_string || !tmp2 || (tmp2[0].length % 2) == 0) { // start of string with ", or unescaped " character found to end string
          in_string = !in_string;
        }
        from--; // include " character in next catch
        rc = json.substring(from);
      }
      else if (tmp[0] == "/*" && !in_string && !in_multiline_comment && !in_singleline_comment) {
        in_multiline_comment = true;
      }
      else if (tmp[0] == "*/" && !in_string && in_multiline_comment && !in_singleline_comment) {
        in_multiline_comment = false;
      }
      else if (tmp[0] == "//" && !in_string && !in_multiline_comment && !in_singleline_comment) {
        in_singleline_comment = true;
      }
      else if ((tmp[0] == "\n" || tmp[0] == "\r") && !in_string && !in_multiline_comment && in_singleline_comment) {
        in_singleline_comment = false;
      }
      else if (!in_multiline_comment && !in_singleline_comment && !(/\n|\r|\s/.test(tmp[0]))) {
        new_str[ns++] = tmp[0];
      }
    }
    new_str[ns++] = rc;
    return new_str.join("");
  };
})(typeof global !== 'undefined' ? global : this);
<commit_msg>Strip out trailing commas from JSON files<commit_after>/*! JSON.minify()
  v0.1 (c) Kyle Simpson
  MIT License
*/

(function(global){
  if (typeof global.JSON == "undefined" || !global.JSON) {
    global.JSON = {};
  }

  global.JSON.minify = function(json) {

    var tokenizer = /"|(\/\*)|(\*\/)|(\/\/)|\n|\r/g,
      in_string = false,
      in_multiline_comment = false,
      in_singleline_comment = false,
      tmp, tmp2, new_str = [], ns = 0, from = 0, lc, rc
    ;

    tokenizer.lastIndex = 0;

    while (tmp = tokenizer.exec(json)) {
      lc = RegExp.leftContext;
      rc = RegExp.rightContext;
      if (!in_multiline_comment && !in_singleline_comment) {
        tmp2 = lc.substring(from);
        if (!in_string) {
          tmp2 = tmp2.replace(/(\n|\r|\s)*/g,"");
        }
        new_str[ns++] = tmp2;
      }
      from = tokenizer.lastIndex;

      if (tmp[0] == "\"" && !in_multiline_comment && !in_singleline_comment) {
        tmp2 = lc.match(/(\\)*$/);
        if (!in_string || !tmp2 || (tmp2[0].length % 2) == 0) { // start of string with ", or unescaped " character found to end string
          in_string = !in_string;
        }
        from--; // include " character in next catch
        rc = json.substring(from);
      }
      else if (tmp[0] == "/*" && !in_string && !in_multiline_comment && !in_singleline_comment) {
        in_multiline_comment = true;
      }
      else if (tmp[0] == "*/" && !in_string && in_multiline_comment && !in_singleline_comment) {
        in_multiline_comment = false;
      }
      else if (tmp[0] == "//" && !in_string && !in_multiline_comment && !in_singleline_comment) {
        in_singleline_comment = true;
      }
      else if ((tmp[0] == "\n" || tmp[0] == "\r") && !in_string && !in_multiline_comment && in_singleline_comment) {
        in_singleline_comment = false;
      }
      else if (!in_multiline_comment && !in_singleline_comment && !(/\n|\r|\s/.test(tmp[0]))) {
        new_str[ns++] = tmp[0];
      }
    }
    new_str[ns++] = rc;
    return new_str.join("").replace(/\,([\]\}])/g, '$1');
  };
})(typeof global !== 'undefined' ? global : this);
<|endoftext|>


<commit_before>/**
 * Generate node module API markdown with dox
 *
 * Licensed under MIT.
 * Copyright (c) 2013 David Smith <https://github.com/codeactual/>
 */

/*jshint node:true*/
'use strict';

/**
 * GitemplateDox constructor.
 */
exports.GitemplateDox = GitemplateDox;

/**
 * Create a new GitemplateDox.
 *
 * @return {object}
 */
exports.create = function() { return new GitemplateDox(); };

/**
 * Extend GitemplateDox.prototype.
 *
 * @param {object} ext
 * @return {object} Merge result.
 */
exports.extend = function(ext) { return extend(GitemplateDox.prototype, ext); };

var dox = require('dox');
var fs = require('fs');
var path = require('path');
var util = require('util');
var sprintf = util.format;

var requireComponent = require('../component/require');
var extend = requireComponent('extend');
var configurable = requireComponent('configurable.js');

/**
 * Usage:
 *
 *     var dox = require('gitemplate-dox').create();
 *     var markdown = dox
 *       .set('input', '/path/to/source.js')
 *       .set('output', '/path/to/output.md')
 *       .parse()
 *       .convert();
 *
 * Configuration:
 *
 * - `{string} [input]` Source file to read
 * - `{string} [output]` Markdown file to write
 *
 * Properties:
 *
 * - `{array} [comments=[]]` Filtered dox-provided objects to convert
 * - `{object} [fileComment={}]` First dox-provided comment found in the file
 * - `{array} [lines=[]]` Markdown lines
 * - `{object} [params={}]` Collected `@param` meta indexed by method name
 *   - `{array} types` Type names
 *   - `{string} description` First line
 *   - `{array} overflow` Additional lines
 * - `{object} [returns={}]` Collected `@return` metadata indexed by method name
 *   - `{array} types` Type names
 *   - `{string} description` First line
 *   - `{array} overflow` Additional lines
 * - `{array} [sees=[]]` Collected `@see` lines
 * - `{array} [toc=[]]` Collected table-of-contents metadata objects
 *   - `{string} title` Link title
 *   - `{string} url` Link URL
 *
 */
function GitemplateDox() {
  this.settings = {input: '', output: ''};
  this.comments = [];
  this.fileComment = {};
  this.lines = [];
  this.params = {};
  this.returns = {};
  this.sees = [];
  this.toc = [];
}

configurable(GitemplateDox.prototype);

// ==== PUBLIC ====

/**
 * Parse the source file.
 *
 * @param {string} file
 * @see GitemplateDox.prototype.parseComment
 */
GitemplateDox.prototype.parse = function() {
  var text = fs.readFileSync(this.get('input')).toString();
  dox.parseComments(text, {raw: true}).forEach(this.parseComment.bind(this));
};

/**
 * Convert comments to markdown.
 *
 * @return {string}
 */
GitemplateDox.prototype.convert = function() {
  this.comments.forEach(this.collectParams.bind(this));
  this.comments.forEach(this.convertOne.bind(this));
  this.prependTOC();
  return this.lines.join('\n').trim() + '\n';
};

// ==== PRIVATE ====

/**
 * Convert one parsed comment to markdown line(s).
 *
 * @param {Markdown} Line collection
 * @param {object} comment
 * @api private
 */
GitemplateDox.prototype.convertOne = function(comment) {
  this.collectReturns(comment);
  this.collectTOC(comment);
  this.collectSees(comment);

  this.convertHeading(comment);
  this.convertSummary(comment);
  this.convertJsBlocks(comment);
  this.convertParams(comment);
  this.convertReturns(comment);
  this.convertSees(comment);
};

/**
 * Filter comment objects for conversion to text.
 *
 * @param {object} comment Attributes extracted by `dox`.
 * @param {number} idx Iteration
 * @api private
 * @see GitemplateDox.prototype.parse
 */
GitemplateDox.prototype.parseComment = function(comment, idx) {
  if (!idx) { this.fileComment = comment; return; }
  if (!comment.ctx && !this.exportName(comment)) { return; } // Ex. jshint
  if (comment.isPrivate) { return; } // @api private
  if (/^var/.test(comment.code)) { return; } // Module-private var
  this.comments.push(comment);
};

/**
 * @api private
 */
GitemplateDox.prototype.prependTOC = function() {
  var source = path.relative(this.get('output'), this.get('input'));

  var lines = [];

  lines.push('# Contents');
  lines.push('');
  lines.push(this.linkSymbols(this.fileComment.description.summary, this.fileComment));
  lines.push('');
  lines.push(sprintf('Source: [%s](%s)', source, source));
  lines.push('');
  this.toc.forEach(function(entry) {
    lines.push(sprintf('- [%s](%s)', entry.title, entry.url));
  });

  this.lines = lines.concat(this.lines);
};

/**
 * @api private
 */
GitemplateDox.prototype.collectTOC = function(comment) {
  var method = this.method(comment);
  this.toc.push({
    title: method,
    url: '#' + this.slugMethod(method)
  });
};

/**
 * @api private
 */
GitemplateDox.prototype.collectParams = function(comment) {
  if (!comment.tags) { return; }

  var self = this;
  var method = this.method(comment);
  var lastParam;

  this.params[method] = {};

  comment.tags.forEach(function(tag) {
    if ('param' === tag.type) {
      self.params[method][tag.name] = {
        types: tag.types,
        description: tag.description,
        overflow: []
      };
      lastParam = tag.name;
    } else if ('' === tag.type && lastParam) {
      self.params[method][lastParam].overflow.push(tag.string);
    } else { // Ex. @return
      lastParam = null;
    }
  });
};

/**
 * @api private
 */
GitemplateDox.prototype.collectReturns = function(comment) {
  if (!comment.tags) { return; }

  var self = this;
  var method = this.method(comment);
  var collectOverflow = false;

  comment.tags.forEach(function(tag) {
    if ('return' === tag.type) {
      self.returns[method] = {
        types: tag.types,
        description: tag.description,
        overflow: []
      };
      collectOverflow = true;
    } else if ('' === tag.type && collectOverflow) {
      self.returns[method].overflow.push(tag.string);
    } else { // Ex. @see
      collectOverflow = false;
    }
  });
};

/**
 * @api private
 */
GitemplateDox.prototype.collectSees = function(comment) {
  if (!comment.tags) { return; }

  var self = this;
  var method = this.method(comment);

  this.sees[method] = [];
  comment.tags.forEach(function(tag) {
    if ('see' === tag.type) {
      if (tag.url) {
        self.sees[method].push(sprintf('[%s](%s)', tag.title, tag.url));
      } else if (tag.local) {
        self.sees[method].push(tag.local);
      }
    }
  });
};

/**
 * @api private
 */
GitemplateDox.prototype.convertHeading = function(comment) {
  var method = this.method(comment);
  var heading = method + this.signature(method);
  this.h(1, this.linkSymbols(heading, comment));
};

/**
 * @api private
 */
GitemplateDox.prototype.convertSummary = function(comment) {
  this.p(this.linkSymbols(comment.description.summary, comment));
};

/**
 * @api private
 */
GitemplateDox.prototype.convertParams = function(comment) {
  var self = this;
  var method = this.method(comment);
  var names = Object.keys(this.params[method]);

  if (!names.length) { return; }

  this.h(2, 'Parameters');
  names.forEach(function(name) {
    var param = self.params[method][name];
    var heading = sprintf(
        '`{%s} %s`%s',
        param.types.join(' | '),
        name,
        param.description ? ': ' + self.linkSymbols(param.description, comment) : ''
    );
    self.h(3, heading);

    if (param.overflow.length) {
      self.newline();
      param.overflow.forEach(function(line) {
        self.lines.push(self.linkSymbols(line, comment));
      });
    }
  });
};

/**
 * @api private
 */
GitemplateDox.prototype.convertReturns = function(comment) {
  var self = this;
  var method = this.method(comment);
  var returns = this.returns[method];

  if (!returns) { return; }

  this.h(2, 'Return');

  var heading = sprintf(
    '`{%s}`%s',
    returns.types.join(' | '),
    returns.description ? ': ' + this.linkSymbols(returns.description, comment) : ''
  );
  this.h(3, heading);

  if (returns.overflow.length) {
    self.newline();
    returns.overflow.forEach(function(line) {
      self.lines.push(self.linkSymbols(line, comment));
    });
  }
};

/**
 * @api private
 */
GitemplateDox.prototype.convertSees = function(comment) {
  var self = this;
  var method = this.method(comment);
  var sees = this.sees[method];

  if (!sees.length) { return; }

  this.h(2, 'See');

  this.newline();
  sees.forEach(function(description) {
    self.lines.push('- ' + self.linkSymbols(description, comment));
  });
};

/**
 * @api private
 */
GitemplateDox.prototype.convertJsBlocks = function(comment) {
  if (!comment.description.body) { return; }

  // From https://github.com/visionmedia/dox/blob/master/lib/api.js
  var buf = comment.description.body;
  var code = buf.match(/^( {4}[^\n]+\n*)+/gm) || [];
  code.forEach(function(block){
    var code = block.replace(/^ {4}/gm, '');
    buf = buf.replace(block, '```js\n' + code.trimRight() + '\n```\n\n');
  });
  this.p(this.linkSymbols(buf, comment));
};

/**
 * @api private
 */
GitemplateDox.prototype.linkSymbols = function(text, comment) {
  var self = this;
  var methods = Object.keys(this.params);
  methods.sort(function(a, b) { // Replace longest symbols first
    return a.length > b.length ? -1 : (a.length === b.length ? 0 : 1);
  });

  var curMethod = this.method(comment);

  methods.forEach(function(method) {
    if (method === curMethod) { return; }
    var regex = new RegExp(

      // Not following '`' char(s), which possibly open a multi-line code block
      '(?!`+\\w?\\n?)' +

      '(^|\\s)(' + // Preceeded by nothing/whitespace

      method.replace(/\./, '\\.') +

      ')(\\s|$)' + // Followed by nothing/whitespace

      // Not followed by '`' char(s), which possibly close a multi-line code
      '(?!\\n?`+\\n?)',

      'gm'
    );
    text = text.replace(regex, sprintf('$1[%s](#%s)$3', method, self.slugMethod(method)));
  });
  return text;
};

// ==== MISC ====

GitemplateDox.prototype.h = function(level, text) {
  this.lines.push('\n' + (new Array(level + 1)).join('#') + ' ' + text);
};

GitemplateDox.prototype.p = function(text) {
  text = text.trim();
  this.lines.push('\n' + text);
};

GitemplateDox.prototype.newline = function(line) {
  this.lines.push('');
};

/**
 * Extract an exported property name.
 *
 * @param {object} comment dox-provided comment metadata
 * @return {string} Extracted name or null
 * @api private
 */
GitemplateDox.prototype.exportName = function(comment) {
  if (!comment.code) { return null; }
  var matches = comment.code.match(/^(module\.)?exports\.([^ ]+) =/);
  return matches ? matches[2] : null;
};

/**
 * @api private
 */
GitemplateDox.prototype.slug = function(raw) {
  return raw.replace(/[^a-zA-Z0-9- ]+/g, '').replace(/\s+/g, '-').toLowerCase();
};

GitemplateDox.prototype.slugMethod = function(method) {
  return this.slug(method + this.signature(method));
};

/**
 * @api private
 */
GitemplateDox.prototype.signature = function(method) {
  return '(' + Object.keys(this.params[method]).join(', ') + ')';
};

GitemplateDox.prototype.method = function(comment) {
  return comment.ctx ? comment.ctx.string.replace('()', '') : this.exportName(comment);
};
<commit_msg>docs(lib): Add comments<commit_after>/**
 * Generate node module API markdown with dox
 *
 * Licensed under MIT.
 * Copyright (c) 2013 David Smith <https://github.com/codeactual/>
 */

/*jshint node:true*/
'use strict';

/**
 * GitemplateDox constructor.
 */
exports.GitemplateDox = GitemplateDox;

/**
 * Create a new GitemplateDox.
 *
 * @return {object}
 */
exports.create = function() { return new GitemplateDox(); };

/**
 * Extend GitemplateDox.prototype.
 *
 * @param {object} ext
 * @return {object} Merge result.
 */
exports.extend = function(ext) { return extend(GitemplateDox.prototype, ext); };

var dox = require('dox');
var fs = require('fs');
var path = require('path');
var util = require('util');
var sprintf = util.format;

var requireComponent = require('../component/require');
var extend = requireComponent('extend');
var configurable = requireComponent('configurable.js');

/**
 * Usage:
 *
 *     var dox = require('gitemplate-dox').create();
 *     var markdown = dox
 *       .set('input', '/path/to/source.js')
 *       .set('output', '/path/to/output.md')
 *       .parse()
 *       .convert();
 *
 * Configuration:
 *
 * - `{string} [input]` Source file to read
 * - `{string} [output]` Markdown file to write
 *
 * Properties:
 *
 * - `{array} [comments=[]]` Filtered dox-provided objects to convert
 * - `{object} [fileComment={}]` First dox-provided comment found in the file
 * - `{array} [lines=[]]` Markdown lines
 * - `{object} [params={}]` Collected `@param` meta indexed by method name
 *   - `{array} types` Type names
 *   - `{string} description` First line
 *   - `{array} overflow` Additional lines
 * - `{object} [returns={}]` Collected `@return` metadata indexed by method name
 *   - `{array} types` Type names
 *   - `{string} description` First line
 *   - `{array} overflow` Additional lines
 * - `{array} [sees=[]]` Collected `@see` lines
 * - `{array} [toc=[]]` Collected table-of-contents metadata objects
 *   - `{string} title` Link title
 *   - `{string} url` Link URL
 *
 */
function GitemplateDox() {
  this.settings = {input: '', output: ''};
  this.comments = [];
  this.fileComment = {};
  this.lines = [];
  this.params = {};
  this.returns = {};
  this.sees = [];
  this.toc = [];
}

configurable(GitemplateDox.prototype);

// ===================== PUBLIC =====================

/**
 * Parse the source file.
 *
 * @param {string} file
 * @see GitemplateDox.prototype.parseComment
 */
GitemplateDox.prototype.parse = function() {
  var text = fs.readFileSync(this.get('input')).toString();
  dox.parseComments(text, {raw: true}).forEach(this.parseComment.bind(this));
};

/**
 * Convert comments to markdown.
 *
 * @return {string}
 */
GitemplateDox.prototype.convert = function() {
  this.comments.forEach(this.collectParams.bind(this));
  this.comments.forEach(this.convertOne.bind(this));
  this.prependTOC();
  return this.lines.join('\n').trim() + '\n';
};

// ===================== PRIVATE =====================

/**
 * Convert one parsed comment to markdown line(s).
 *
 * @param {Markdown} Line collection
 * @param {object} comment
 * @api private
 */
GitemplateDox.prototype.convertOne = function(comment) {
  this.collectReturns(comment);
  this.collectTOC(comment);
  this.collectSees(comment);

  this.convertMethodName(comment);
  this.convertSummary(comment);
  this.convertJsBlocks(comment);
  this.convertParams(comment);
  this.convertReturns(comment);
  this.convertSees(comment);
};

/**
 * Filter comment objects for conversion to text.
 *
 * @param {object} comment Attributes extracted by `dox`
 * @param {number} idx Iteration
 * @see GitemplateDox.prototype.parse
 * @api private
 */
GitemplateDox.prototype.parseComment = function(comment, idx) {
  if (!idx) { this.fileComment = comment; return; }
  if (!comment.ctx && !this.exportedProp(comment)) { return; } // Ex. jshint
  if (comment.isPrivate) { return; } // @api private
  if (/^var/.test(comment.code)) { return; } // Module-private var
  this.comments.push(comment);
};

/**
 * Prepend table-of-contents markdown lines.
 *
 * @api private
 */
GitemplateDox.prototype.prependTOC = function() {
  // From markdown dir to source file
  var source = path.relative(this.get('output'), this.get('input'));

  var lines = [];
  lines.push('# Contents');
  lines.push('');
  lines.push(this.linkSymbols(this.fileComment.description.summary, this.fileComment));
  lines.push('');
  lines.push(sprintf('Source: [%s](%s)', source, source));
  lines.push('');
  this.toc.forEach(function(entry) {
    lines.push(sprintf('- [%s](%s)', entry.title, entry.url));
  });
  this.lines = lines.concat(this.lines);
};

/**
 * Collect table-of-contents meta.
 *
 * @param {object} comment Attributes extracted by `dox`
 * @api private
 */
GitemplateDox.prototype.collectTOC = function(comment) {
  var method = this.method(comment);
  this.toc.push({
    title: method,
    url: '#' + this.slugMethod(method)
  });
};

/**
 * Collect `@param` meta.
 *
 * @param {object} comment Attributes extracted by `dox`
 * @api private
 */
GitemplateDox.prototype.collectParams = function(comment) {
  if (!comment.tags) { return; }

  var self = this;
  var method = this.method(comment);
  var prevParam;

  this.params[method] = {};

  comment.tags.forEach(function(tag) {
    if ('param' === tag.type) {
      self.params[method][tag.name] = {
        types: tag.types,
        description: tag.description,
        overflow: []
      };
      prevParam = tag.name;
    } else if ('' === tag.type && prevParam) {
      self.params[method][prevParam].overflow.push(tag.string);
    } else { // Ex. @return
      prevParam = null;
    }
  });
};

/**
 * Collect `@return` meta.
 *
 * @param {object} comment Attributes extracted by `dox`
 * @api private
 */
GitemplateDox.prototype.collectReturns = function(comment) {
  if (!comment.tags) { return; }

  var self = this;
  var method = this.method(comment);
  var collectOverflow = false;

  comment.tags.forEach(function(tag) {
    if ('return' === tag.type) {
      self.returns[method] = {
        types: tag.types,
        description: tag.description,
        overflow: []
      };
      collectOverflow = true;
    } else if ('' === tag.type && collectOverflow) {
      self.returns[method].overflow.push(tag.string);
    } else { // Ex. @see
      collectOverflow = false;
    }
  });
};

/**
 * Collect `@see` meta.
 *
 * @param {object} comment Attributes extracted by `dox`
 * @api private
 */
GitemplateDox.prototype.collectSees = function(comment) {
  if (!comment.tags) { return; }

  var self = this;
  var method = this.method(comment);

  this.sees[method] = [];
  comment.tags.forEach(function(tag) {
    if ('see' === tag.type) {
      if (tag.url) {
        self.sees[method].push(sprintf('[%s](%s)', tag.title, tag.url));
      } else if (tag.local) {
        self.sees[method].push(tag.local);
      }
    }
  });
};

/**
 * Convert the method name to a markdown.
 *
 * @param {object} comment Attributes extracted by `dox`
 * @api private
 */
GitemplateDox.prototype.convertMethodName = function(comment) {
  var method = this.method(comment);
  var heading = method + this.signature(method);
  this.h(1, this.linkSymbols(heading, comment));
};

/**
 * Convert the method description to markdown.
 *
 * @param {object} comment Attributes extracted by `dox`
 * @api private
 */
GitemplateDox.prototype.convertSummary = function(comment) {
  this.p(this.linkSymbols(comment.description.summary, comment));
};

/**
 * Convert the `@param` meta to markdown.
 *
 * @param {object} comment Attributes extracted by `dox`
 * @api private
 */
GitemplateDox.prototype.convertParams = function(comment) {
  var self = this;
  var method = this.method(comment);
  var names = Object.keys(this.params[method]);

  if (!names.length) { return; }

  this.h(2, 'Parameters');
  names.forEach(function(name) {
    var param = self.params[method][name];
    var heading = sprintf(
        '`{%s} %s`%s',
        param.types.join(' | '),
        name,
        param.description ? ': ' + self.linkSymbols(param.description, comment) : ''
    );
    self.h(3, heading);

    if (param.overflow.length) {
      self.newline();
      param.overflow.forEach(function(line) {
        self.lines.push(self.linkSymbols(line, comment));
      });
    }
  });
};

/**
 * Convert the `@return` meta to markdown.
 *
 * @param {object} comment Attributes extracted by `dox`
 * @api private
 */
GitemplateDox.prototype.convertReturns = function(comment) {
  var self = this;
  var method = this.method(comment);
  var returns = this.returns[method];

  if (!returns) { return; }

  this.h(2, 'Return');

  var heading = sprintf(
    '`{%s}`%s',
    returns.types.join(' | '),
    returns.description ? ': ' + this.linkSymbols(returns.description, comment) : ''
  );
  this.h(3, heading);

  if (returns.overflow.length) {
    self.newline();
    returns.overflow.forEach(function(line) {
      self.lines.push(self.linkSymbols(line, comment));
    });
  }
};

/**
 * Convert the `@see` meta to markdown.
 *
 * @param {object} comment Attributes extracted by `dox`
 * @api private
 */
GitemplateDox.prototype.convertSees = function(comment) {
  var self = this;
  var method = this.method(comment);
  var sees = this.sees[method];

  if (!sees.length) { return; }

  this.h(2, 'See');

  this.newline();
  sees.forEach(function(description) {
    self.lines.push('- ' + self.linkSymbols(description, comment));
  });
};

/**
 * Convert the 4-space indented blocks to markdown js-lang code blocks.
 *
 * @param {object} comment Attributes extracted by `dox`
 * @api private
 */
GitemplateDox.prototype.convertJsBlocks = function(comment) {
  if (!comment.description.body) { return; }

  // From https://github.com/visionmedia/dox/blob/master/lib/api.js
  var buf = comment.description.body;
  var code = buf.match(/^( {4}[^\n]+\n*)+/gm) || [];
  code.forEach(function(block){
    var code = block.replace(/^ {4}/gm, '');
    buf = buf.replace(block, '```js\n' + code.trimRight() + '\n```\n\n');
  });
  this.p(this.linkSymbols(buf, comment));
};

/**
 * Link all method name references to their GitHub generated heading anchors.
 *
 * @param {string} text
 * @param {object} comment Attributes extracted by `dox`
 * @return {string}
 * @api private
 */
GitemplateDox.prototype.linkSymbols = function(text, comment) {
  var self = this;
  var curMethod = this.method(comment);
  var methods = Object.keys(this.params);

  methods.sort(function(a, b) { // Replace longest symbols first
    return a.length > b.length ? -1 : (a.length === b.length ? 0 : 1);
  });

  methods.forEach(function(method) {
    if (method === curMethod) { return; }
    var regex = new RegExp(

      // Not following '`' char(s), which possibly open a multi-line code block
      '(?!`+\\w?\\n?)' +

      '(^|\\s)(' + // Preceeded by nothing/whitespace

      method.replace(/\./, '\\.') +

      ')(\\s|$)' + // Followed by nothing/whitespace

      // Not followed by '`' char(s), which possibly close a multi-line code
      '(?!\\n?`+\\n?)',

      'gm'
    );
    text = text.replace(regex, sprintf('$1[%s](#%s)$3', method, self.slugMethod(method)));
  });
  return text;
};

// ===================== MISC =====================

/**
 * Convert text to a markdown heading.
 *
 * @param {number} level Ex. 1 for <h1>
 * @param {string} text
 * @api private
 */
GitemplateDox.prototype.h = function(level, text) {
  this.lines.push('\n' + (new Array(level + 1)).join('#') + ' ' + text);
};

/**
 * Convert text to a markdown paragraph.
 *
 * @param {string} text
 * @api private
 */
GitemplateDox.prototype.p = function(text) {
  text = text.trim();
  this.lines.push('\n' + text);
};

/**
 * Add a blank line to the markdown output.
 *
 * @api private
 */
GitemplateDox.prototype.newline = function() {
  this.lines.push('');
};

/**
 * Extract an exported property name.
 *
 * @param {object} comment Attributes extracted by `dox`
 * @return {string} Extracted name or null
 * @api private
 */
GitemplateDox.prototype.exportedProp = function(comment) {
  if (!comment.code) { return null; }
  var matches = comment.code.match(/^(module\.)?exports\.([^ ]+) =/);
  return matches ? matches[2] : null;
};

/**
 * Build a GitHub-compatbible heading anchor slug.
 *
 * @param {string} text
 * @return {string}
 * @api private
 */
GitemplateDox.prototype.slug = function(text) {
  return text.replace(/[^a-zA-Z0-9- ]+/g, '').replace(/\s+/g, '-').toLowerCase();
};

/**
 * Build a slug for a method and its signature.
 *
 * @param {string} method
 * @return {string}
 * @api private
 */
GitemplateDox.prototype.slugMethod = function(method) {
  return this.slug(method + this.signature(method));
};

/**
 * Build a method's signature string.
 *
 * @param {string} method
 * @return {string} Ex. '(arg1, arg2)'
 * @api private
 */
GitemplateDox.prototype.signature = function(method) {
  return '(' + Object.keys(this.params[method]).join(', ') + ')';
};

/**
 * Extract a method name (ex. 'Klass') from its dox-provided version
 * (ex. 'Klass()').
 *
 * @param {string} method
 * @return {string} Ex. '(arg1, arg2)'
 * @api private
 */
GitemplateDox.prototype.method = function(comment) {
  return comment.ctx ? comment.ctx.string.replace('()', '') : this.exportedProp(comment);
};
<|endoftext|>


<commit_before>'use strict'
var clone = require('lodash.clonedeep')
var union = require('lodash.union')
var without = require('lodash.without')
var validate = require('aproba')
var flattenTree = require('./flatten-tree.js')
var npm = require('../npm.js')

var logicalTree = module.exports = function (tree) {
  validate('O', arguments)
  var newTree = clone(tree)
  var flat = flattenTree(newTree)
  function getNode (flatname) { return flat[flatname] }
  Object.keys(flat).sort().forEach(function (flatname) {
    var node = flat[flatname]
    var requiredBy = node.package._requiredBy || []
    var requiredByNames = requiredBy.filter(function (parentFlatname) {
      var parentNode = getNode(parentFlatname)
      if (!parentNode) return false
      return parentNode.package.dependencies[node.package.name] ||
             parentNode.package.devDependencies[node.package.name]
    })
    var requiredBy = requiredByNames.map(getNode)

    node.requiredBy = requiredBy

    if (!requiredBy.length) return

    if (node.parent) node.parent.children = without(node.parent.children, node)

    requiredBy.forEach(function (parentNode) {
      parentNode.children = union(parentNode.children, [node])
    })
  })
  return newTree
}

module.exports.asReadInstalled = function (tree) {
  return translateTree(logicalTree(tree))
}

function translateTree (tree) {
  var pkg = tree.package
  if (pkg._dependencies) return pkg
  pkg._dependencies = pkg.dependencies
  pkg.dependencies = {}
  tree.children.forEach(function (child) {
    pkg.dependencies[child.package.name] = translateTree(child)
  })
  Object.keys(tree.missingDeps).forEach(function (name) {
    if (pkg.dependencies[name]) {
      pkg.dependencies[name].invalid = true
      pkg.dependencies[name].realName = name
      pkg.dependencies[name].extraneous = false
    } else {
      pkg.dependencies[name] = tree.missingDeps[name]
    }
  })
  pkg.path = tree.path

  // All package-only requiredBys (exclude #USER and #EXISTING)
  var requiredBy = pkg._requiredBy.filter(function (req) { return req[0] !== '#' })
  var isDev = pkg._requiredBy.filter(function (req) { return req[0] === '#DEV' }).length

  var isTopLevel = tree.parent == null
  var isChildOfTop = !isTopLevel && tree.parent.parent == null
  var topHasNoPackageJson = isChildOfTop && tree.parent.package.name === undefined && tree.parent.package.version === undefined && tree.parent.package._id === undefined
  pkg.extraneous = !isTopLevel && (!isChildOfTop || !topHasNoPackageJson) && requiredBy.length === 0 && !isDev
  return pkg
}
<commit_msg>When generate a readInstalled type tree, include link information<commit_after>'use strict'
var clone = require('lodash.clonedeep')
var union = require('lodash.union')
var without = require('lodash.without')
var validate = require('aproba')
var flattenTree = require('./flatten-tree.js')
var npm = require('../npm.js')

var logicalTree = module.exports = function (tree) {
  validate('O', arguments)
  var newTree = clone(tree)
  var flat = flattenTree(newTree)
  function getNode (flatname) { return flat[flatname] }
  Object.keys(flat).sort().forEach(function (flatname) {
    var node = flat[flatname]
    var requiredBy = node.package._requiredBy || []
    var requiredByNames = requiredBy.filter(function (parentFlatname) {
      var parentNode = getNode(parentFlatname)
      if (!parentNode) return false
      return parentNode.package.dependencies[node.package.name] ||
             parentNode.package.devDependencies[node.package.name]
    })
    var requiredBy = requiredByNames.map(getNode)

    node.requiredBy = requiredBy

    if (!requiredBy.length) return

    if (node.parent) node.parent.children = without(node.parent.children, node)

    requiredBy.forEach(function (parentNode) {
      parentNode.children = union(parentNode.children, [node])
    })
  })
  return newTree
}

module.exports.asReadInstalled = function (tree) {
  return translateTree(logicalTree(tree))
}

function translateTree (tree) {
  var pkg = tree.package
  if (pkg._dependencies) return pkg
  pkg._dependencies = pkg.dependencies
  pkg.dependencies = {}
  tree.children.forEach(function (child) {
    pkg.dependencies[child.package.name] = translateTree(child)
  })
  Object.keys(tree.missingDeps).forEach(function (name) {
    if (pkg.dependencies[name]) {
      pkg.dependencies[name].invalid = true
      pkg.dependencies[name].realName = name
      pkg.dependencies[name].extraneous = false
    } else {
      pkg.dependencies[name] = tree.missingDeps[name]
    }
  })
  pkg.path = tree.path

  // All package-only requiredBys (exclude #USER and #EXISTING)
  var requiredBy = pkg._requiredBy.filter(function (req) { return req[0] !== '#' })
  var isDev = pkg._requiredBy.filter(function (req) { return req[0] === '#DEV' }).length

  var isTopLevel = tree.parent == null
  var isChildOfTop = !isTopLevel && tree.parent.parent == null
  var topHasNoPackageJson = isChildOfTop && tree.parent.package.name === undefined && tree.parent.package.version === undefined && tree.parent.package._id === undefined
  pkg.extraneous = !isTopLevel && (!isChildOfTop || !topHasNoPackageJson) && requiredBy.length === 0 && !isDev
  if (tree.target) pkg.link = tree.realpath
  return pkg
}
<|endoftext|>


<commit_before>'use strict';

var curl = require('../../curl');
var strip_ansi = require('strip-ansi');

function messageListener(db, from, channel, message, reply) {

  if (/^@wx/.test(message)) {
    var match = /^@wx\s+(.*)$/.exec(message);
    if (match) {
      var loc = match[1];

      curl( 'https://nominatim.openstreetmap.org/search/' + encodeURIComponent(loc) + '?format=json&limit=1',
            function (osmRes) {
              if (osmRes.length === 1) {

                curl( [ 'https://api.darksky.net/forecast/',
                         process.env.DARK_SKY_API_KEY,
                         '/',
                          osmRes[0].lat,
                         ',',
                         osmRes[0].lon
                      ].join(''),
                      function (dsRes) {
                        var summary =
                          [ 'Location: ', osmRes[0].display_name.split(', ')[0],
                            ', Temperature: ', dsRes.currently.temperature, '°',
                            ', Humidity: ', (dsRes.currently.humidity*100), '%',
                            ', Wind: ', dsRes.currently.windSpeed, ' mph',
                            ', Gusts: ', dsRes.currently.windGust, ' mph',
                            ', UV index: ', dsRes.currently.uvIndex,
                          ].join('');
                        reply({ to: channel, message: summary });
                      }
                    );

              } else {
                reply({ to: channel,
                        message: 'No location found for "' + loc + '"'
                      });
              }
            }
          );

    } else {
      reply({ to: channel, message: '@wx <location>' });
    }
  }

}

module.exports = messageListener;
module.exports.help =
  [ { cmd:'@wx',
      syntax: '@wx <location>',
      output: { success: [ 'Current weather conditions' ], failure: [] }
    }
  ];
<commit_msg>Add summary, and robustify location search<commit_after>'use strict';

var curl = require('../../curl');
var strip_ansi = require('strip-ansi');

function messageListener(db, from, channel, message, reply) {

  if (/^@wx/.test(message)) {
    var match = /^@wx\s+(.*)$/.exec(message);
    if (match) {
      var loc = match[1];

      curl( 'https://nominatim.openstreetmap.org/search?format=json&limit=1&q=' + encodeURIComponent(loc),
            function (osmRes) {
              if (osmRes.length === 1) {

                curl( [ 'https://api.darksky.net/forecast/',
                         process.env.DARK_SKY_API_KEY,
                         '/',
                          osmRes[0].lat,
                         ',',
                         osmRes[0].lon
                      ].join(''),
                      function (dsRes) {
                        var summary =
                          [ 'Location: ', osmRes[0].display_name.split(', ')[0],
                            ', ', dsRes.currently.summary,
                            ', Temperature: ', dsRes.currently.temperature, '°',
                            ', Humidity: ', (dsRes.currently.humidity*100), '%',
                            ', Wind: ', dsRes.currently.windSpeed, ' mph',
                            ', Gusts: ', dsRes.currently.windGust, ' mph',
                            ', UV index: ', dsRes.currently.uvIndex,
                          ].join('');
                        reply({ to: channel, message: summary });
                      }
                    );

              } else {
                reply({ to: channel,
                        message: 'No location found for "' + loc + '"'
                      });
              }
            }
          );

    } else {
      reply({ to: channel, message: '@wx <location>' });
    }
  }

}

module.exports = messageListener;
module.exports.help =
  [ { cmd:'@wx',
      syntax: '@wx <location>',
      output: { success: [ 'Current weather conditions' ], failure: [] }
    }
  ];
<|endoftext|>


<commit_before>
/*
	Initialize the Google Map
	Might include some kind of randomization later?
*/
function initialize() {
	var mapOptions = {
		center: new google.maps.LatLng(41.931124, -87.648044),
		zoom: 15
	};

	var map = new google.maps.Map(document.getElementById("map-canvas"), mapOptions);
}

/*
	Set the score at a location.
	Need to figure out how to put custom HTML as a pin.
*/
function display_score_at_location(location, score, lat, lng) {
	var lat_lng = new google.maps.LatLng(lat, lng);

	// Set up the map
	var mapOptions = {
		center: lat_lng,
		zoom: 15
	};

	// Create the map
	var map = new google.maps.Map(document.getElementById("map-canvas"), mapOptions);

	// Create a marker to attach the score info to
	var marker = new google.maps.Marker({
	    position: lat_lng,
	    map: map
	});

	// Render the score template
	var template = '
{{ location }}

{{ score }}
';
	var data = {score: score, location: location}
	var rendered = Mustache.render(template, data);

	// Set the score to an info window
	var infowindow = new google.maps.InfoWindow({
		content: rendered
	});

	// Apply the info window listener
	google.maps.event.addListener(marker, 'click', function() {
	    infowindow.open(map,marker);
	});

	// Start with the window open
	infowindow.open(map,marker);
}

function get_score(location) {
	var url = '/score/' + location;

	$.getJSON(url, function(data) {
		display_score_at_location(location, data.score, data.lat, data.lng);
	});
}

/*
	Register the click listener for the burrito submit button.
*/
$('#get_burrito_score').on('click', function() {
	var address = $('#address').val();
	if (address != '' && address != null) {
		get_score(address);
	}
});

/*
	Register the keypress listener so you can press enter
	to submit the address.
*/
$('#address').keydown(function(event){
	event.preventDefault();

	if (event.keyCode == 13) {
		$('#get_burrito_score').click();
	}
});

/*
	On page load
*/
$(function() {

	google.maps.event.addDomListener(window, 'load', initialize);

});<commit_msg>fixed input on map<commit_after>
/*
	Initialize the Google Map
	Might include some kind of randomization later?
*/
function initialize() {
	var mapOptions = {
		center: new google.maps.LatLng(41.931124, -87.648044),
		zoom: 15
	};

	var map = new google.maps.Map(document.getElementById("map-canvas"), mapOptions);
}

/*
	Set the score at a location.
	Need to figure out how to put custom HTML as a pin.
*/
function display_score_at_location(location, score, lat, lng) {
	var lat_lng = new google.maps.LatLng(lat, lng);

	// Set up the map
	var mapOptions = {
		center: lat_lng,
		zoom: 15
	};

	// Create the map
	var map = new google.maps.Map(document.getElementById("map-canvas"), mapOptions);

	// Create a marker to attach the score info to
	var marker = new google.maps.Marker({
	    position: lat_lng,
	    map: map
	});

	// Render the score template
	var template = '
{{ location }}

{{ score }}
';
	var data = {score: score, location: location}
	var rendered = Mustache.render(template, data);

	// Set the score to an info window
	var infowindow = new google.maps.InfoWindow({
		content: rendered
	});

	// Apply the info window listener
	google.maps.event.addListener(marker, 'click', function() {
	    infowindow.open(map,marker);
	});

	// Start with the window open
	infowindow.open(map,marker);
}

function get_score(location) {
	var url = '/score/' + location;

	$.getJSON(url, function(data) {
		display_score_at_location(location, data.score, data.lat, data.lng);
	});
}

/*
	Register the click listener for the burrito submit button.
*/
$('#get_burrito_score').on('click', function() {
	var address = $('#address').val();
	if (address != '' && address != null) {
		get_score(address);
	}
});

/*
	Register the keypress listener so you can press enter
	to submit the address.
*/
$('#address').keydown(function(event){
	if (event.keyCode == 13) {
		$('#get_burrito_score').click();
	}
});

/*
	On page load
*/
$(function() {

	google.maps.event.addDomListener(window, 'load', initialize);

});<|endoftext|>


<commit_before>var errorPrefix = 'Model#save([options], cb): ';

module.exports = function (Promise, utils, errors, models) {
  /**
   * @doc method
   * @id Model.instance_methods:save
   * @name save
   * @description
   * Save this instance to the database.
   *
   * If this instance is new (has not yet been saved to the database), then a new row will be inserted into the database
   * with the current attributes of this instance. If this instance's Model was configured with "timestamps: true"
   * (which is default), then the new row will also be set with a "created" and an "updated" property, set to the UTC
   * datetime (of the database) at which the operation occurs.
   *
   * If the instance isn't new (update operation), then the row specified by the primary key of the instance will be
   * updated with the current attributes of the instance. If the Model for the instance was configured with
   * "timestamps: true" (which is default), then the "updated" property of the row specified by the primary key of the
   * instance will be updated with the UTC datetime (of the database) at which the operation occurs.
   *
   * ## Signature:
   * ```js
   * Model#save([options][, cb])
   * ```
   *
   * ## Example:
   *
   * ### Promise-style:
   * ```js
   *  var post = new Post({
	 *      author: 'John Anderson',
	 *      title: 'How to cook'
	 *  });
   *
   *  post.save().then(function (post) {
	 *      res.send(201, post.toJSON());
	 *  })
   *  .catch(reheat.support.ValidationError, function (err) {
	 *      res.send(400, err.errors);
	 *  })
   *  .error(function (err) {
	 *      res.send(500, err.message);
	 *  });
   * ```
   *
   * ### Node-style:
   * ```js
   *  var post = new Post({
	 *      author: 'John Anderson',
	 *      title: 'How to cook'
	 *  });
   *
   *  post.save(function (err, post) {
	 *      if (err) {
	 *          if (err instanceof reheat.support.IllegalArgumentError || err instanceof reheat.support.ValidationError) {
	 *              res.send(400, err.errors);
	 *          } else {
	 *              res.send(500, err.message);
	 *          }
	 *      } else {
	 *          if (post) {
	 *              res.send(201, post.toJSON());
	 *          } else {
	 *              res.send(404);
	 *          }
	 *      }
	 *  });
   * ```
   *
   * ## Throws/Rejects with:
   *
   * - `{ValidationError}`
   * - `{IllegalArgumentError}`
   * - `{UnhandledError}`
   *
   * @param {object=} options Optional configuration options. Properties:
   *
   * - `{boolean=false}` - `deepSave` - If `true`, call `save()` on any hasOne or hasMany relations currently
   * loaded into this model instance. Will attempt to set the proper foreign keys.
   *
   * @param {function=} cb Optional callback function for Node-style usage. Signature: `cb(err, instance)`. Arguments:
   *
   * - `{ValidationError|UnhandledError}` - `err` - `null` if no error occurs.
   * - `{object}` - `instance` - If no error occurs, a reference to the instance on which `save([options][, cb])` was called.
   * @returns {Promise} Promise.
   */
  function save(options, cb) {
    var relationsToMerge = {};
    var childRelations = {};
    var Model = this.constructor;

    if (utils.isFunction(options)) {
      cb = options;
      options = {};
    }
    options = options || {};
    if (cb && !utils.isFunction(cb)) {
      throw new errors.IllegalArgumentError(errorPrefix + 'cb: Must be a function!', { actual: typeof cb, expected: 'function' });
    }
    return Promise.resolve().bind(this)
      .then(function () {
        if (!utils.isObject(options)) {
          throw new errors.IllegalArgumentError(errorPrefix + 'options: Must be an object!', { actual: typeof options, expected: 'object' });
        }
      })
      .then(function () {
        if (Model.relations.belongsTo) {
          utils.forOwn(Model.relations.belongsTo, function (relation, modelName) {
            if (!models[modelName]) {
              throw new errors.RuntimeError(Model.name + ' Model defined belongsTo relationship to nonexistent ' + modelName + ' Model!');
            }

            var parent = this.get(relation.localField);

            if (parent) {
              relationsToMerge[relation.localField] = parent;
              utils.unset(this.attributes, relation.localField);
              console.warn('Cascade saving not supported yet! Failing to save "' + relation.localField + '". You must save it yourself!');
            }
          }, this);
        }

        if (Model.relations.hasMany) {
          utils.forOwn(Model.relations.hasMany, function (relation, modelName) {
            if (!models[modelName]) {
              throw new errors.RuntimeError(Model.name + ' Model defined hasMany relationship to nonexistent ' + modelName + ' Model!');
            }

            var children = this.get(relation.localField);

            if (children) {
              childRelations[relation.localField] = {
                value: children,
                isCollection: true,
                localField: relation.localField,
                foreignKey: relation.foreignKey
              };
              relationsToMerge[relation.localField] = children;
              utils.unset(this.attributes, relation.localField);
            }
          }, this);
        }

        if (Model.relations.hasOne) {
          utils.forOwn(Model.relations.hasOne, function (relation, modelName) {
            if (!models[modelName]) {
              throw new errors.RuntimeError(Model.name + ' Model defined hasOne relationship to nonexistent ' + modelName + ' Model!');
            }

            var sibling = this.get(relation.localField);

            if (sibling) {
              childRelations[relation.localField] = {
                value: sibling,
                localField: relation.localField,
                foreignKey: relation.foreignKey
              };
              relationsToMerge[relation.localField] = sibling;
              utils.unset(this.attributes, relation.localField);
            }
          }, this);
        }

        if (this.isNew() && Model.schema && Model.schema.defaults) {
          Model.schema.addDefaultsToTarget(this.attributes);
        }

        options.validate = 'validate' in options ? options.validate : true;

        if (options.validate) {
          return Promise.promisify(this.beforeValidate, this)().bind(this)
            .then(function () {
              return Promise.promisify(this.validate, this)().bind(this);
            })
            .then(function () {
              return Promise.promisify(this.afterValidate, this)().bind(this);
            });
        }
      })
      .then(function () {
        return this.isNew() ? Promise.promisify(this.beforeCreate, this)().bind(this).return(Model) : Promise.promisify(this.beforeUpdate, this)().bind(this).return(Model);
      })
      .then(function (Model) {
        if (Model.tableReady && Model.tableReady !== true) {
          return Model.tableReady.return(Model);
        } else {
          return Model;
        }
      })
      .then(function (Model) {
        if (Model.timestamps) {
          if (this.isNew()) {
            utils.deepMixIn(this.attributes, { deleted: null, updated: Model.r.now(), created: Model.r.now() });
          } else {
            utils.deepMixIn(this.attributes, { updated: Model.r.now() });
          }
        }
        return Model.connection.run(Model.r.table(Model.tableName).insert(this.attributes, { returnVals: true, upsert: true }));
      })
      .then(function (cursor) {
        if (cursor.errors !== 0) {
          throw new Error(cursor.first_error);
        } else {
          this.previousAttributes = utils.merge({}, cursor.old_val);
          this.meta = utils.merge({}, cursor);
          utils.deepMixIn(this.attributes, utils.merge({}, cursor.new_val));
          utils.deepMixIn(this.attributes, relationsToMerge);

          var tasks = [];

          if (options.deepSave) {
            utils.forOwn(childRelations, function (child) {
              if (child.isCollection) {
                child.value.forEach(function (item) {
                  item.setSync(child.foreignKey, this.get(Model.idAttribute));
                }, this);
              } else {
                child.value.setSync(child.foreignKey, this.get(Model.idAttribute));
              }
              tasks.push(child.value.save(options));
            }, this);
          }

          if (tasks.length) {
            return Promise.all(tasks);
          }
        }
      })
      .then(function () {
        return this.meta.inserted ? Promise.promisify(this.afterCreate, this)(this) : Promise.promisify(this.afterUpdate, this)(this);
      })
      .finally(function () {
        relationsToMerge = null;
        childRelations = {};
      }).nodeify(cb);
  }

  return save;
};

<commit_msg>Removed upsert.<commit_after>var errorPrefix = 'Model#save([options], cb): ';

module.exports = function (Promise, utils, errors, models) {
  /**
   * @doc method
   * @id Model.instance_methods:save
   * @name save
   * @description
   * Save this instance to the database.
   *
   * If this instance is new (has not yet been saved to the database), then a new row will be inserted into the database
   * with the current attributes of this instance. If this instance's Model was configured with "timestamps: true"
   * (which is default), then the new row will also be set with a "created" and an "updated" property, set to the UTC
   * datetime (of the database) at which the operation occurs.
   *
   * If the instance isn't new (update operation), then the row specified by the primary key of the instance will be
   * updated with the current attributes of the instance. If the Model for the instance was configured with
   * "timestamps: true" (which is default), then the "updated" property of the row specified by the primary key of the
   * instance will be updated with the UTC datetime (of the database) at which the operation occurs.
   *
   * ## Signature:
   * ```js
   * Model#save([options][, cb])
   * ```
   *
   * ## Example:
   *
   * ### Promise-style:
   * ```js
   *  var post = new Post({
	 *      author: 'John Anderson',
	 *      title: 'How to cook'
	 *  });
   *
   *  post.save().then(function (post) {
	 *      res.send(201, post.toJSON());
	 *  })
   *  .catch(reheat.support.ValidationError, function (err) {
	 *      res.send(400, err.errors);
	 *  })
   *  .error(function (err) {
	 *      res.send(500, err.message);
	 *  });
   * ```
   *
   * ### Node-style:
   * ```js
   *  var post = new Post({
	 *      author: 'John Anderson',
	 *      title: 'How to cook'
	 *  });
   *
   *  post.save(function (err, post) {
	 *      if (err) {
	 *          if (err instanceof reheat.support.IllegalArgumentError || err instanceof reheat.support.ValidationError) {
	 *              res.send(400, err.errors);
	 *          } else {
	 *              res.send(500, err.message);
	 *          }
	 *      } else {
	 *          if (post) {
	 *              res.send(201, post.toJSON());
	 *          } else {
	 *              res.send(404);
	 *          }
	 *      }
	 *  });
   * ```
   *
   * ## Throws/Rejects with:
   *
   * - `{ValidationError}`
   * - `{IllegalArgumentError}`
   * - `{UnhandledError}`
   *
   * @param {object=} options Optional configuration options. Properties:
   *
   * - `{boolean=false}` - `deepSave` - If `true`, call `save()` on any hasOne or hasMany relations currently
   * loaded into this model instance. Will attempt to set the proper foreign keys.
   *
   * @param {function=} cb Optional callback function for Node-style usage. Signature: `cb(err, instance)`. Arguments:
   *
   * - `{ValidationError|UnhandledError}` - `err` - `null` if no error occurs.
   * - `{object}` - `instance` - If no error occurs, a reference to the instance on which `save([options][, cb])` was called.
   * @returns {Promise} Promise.
   */
  function save(options, cb) {
    var relationsToMerge = {};
    var childRelations = {};
    var Model = this.constructor;

    if (utils.isFunction(options)) {
      cb = options;
      options = {};
    }
    options = options || {};
    if (cb && !utils.isFunction(cb)) {
      throw new errors.IllegalArgumentError(errorPrefix + 'cb: Must be a function!', { actual: typeof cb, expected: 'function' });
    }
    return Promise.resolve().bind(this)
      .then(function () {
        if (!utils.isObject(options)) {
          throw new errors.IllegalArgumentError(errorPrefix + 'options: Must be an object!', { actual: typeof options, expected: 'object' });
        }
      })
      .then(function () {
        if (Model.relations.belongsTo) {
          utils.forOwn(Model.relations.belongsTo, function (relation, modelName) {
            if (!models[modelName]) {
              throw new errors.RuntimeError(Model.name + ' Model defined belongsTo relationship to nonexistent ' + modelName + ' Model!');
            }

            var parent = this.get(relation.localField);

            if (parent) {
              relationsToMerge[relation.localField] = parent;
              utils.unset(this.attributes, relation.localField);
              console.warn('Cascade saving not supported yet! Failing to save "' + relation.localField + '". You must save it yourself!');
            }
          }, this);
        }

        if (Model.relations.hasMany) {
          utils.forOwn(Model.relations.hasMany, function (relation, modelName) {
            if (!models[modelName]) {
              throw new errors.RuntimeError(Model.name + ' Model defined hasMany relationship to nonexistent ' + modelName + ' Model!');
            }

            var children = this.get(relation.localField);

            if (children) {
              childRelations[relation.localField] = {
                value: children,
                isCollection: true,
                localField: relation.localField,
                foreignKey: relation.foreignKey
              };
              relationsToMerge[relation.localField] = children;
              utils.unset(this.attributes, relation.localField);
            }
          }, this);
        }

        if (Model.relations.hasOne) {
          utils.forOwn(Model.relations.hasOne, function (relation, modelName) {
            if (!models[modelName]) {
              throw new errors.RuntimeError(Model.name + ' Model defined hasOne relationship to nonexistent ' + modelName + ' Model!');
            }

            var sibling = this.get(relation.localField);

            if (sibling) {
              childRelations[relation.localField] = {
                value: sibling,
                localField: relation.localField,
                foreignKey: relation.foreignKey
              };
              relationsToMerge[relation.localField] = sibling;
              utils.unset(this.attributes, relation.localField);
            }
          }, this);
        }

        if (this.isNew() && Model.schema && Model.schema.defaults) {
          Model.schema.addDefaultsToTarget(this.attributes);
        }

        options.validate = 'validate' in options ? options.validate : true;

        if (options.validate) {
          return Promise.promisify(this.beforeValidate, this)().bind(this)
            .then(function () {
              return Promise.promisify(this.validate, this)().bind(this);
            })
            .then(function () {
              return Promise.promisify(this.afterValidate, this)().bind(this);
            });
        }
      })
      .then(function () {
        return this.isNew() ? Promise.promisify(this.beforeCreate, this)().bind(this).return(Model) : Promise.promisify(this.beforeUpdate, this)().bind(this).return(Model);
      })
      .then(function (Model) {
        if (Model.tableReady && Model.tableReady !== true) {
          return Model.tableReady.return(Model);
        } else {
          return Model;
        }
      })
      .then(function (Model) {
        if (Model.timestamps) {
          if (this.isNew()) {
            utils.deepMixIn(this.attributes, { deleted: null, updated: Model.r.now(), created: Model.r.now() });
          } else {
            utils.deepMixIn(this.attributes, { updated: Model.r.now() });
          }
        }
        if (this.isNew()) {
          return Model.connection.run(Model.r.table(Model.tableName).insert(this.attributes, { returnVals: true }));
        } else {
          return Model.connection.run(Model.r.table(Model.tableName).get(this.get(Model.idAttribute)).update(this.attributes, { returnVals: true }));
        }
      })
      .then(function (cursor) {
        if (cursor.errors !== 0) {
          throw new Error(cursor.first_error);
        } else {
          this.previousAttributes = utils.merge({}, cursor.old_val);
          this.meta = utils.merge({}, cursor);
          utils.deepMixIn(this.attributes, utils.merge({}, cursor.new_val));
          utils.deepMixIn(this.attributes, relationsToMerge);

          var tasks = [];

          if (options.deepSave) {
            utils.forOwn(childRelations, function (child) {
              if (child.isCollection) {
                child.value.forEach(function (item) {
                  item.setSync(child.foreignKey, this.get(Model.idAttribute));
                }, this);
              } else {
                child.value.setSync(child.foreignKey, this.get(Model.idAttribute));
              }
              tasks.push(child.value.save(options));
            }, this);
          }

          if (tasks.length) {
            return Promise.all(tasks);
          }
        }
      })
      .then(function () {
        return this.meta.inserted ? Promise.promisify(this.afterCreate, this)(this) : Promise.promisify(this.afterUpdate, this)(this);
      })
      .finally(function () {
        relationsToMerge = null;
        childRelations = {};
      }).nodeify(cb);
  }

  return save;
};

<|endoftext|>


<commit_before>'use strict';

/**
 * 词典优化模块
 *
 * @author 老雷<leizongmin@gmail.com>
 */

var debug = console.log;

/** 模块类型 */
exports.type = 'optimizer';

/**
 * 模块初始化
 *
 * @param {Segment} segment 分词接口
 */
exports.init = function (segment) {
  exports.segment = segment;
};

/**
 * 词典优化
 *
 * @param {array} words 单词数组
 * @param {bool} is_not_first 是否为管理器调用的
 * @return {array}
 */
exports.doOptimize = function (words, is_not_first) {
  //debug(words);
  if (typeof is_not_first == 'undefined') {
    is_not_first = false;
  }
  // 合并相邻的能组成一个单词的两个词
  var TABLE = exports.segment.getDict('TABLE');
  var POSTAG = exports.segment.POSTAG;

  var i = 0;
  var ie = words.length - 1;
  while (i < ie) {
    var w1 = words[i];
    var w2 = words[i + 1];
    //debug(w1.w + ', ' + w2.w);

    // ==========================================
    // 能组成一个新词的(词性必须相同)
    var nw = w1.w + w2.w;
    if (w1.p == w2.p && nw in TABLE) {
      words.splice(i, 2, {
        w:  nw,
        p:  TABLE[nw].p
      });
      ie--;
      continue;
    }

    if ((w1.p & POSTAG.A_M) > 0) {
      // ============================================
      // 数词组合
      //debug(w2.w + ' ' + (w2.p & POSTAG.A_M));
      // 百分比数字 如 10%，或者下一个词也是数词，则合并
      if ((w2.p & POSTAG.A_M) > 0 || w2.w == '%') {
        words.splice(i, 2, {
          w:  w1.w + w2.w,
          p:  POSTAG.A_M
        });
        ie--;
        continue;
      }
      // 数词 + 量词，合并。如： 100个
      if ((w2.p & POSTAG.A_Q) > 0) {
        words.splice(i, 2, {
          w:  w1.w + w2.w,
          p:  POSTAG.D_MQ // 数量词
        });
        ie--;
        continue;
      }
      // 带小数点的数字 ，如 “3 . 14”，或者 “十五点三”
      // 数词 + "分之" + 数词，如“五十分之一”
      var w3 = words[i + 2];
      if (w3 && (w3.p & POSTAG.A_M) > 0 &&
      (w2.w == '.' || w2.w == '点' || w2.w == '分之')) {
        words.splice(i, 3, {
          w:  w1.w + w2.w + w3.w,
          p:  POSTAG.A_M
        });
        ie -= 2;
        continue;
      }
    }

    // 移到下一个词
    i++;
  }

  // 针对组合数字后无法识别新组合的数字问题，需要重新扫描一次
  return is_not_first === true ? words : exports.doOptimize(words, true);
};
<commit_msg>修正“十五点五八”识别问题<commit_after>'use strict';

/**
 * 词典优化模块
 *
 * @author 老雷<leizongmin@gmail.com>
 */

var debug = console.log;

/** 模块类型 */
exports.type = 'optimizer';

/**
 * 模块初始化
 *
 * @param {Segment} segment 分词接口
 */
exports.init = function (segment) {
  exports.segment = segment;
};

/**
 * 词典优化
 *
 * @param {array} words 单词数组
 * @param {bool} is_not_first 是否为管理器调用的
 * @return {array}
 */
exports.doOptimize = function (words, is_not_first) {
  //debug(words);
  if (typeof is_not_first == 'undefined') {
    is_not_first = false;
  }
  // 合并相邻的能组成一个单词的两个词
  var TABLE = exports.segment.getDict('TABLE');
  var POSTAG = exports.segment.POSTAG;

  var i = 0;
  var ie = words.length - 1;
  while (i < ie) {
    var w1 = words[i];
    var w2 = words[i + 1];
    //debug(w1.w + ', ' + w2.w);

    // ==========================================
    // 能组成一个新词的(词性必须相同)
    var nw = w1.w + w2.w;
    if (w1.p == w2.p && nw in TABLE) {
      words.splice(i, 2, {
        w:  nw,
        p:  TABLE[nw].p
      });
      ie--;
      continue;
    }

    if ((w1.p & POSTAG.A_M) > 0) {
      // ============================================
      // 数词组合
      //debug(w2.w + ' ' + (w2.p & POSTAG.A_M));
      // 百分比数字 如 10%，或者下一个词也是数词，则合并
      if ((w2.p & POSTAG.A_M) > 0 || w2.w == '%') {
        words.splice(i, 2, {
          w:  w1.w + w2.w,
          p:  POSTAG.A_M
        });
        ie--;
        continue;
      }
      // 数词 + 量词，合并。如： 100个
      if ((w2.p & POSTAG.A_Q) > 0) {
        words.splice(i, 2, {
          w:  w1.w + w2.w,
          p:  POSTAG.D_MQ // 数量词
        });
        ie--;
        continue;
      }
      // 带小数点的数字 ，如 “3 . 14”，或者 “十五点三”
      // 数词 + "分之" + 数词，如“五十分之一”
      var w3 = words[i + 2];
      if (w3 && (w3.p & POSTAG.A_M) > 0 &&
         (w2.w == '.' || w2.w == '点' || w2.w == '分之')) {
        words.splice(i, 3, {
          w:  w1.w + w2.w + w3.w,
          p:  POSTAG.A_M
        });
        ie -= 2;
        continue;
      }
    }

    // 修正 “十五点五八”问题
    if ((w1.p & POSTAG.D_MQ) > 0 && w1.w.substr(-1) === '点' && w2.p & POSTAG.A_M) {
      //debug(w1, w2);
      var i2 = 2;
      var w4w = '';
      for (var j = i + i2; j < ie; j++) {
        var w3 = words[j];
        if ((w3.p & POSTAG.A_M) > 0) {
          w4w += w3.w;
          i2++;
        } else {
          break;
        }
      }
      words.splice(i, i2, {
        w:  w1.w + w2.w + w4w,
        p:  POSTAG.D_MQ // 数量词
      });
      ie -= i2 - 1;
      continue;
    }

    // 移到下一个词
    i++;
  }

  // 针对组合数字后无法识别新组合的数字问题，需要重新扫描一次
  return is_not_first === true ? words : exports.doOptimize(words, true);
};
<|endoftext|>


<commit_before>import namehash from 'eth-ens-namehash';

/*global web3, EmbarkJS*/
let __embarkENS = {};

// resolver interface
__embarkENS.resolverInterface = [
  {
    "constant": true,
    "inputs": [
      {
        "name": "node",
        "type": "bytes32"
      }
    ],
    "name": "addr",
    "outputs": [
      {
        "name": "",
        "type": "address"
      }
    ],
    "type": "function"
  },
  {
    "constant": true,
    "inputs": [
      {
        "name": "node",
        "type": "bytes32"
      }
    ],
    "name": "content",
    "outputs": [
      {
        "name": "",
        "type": "bytes32"
      }
    ],
    "type": "function"
  },
  {
    "constant": true,
    "inputs": [
      {
        "name": "node",
        "type": "bytes32"
      }
    ],
    "name": "name",
    "outputs": [
      {
        "name": "",
        "type": "string"
      }
    ],
    "type": "function"
  },
  {
    "constant": true,
    "inputs": [
      {
        "name": "node",
        "type": "bytes32"
      },
      {
        "name": "kind",
        "type": "bytes32"
      }
    ],
    "name": "has",
    "outputs": [
      {
        "name": "",
        "type": "bool"
      }
    ],
    "type": "function"
  },
  {
    "constant": false,
    "inputs": [
      {
        "name": "node",
        "type": "bytes32"
      },
      {
        "name": "addr",
        "type": "address"
      }
    ],
    "name": "setAddr",
    "outputs": [],
    "type": "function"
  },
  {
    "constant": false,
    "inputs": [
      {
        "name": "node",
        "type": "bytes32"
      },
      {
        "name": "hash",
        "type": "bytes32"
      }
    ],
    "name": "setContent",
    "outputs": [],
    "type": "function"
  },
  {
    "constant": false,
    "inputs": [
      {
        "name": "node",
        "type": "bytes32"
      },
      {
        "name": "name",
        "type": "string"
      }
    ],
    "name": "setName",
    "outputs": [],
    "type": "function"
  },
  {
    "constant": true,
    "inputs": [
      {
        "name": "node",
        "type": "bytes32"
      },
      {
        "name": "contentType",
        "type": "uint256"
      }
    ],
    "name": "ABI",
    "outputs": [
      {
        "name": "",
        "type": "uint256"
      },
      {
        "name": "",
        "type": "bytes"
      }
    ],
    "payable": false,
    "type": "function"
  }
];

__embarkENS.registryAddresses = {
  // Mainnet
  "1": "0x314159265dd8dbb310642f98f50c066173c1259b",
  // Ropsten
  "3": "0x112234455c3a32fd11230c42e7bccd4a84e02010",
  // Rinkeby
  "4": "0xe7410170f87102DF0055eB195163A03B7F2Bff4A"
};

__embarkENS.setProvider = function () {
  const self = this;
  // get network id and then assign ENS contract based on that
  let registryAddresses = this.registryAddresses;
  this.ens = null;
  web3.eth.net.getId().then(id => {
    if (registryAddresses[id] !== undefined) {
      EmbarkJS.onReady(() => {
        self.ens = new EmbarkJS.Contract({abi: self.registryInterface, address: registryAddresses[id]});
      });
    }
    // todo: deploy at this point
  }).catch(e => {
    if (e.message.indexOf('Provider not set or invalid') > -1) {
      console.warn('ENS is not available in this chain');
      return;
    }
    console.error(e);
  });
};

__embarkENS.resolve = function (name) {
  const self = this;

  if (self.ens === undefined) return;

  let node = namehash.hash(name);

  return self.ens.methods.resolver(node).call().then((resolverAddress) => {
    let resolverContract = new EmbarkJS.Contract({abi: self.resolverInterface, address: resolverAddress});
    return resolverContract.methods.addr(node).call();
  }).then((addr) => {
    return addr;
  }).catch(err => err);
};

__embarkENS.lookup = function (address) {
  const self = this;

  if (self.ens === undefined) return;

  if (address.startsWith("0x")) address = address.slice(2);

  let node = namehash.hash(address.toLowerCase() + ".addr.reverse");

  return self.ens.methods.resolver(node).call().then((resolverAddress) => {
    let resolverContract = new EmbarkJS.Contract({abi: self.resolverInterface, address: resolverAddress});
    return resolverContract.methods.name(node).call();
  }).then((name) => {
    if (name === "" || name === undefined) {
      console.warn("ENS name not found");
      return;
    }
    return name;
  }).catch(err => err);
};

__embarkENS.register = function (name, options) {
  const self = this;

  if (self.ens === undefined) return;

  if (options.livenet) {
    return;
  }

  let node = namehash.hash(name);

  return self.ens.methods.owner(node).call().then((owner) => {
    if (owner !== "0x" || owner !== undefined) {
      console.warn("name has already been claimed, see owner: ", owner);
      return;
    }
    return self.ens.methods.setSubnodeOwner(node, );
  }).then((addr) => {

    return addr;
  }).catch(err => err);
}
<commit_msg>temp commit<commit_after>import namehash from 'eth-ens-namehash';

/*global web3, EmbarkJS*/
let __embarkENS = {};

// resolver interface
__embarkENS.resolverInterface = [
  {
    "constant": true,
    "inputs": [
      {
        "name": "node",
        "type": "bytes32"
      }
    ],
    "name": "addr",
    "outputs": [
      {
        "name": "",
        "type": "address"
      }
    ],
    "type": "function"
  },
  {
    "constant": true,
    "inputs": [
      {
        "name": "node",
        "type": "bytes32"
      }
    ],
    "name": "content",
    "outputs": [
      {
        "name": "",
        "type": "bytes32"
      }
    ],
    "type": "function"
  },
  {
    "constant": true,
    "inputs": [
      {
        "name": "node",
        "type": "bytes32"
      }
    ],
    "name": "name",
    "outputs": [
      {
        "name": "",
        "type": "string"
      }
    ],
    "type": "function"
  },
  {
    "constant": true,
    "inputs": [
      {
        "name": "node",
        "type": "bytes32"
      },
      {
        "name": "kind",
        "type": "bytes32"
      }
    ],
    "name": "has",
    "outputs": [
      {
        "name": "",
        "type": "bool"
      }
    ],
    "type": "function"
  },
  {
    "constant": false,
    "inputs": [
      {
        "name": "node",
        "type": "bytes32"
      },
      {
        "name": "addr",
        "type": "address"
      }
    ],
    "name": "setAddr",
    "outputs": [],
    "type": "function"
  },
  {
    "constant": false,
    "inputs": [
      {
        "name": "node",
        "type": "bytes32"
      },
      {
        "name": "hash",
        "type": "bytes32"
      }
    ],
    "name": "setContent",
    "outputs": [],
    "type": "function"
  },
  {
    "constant": false,
    "inputs": [
      {
        "name": "node",
        "type": "bytes32"
      },
      {
        "name": "name",
        "type": "string"
      }
    ],
    "name": "setName",
    "outputs": [],
    "type": "function"
  },
  {
    "constant": true,
    "inputs": [
      {
        "name": "node",
        "type": "bytes32"
      },
      {
        "name": "contentType",
        "type": "uint256"
      }
    ],
    "name": "ABI",
    "outputs": [
      {
        "name": "",
        "type": "uint256"
      },
      {
        "name": "",
        "type": "bytes"
      }
    ],
    "payable": false,
    "type": "function"
  }
];

__embarkENS.registryAddresses = {
  // Mainnet
  "1": "0x314159265dd8dbb310642f98f50c066173c1259b",
  // Ropsten
  "3": "0x112234455c3a32fd11230c42e7bccd4a84e02010",
  // Rinkeby
  "4": "0xe7410170f87102DF0055eB195163A03B7F2Bff4A"
};

__embarkENS.setProvider = function () {
  const self = this;
  // get network id and then assign ENS contract based on that
  let registryAddresses = this.registryAddresses;
  this.ens = null;
  web3.eth.net.getId().then(id => {
    if (registryAddresses[id] !== undefined) {
      EmbarkJS.onReady(() => {
        self.ens = new EmbarkJS.Contract({abi: self.registryInterface, address: registryAddresses[id]});
      });
    }
    // todo: deploy at this point
  }).catch(e => {
    if (e.message.indexOf('Provider not set or invalid') > -1) {
      console.warn('ENS is not available in this chain');
      return;
    }
    console.error(e);
  });
};

__embarkENS.resolve = function (name) {
  const self = this;

  if (self.ens === undefined) return;

  let node = namehash.hash(name);

  return self.ens.methods.resolver(node).call().then((resolverAddress) => {
    let resolverContract = new EmbarkJS.Contract({abi: self.resolverInterface, address: resolverAddress});
    return resolverContract.methods.addr(node).call();
  }).then((addr) => {
    return addr;
  }).catch(err => err);
};

__embarkENS.lookup = function (address) {
  const self = this;

  if (self.ens === undefined) return;

  if (address.startsWith("0x")) address = address.slice(2);

  let node = namehash.hash(address.toLowerCase() + ".addr.reverse");

  return self.ens.methods.resolver(node).call().then((resolverAddress) => {
    let resolverContract = new EmbarkJS.Contract({abi: self.resolverInterface, address: resolverAddress});
    return resolverContract.methods.name(node).call();
  }).then((name) => {
    if (name === "" || name === undefined) {
      console.warn("ENS name not found");
      return;
    }
    return name;
  }).catch(err => err);
};

/*__embarkENS.register = function (name, options) {
  const self = this;

  if (self.ens === undefined) return;

  if (options.livenet) {
    return;
  }

  let node = namehash.hash(name);

  return self.ens.methods.owner(node).call().then((owner) => {
    if (owner !== "0x" || owner !== undefined) {
      console.warn("name has already been claimed, see owner: ", owner);
      return;
    }
    return self.ens.methods.setSubnodeOwner(node, );
  }).then((addr) => {

    return addr;
  }).catch(err => err);
}*/
<|endoftext|>