<commit_before>// =============== Modules Start
var fs = require('fs');
var app = require('express')();
var http = require('http').Server(app);
var util = require('util');
var validator = require('validator');
// =============== Modules End

// =============== Main Start
var config = JSON.parse(fs.readFileSync('config.json', 'utf8'));
var port = config['port'];
var timeout = config['timeout'];
var maxPlayers = config['max_players'];
var motd = config['motd'];

var io = require('socket.io')(http, {
	'close timeout': timeout
});

var responses = {
	'/': 'index.html',
	'/?css': 'ch.css',
	'/?js': 'ch_client.js'
};

app.get('/', function(req, res){
	//res.send(util.inspect(req, false, null));
	var url = req['url'];

	if(url in responses)
		res.sendFile(__dirname + '/public/' + responses[url]);
	else res.send('No known response for ' + url);
});

http.listen(port, function() {
	console.log('HTTP Server running on port ' + port);
});

io.on('connection', function(socket) {
	gameserver.onConnect(socket);

	socket.on('disconnect', function() {
		gameserver.onDisconnect(socket);
	});

	// TODO IMPORTANT - Sanitize mesasge, no xss here.
	socket.on('chat-message', function(msg) {
		gameserver.onChatMessage(socket, msg);
	});
});
// =============== Main End

// =============== GameServer start
var GameServer = function() {
	this.games = [];
	this.players = {};
	this.playerCount = 0;
};
var gameserver = new GameServer();

GameServer.prototype.onConnect = function(socket) {
	console.log('Connection opened by socket with ID ' + socket.id);
	this.players[socket.id] = socket;
	this.playerCount++;
	this.sendMessageToClient(socket, 'Welcome to ClonedHumanity. There are ' + this.playerCount + '/' + maxPlayers + ' players online. | ' + 'MOTD: ' + motd);
	this.sendMessageToAll('' + socket.id + ' has logged in.');
}

GameServer.prototype.onDisconnect = function(socket) {
	console.log('Connection closed socket with ID ' + socket.id);
	this.playerCount--;
	this.sendMessageToAll('' + socket.id + ' has logged out.');
	delete this.players[socket.id];
}

GameServer.prototype.onChatMessage = function(socket, msg) {
	console.log('Message from ' + socket.id + ': ' + msg);
	msg = validator.escape(msg);
	this.sendMessageToAll(''  + socket.id + ': '  + msg);
}

GameServer.prototype.sendMessageToAll = function(msg) {
	io.emit('chat-message', msg);
}

GameServer.prototype.sendMessageToClient = function(socket, msg) {
	socket.emit('chat-message', msg);
}
// =============== GameServer end<commit_msg>Add heartbeat timeout property.<commit_after>// =============== Modules Start
var fs = require('fs');
var app = require('express')();
var http = require('http').Server(app);
var util = require('util');
var validator = require('validator');
// =============== Modules End

// =============== Main Start
var config = JSON.parse(fs.readFileSync('config.json', 'utf8'));
var port = config['port'];
var timeout = config['timeout'];
var maxPlayers = config['max_players'];
var motd = config['motd'];

var io = require('socket.io')(http, {
	'close timeout': timeout,
	'heartbeat timeout': timeout
});

var responses = {
	'/': 'index.html',
	'/?css': 'ch.css',
	'/?js': 'ch_client.js'
};

app.get('/', function(req, res){
	//res.send(util.inspect(req, false, null));
	var url = req['url'];

	if(url in responses)
		res.sendFile(__dirname + '/public/' + responses[url]);
	else res.send('No known response for ' + url);
});

http.listen(port, function() {
	console.log('HTTP Server running on port ' + port);
});

io.on('connection', function(socket) {
	gameserver.onConnect(socket);

	socket.on('disconnect', function() {
		gameserver.onDisconnect(socket);
	});

	// TODO IMPORTANT - Sanitize mesasge, no xss here.
	socket.on('chat-message', function(msg) {
		gameserver.onChatMessage(socket, msg);
	});
});
// =============== Main End

// =============== GameServer start
var GameServer = function() {
	this.games = [];
	this.players = {};
	this.playerCount = 0;
};
var gameserver = new GameServer();

GameServer.prototype.onConnect = function(socket) {
	console.log('Connection opened by socket with ID ' + socket.id);
	this.players[socket.id] = socket;
	this.playerCount++;
	this.sendMessageToClient(socket, 'Welcome to ClonedHumanity. There are ' + this.playerCount + '/' + maxPlayers + ' players online. | ' + 'MOTD: ' + motd);
	this.sendMessageToAll('' + socket.id + ' has logged in.');
}

GameServer.prototype.onDisconnect = function(socket) {
	console.log('Connection closed socket with ID ' + socket.id);
	this.playerCount--;
	this.sendMessageToAll('' + socket.id + ' has logged out.');
	delete this.players[socket.id];
}

GameServer.prototype.onChatMessage = function(socket, msg) {
	console.log('Message from ' + socket.id + ': ' + msg);
	msg = validator.escape(msg);
	this.sendMessageToAll(''  + socket.id + ': '  + msg);
}

GameServer.prototype.sendMessageToAll = function(msg) {
	io.emit('chat-message', msg);
}

GameServer.prototype.sendMessageToClient = function(socket, msg) {
	socket.emit('chat-message', msg);
}
// =============== GameServer end<|endoftext|>


<commit_before>import moment from 'moment';
import {
  SHEKEL_CURRENCY,
  NORMAL_TXN_TYPE,
  TRANSACTION_STATUS,
} from '../constants';
import { BaseScraperWithBrowser, LOGIN_RESULT } from './base-scraper-with-browser';
import { fetchPostWithinPage } from '../helpers/fetch';
import { waitForNavigation } from '../helpers/navigation';
import { pageEvalAll } from '../helpers/elements-interactions';

const BASE_URL = 'https://www.mizrahi-tefahot.co.il';
const LOGIN_URL = `${BASE_URL}/he/bank/Pages/Default.aspx`;
const AFTER_LOGIN_BASE_URL = /https:\/\/mto\.mizrahi-tefahot\.co\.il\/ngOnline\/index\.html#\/main\/uis/;
const OSH_PAGE = 'https://mto.mizrahi-tefahot.co.il/ngOnline/index.html#/main/uis/osh/p428/';
const TRANSACTIONS_REQUEST_URL = 'https://mto.mizrahi-tefahot.co.il/Online/api/SkyOSH/get428Index';
const PENDING_TRANSACTIONS_PAGE = 'https://mto.mizrahi-tefahot.co.il/Online/Osh/p420.aspx';
const DATE_FORMAT = 'DD/MM/YYYY';
const MAX_ROWS_PER_REQUEST = 10000000000;

function createLoginFields(credentials) {
  return [
    { selector: '#ctl00_PlaceHolderLogin_ctl00_tbUserName', value: credentials.username },
    { selector: '#ctl00_PlaceHolderLogin_ctl00_tbPassword', value: credentials.password },
  ];
}

function getPossibleLoginResults() {
  const urls = {};
  urls[LOGIN_RESULT.SUCCESS] = [AFTER_LOGIN_BASE_URL];
  urls[LOGIN_RESULT.INVALID_PASSWORD] = [`${BASE_URL}/login/loginMTO.aspx`];
  urls[LOGIN_RESULT.CHANGE_PASSWORD] = [
    `${AFTER_LOGIN_BASE_URL}/main/uis/ge/changePassword/`,
  ];
  return urls;
}

function CreateDataFromRequest(request, optionsStartDate) {
  const defaultStartMoment = moment().subtract(1, 'years');
  const startDate = optionsStartDate || defaultStartMoment.toDate();
  const startMoment = moment.max(defaultStartMoment, moment(startDate));

  const data = JSON.parse(request.postData());

  data.inToDate = moment().format(DATE_FORMAT);
  data.inFromDate = startMoment.format(DATE_FORMAT);
  data.table.maxRow = MAX_ROWS_PER_REQUEST;

  return data;
}

function createHeadersFromRequest(request) {
  return {
    mizrahixsrftoken: request.headers().mizrahixsrftoken,
    'Content-Type': request.headers()['content-type'],
  };
}

function convertTransactions(txns) {
  return txns.map((row) => {
    const txnDate = moment(row.MC02PeulaTaaEZ, moment.HTML5_FMT.DATETIME_LOCAL_SECONDS)
      .toISOString();

    return {
      type: NORMAL_TXN_TYPE,
      identifier: row.MC02AsmahtaMekoritEZ ? parseInt(row.MC02AsmahtaMekoritEZ, 10) : null,
      date: txnDate,
      processedDate: txnDate,
      originalAmount: row.MC02SchumEZ,
      originalCurrency: SHEKEL_CURRENCY,
      chargedAmount: row.MC02SchumEZ,
      description: row.MC02TnuaTeurEZ,
      status: TRANSACTION_STATUS.COMPLETED,
    };
  });
}

async function extractPendingTransactions(page) {
  const pendingTxn = await pageEvalAll(page, 'tr.rgRow', [], (trs) => {
    return trs.map((tr) => Array.from(tr.querySelectorAll('td'), (td) => td.textContent));
  });

  return pendingTxn.map((txn) => {
    const date = moment(txn[0], 'DD/MM/YY').toISOString();
    const amount = parseInt(txn[3], 10);
    return {
      type: NORMAL_TXN_TYPE,
      identifier: null,
      date,
      processedDate: date,
      originalAmount: amount,
      originalCurrency: SHEKEL_CURRENCY,
      chargedAmount: amount,
      description: txn[1],
      status: TRANSACTION_STATUS.PENDING,
    };
  });
}

class MizrahiScraper extends BaseScraperWithBrowser {
  getLoginOptions(credentials) {
    return {
      loginUrl: `${LOGIN_URL}`,
      fields: createLoginFields(credentials),
      submitButtonSelector: '#ctl00_PlaceHolderLogin_ctl00_Enter',
      postAction: async () => waitForNavigation(this.page, { waitUntil: 'networkidle0' }),
      possibleResults: getPossibleLoginResults(),
    };
  }

  async fetchData() {
    await this.navigateTo(OSH_PAGE, this.page);
    const request = await this.page.waitForRequest(TRANSACTIONS_REQUEST_URL);
    const data = CreateDataFromRequest(request, this.options.startDate);
    const headers = createHeadersFromRequest(request);

    const response = await fetchPostWithinPage(this.page,
      TRANSACTIONS_REQUEST_URL, data, headers);

    if (response.header.success === false) {
      return {
        success: false,
        errorType: 'generic',
        errorMessage:
          `Error fetching transaction. Response message: ${response.header.messages[0].text}`,
      };
    }

    const relevantRows = response.body.table.rows.filter((row) => row.RecTypeSpecified);
    const oshTxn = convertTransactions(relevantRows);

    await this.navigateTo(PENDING_TRANSACTIONS_PAGE, this.page);
    const pendingTxn = await extractPendingTransactions(this.page);

    const allTxn = oshTxn.concat(pendingTxn);

    return {
      success: true,
      accounts: [
        {
          accountNumber: response.body.fields.AccountNumber,
          txns: allTxn,
        },
      ],
    };
  }
}

export default MizrahiScraper;
<commit_msg>Use base app URL in all app URLs<commit_after>import moment from 'moment';
import {
  SHEKEL_CURRENCY,
  NORMAL_TXN_TYPE,
  TRANSACTION_STATUS,
} from '../constants';
import { BaseScraperWithBrowser, LOGIN_RESULT } from './base-scraper-with-browser';
import { fetchPostWithinPage } from '../helpers/fetch';
import { waitForNavigation } from '../helpers/navigation';
import { pageEvalAll } from '../helpers/elements-interactions';

const BASE_WEBSITE_URL = 'https://www.mizrahi-tefahot.co.il';
const LOGIN_URL = `${BASE_WEBSITE_URL}/he/bank/Pages/Default.aspx`;
const BASE_APP_URL = 'https://mto.mizrahi-tefahot.co.il/';
const AFTER_LOGIN_BASE_URL = /https:\/\/mto\.mizrahi-tefahot\.co\.il\/ngOnline\/index\.html#\/main\/uis/;
const OSH_PAGE = `${BASE_APP_URL}ngOnline/index.html#/main/uis/osh/p428/`;
const TRANSACTIONS_REQUEST_URL = `${BASE_APP_URL}Online/api/SkyOSH/get428Index`;
const PENDING_TRANSACTIONS_PAGE = `${BASE_APP_URL}Online/Osh/p420.aspx`;
const DATE_FORMAT = 'DD/MM/YYYY';
const MAX_ROWS_PER_REQUEST = 10000000000;

function createLoginFields(credentials) {
  return [
    { selector: '#ctl00_PlaceHolderLogin_ctl00_tbUserName', value: credentials.username },
    { selector: '#ctl00_PlaceHolderLogin_ctl00_tbPassword', value: credentials.password },
  ];
}

function getPossibleLoginResults() {
  const urls = {};
  urls[LOGIN_RESULT.SUCCESS] = [AFTER_LOGIN_BASE_URL];
  urls[LOGIN_RESULT.INVALID_PASSWORD] = [`${BASE_WEBSITE_URL}/login/loginMTO.aspx`];
  urls[LOGIN_RESULT.CHANGE_PASSWORD] = [
    `${AFTER_LOGIN_BASE_URL}/main/uis/ge/changePassword/`,
  ];
  return urls;
}

function CreateDataFromRequest(request, optionsStartDate) {
  const defaultStartMoment = moment().subtract(1, 'years');
  const startDate = optionsStartDate || defaultStartMoment.toDate();
  const startMoment = moment.max(defaultStartMoment, moment(startDate));

  const data = JSON.parse(request.postData());

  data.inToDate = moment().format(DATE_FORMAT);
  data.inFromDate = startMoment.format(DATE_FORMAT);
  data.table.maxRow = MAX_ROWS_PER_REQUEST;

  return data;
}

function createHeadersFromRequest(request) {
  return {
    mizrahixsrftoken: request.headers().mizrahixsrftoken,
    'Content-Type': request.headers()['content-type'],
  };
}

function convertTransactions(txns) {
  return txns.map((row) => {
    const txnDate = moment(row.MC02PeulaTaaEZ, moment.HTML5_FMT.DATETIME_LOCAL_SECONDS)
      .toISOString();

    return {
      type: NORMAL_TXN_TYPE,
      identifier: row.MC02AsmahtaMekoritEZ ? parseInt(row.MC02AsmahtaMekoritEZ, 10) : null,
      date: txnDate,
      processedDate: txnDate,
      originalAmount: row.MC02SchumEZ,
      originalCurrency: SHEKEL_CURRENCY,
      chargedAmount: row.MC02SchumEZ,
      description: row.MC02TnuaTeurEZ,
      status: TRANSACTION_STATUS.COMPLETED,
    };
  });
}

async function extractPendingTransactions(page) {
  const pendingTxn = await pageEvalAll(page, 'tr.rgRow', [], (trs) => {
    return trs.map((tr) => Array.from(tr.querySelectorAll('td'), (td) => td.textContent));
  });

  return pendingTxn.map((txn) => {
    const date = moment(txn[0], 'DD/MM/YY').toISOString();
    const amount = parseInt(txn[3], 10);
    return {
      type: NORMAL_TXN_TYPE,
      identifier: null,
      date,
      processedDate: date,
      originalAmount: amount,
      originalCurrency: SHEKEL_CURRENCY,
      chargedAmount: amount,
      description: txn[1],
      status: TRANSACTION_STATUS.PENDING,
    };
  });
}

class MizrahiScraper extends BaseScraperWithBrowser {
  getLoginOptions(credentials) {
    return {
      loginUrl: `${LOGIN_URL}`,
      fields: createLoginFields(credentials),
      submitButtonSelector: '#ctl00_PlaceHolderLogin_ctl00_Enter',
      postAction: async () => waitForNavigation(this.page, { waitUntil: 'networkidle0' }),
      possibleResults: getPossibleLoginResults(),
    };
  }

  async fetchData() {
    await this.navigateTo(OSH_PAGE, this.page);
    const request = await this.page.waitForRequest(TRANSACTIONS_REQUEST_URL);
    const data = CreateDataFromRequest(request, this.options.startDate);
    const headers = createHeadersFromRequest(request);

    const response = await fetchPostWithinPage(this.page,
      TRANSACTIONS_REQUEST_URL, data, headers);

    if (response.header.success === false) {
      return {
        success: false,
        errorType: 'generic',
        errorMessage:
          `Error fetching transaction. Response message: ${response.header.messages[0].text}`,
      };
    }

    const relevantRows = response.body.table.rows.filter((row) => row.RecTypeSpecified);
    const oshTxn = convertTransactions(relevantRows);

    await this.navigateTo(PENDING_TRANSACTIONS_PAGE, this.page);
    const pendingTxn = await extractPendingTransactions(this.page);

    const allTxn = oshTxn.concat(pendingTxn);

    return {
      success: true,
      accounts: [
        {
          accountNumber: response.body.fields.AccountNumber,
          txns: allTxn,
        },
      ],
    };
  }
}

export default MizrahiScraper;
<|endoftext|>


<commit_before>function filter_cache()
{
    time1 = new Timer();
    if(tmp != document.body.innerHTML)
    {
        console.log("new_stuff");
        tmp = document.body.innerHTML;
        filter.replace();
    }
    console.log("time total : "+time1.end());
}
var filter = new Filter();
var tmp = document.body.innerHTML;
setInterval('filter_cache()', 5000);
<commit_msg>Now instantiating multiple filters and running them on each iteration.<commit_after>function filter_cache()
{
    time1 = new Timer();
    if(tmp != document.body.innerHTML)
    {
        console.log("new_stuff");
        tmp = document.body.innerHTML;
        for (var i in filter) {
            filter[i].replace();
        }
    }
    console.log("time total : "+time1.end());
}
var filter = [new Filter_Facebook(), new Filter_Twitter()];
var tmp = document.body.innerHTML;
setInterval('filter_cache()', 5000);
<|endoftext|>


<commit_before>/*! FitNav.js 1.0
 * https://github.com/stephansmith/FitNav.js
 *
 * Copyright (c) 2014 @_stpehansmith
 * Available under the MIT license
 */

 (function (document, window, index) {

	var fitNav = function ( el, options ) {

		var computed = !!window.getComputedStyle;

		// getComputedStyle polyfill
		if ( !computed ) {
			window.getComputedStyle = function(el) {
				this.el = el;
				this.getPropertyValue = function(prop) {
					var re = /(\-([a-z]){1})/g;
					if (prop === 'float') {
						prop = 'styleFloat';
					}
					if (re.test(prop)) {
						prop = prop.replace(re, function () {
							return arguments[2].toUpperCase();
						});
					}
					return el.currentStyle[prop] ? el.currentStyle[prop] : null;
				};
				return this;
			};
		}

		var addEvent = function (el, evt, fn, bubble) {
			if ('addEventListener' in el) {
				// BBOS6 doesn't support handleEvent, catch and polyfill
				try {
					el.addEventListener(evt, fn, bubble);
				} catch (e) {
					if (typeof fn === 'object' && fn.handleEvent) {
						el.addEventListener(evt, function (e) {
							// Bind fn as this and set first arg as event object
							fn.handleEvent.call(fn, e);
						}, bubble);
					} else {
						throw e;
					}
				}
			} else if ('attachEvent' in el) {
				// check if the callback is an object and contains handleEvent
				if (typeof fn === 'object' && fn.handleEvent) {
					el.attachEvent('on' + evt, function () {
						// Bind fn as this
						fn.handleEvent.call(fn);
					});
				} else {
					el.attachEvent('on' + evt, fn);
				}
			}
		},

		removeEvent = function (el, evt, fn, bubble) {
			if ('removeEventListener' in el) {
				try {
					el.removeEventListener(evt, fn, bubble);
				} catch (e) {
					if (typeof fn === 'object' && fn.handleEvent) {
						el.removeEventListener(evt, function (e) {
							fn.handleEvent.call(fn, e);
						}, bubble);
					} else {
						throw e;
					}
				}
			} else if ('detachEvent' in el) {
				if (typeof fn === 'object' && fn.handleEvent) {
					el.detachEvent('on' + evt, function () {
						fn.handleEvent.call(fn);
					});
				} else {
					el.detachEvent('on' + evt, fn);
				}
			}
		},

		addClass = function (el, cls) {
			if (el.className.indexOf(cls) !== 0) {
				el.className += ' ' + cls;
				el.className = el.className.replace(/(^\s*)|(\s*$)/g,'');
			}
		},

		removeClass = function (el, cls) {
			var reg = new RegExp('(\\s|^)' + cls + '(\\s|$)');
			el.className = el.className.replace(reg, ' ').replace(/(^\s*)|(\s*$)/g,'');
		},

		// forEach method that passes back the stuff we need
		forEach = function (array, callback, scope) {
			for ( var i = 0; i < array.length; i++ ) {
				callback.call( scope, i, array[i] );
			}
		};

		var nav,
			navList,
			navToggle,
			isCollapsed;

		var FitNav = function ( el, options ) {

			var i;

			// Default options
			this.options = {	 // Function: Close callback
				navToggleSelector: '.fitnav__toggle',
				navListSelector: '.fitnav__list',
				navCollapsedClass: 'collapsed'
			};

			// User defined options
			for (i in options) {
				 this.options[i] = options[i];
			}

			// Wrapper
			this.wrapperEl = el.replace( '#', '' );

			// Try selecting ID first
			if (document.getElementById( this.wrapperEl )) {
				this.wrapper = document.getElementById( this.wrapperEl );

			// If element with an ID doesn't exist, use querySelector
			} else if ( document.querySelector( this.wrapperEl ) ) {
				this.wrapper = document.querySelector( this.wrapperEl );

			// If element doesn't exists, stop here.
			} else {
				throw new Error( 'The element you are trying to select does not exist' );
			}

			nav = this.wrapper;
			navList = nav.querySelector( this.options.navListSelector );
			navToggle = nav.querySelector( this.options.navToggleSelector );

			// Init
			this._init(this);
		};

		FitNav.prototype = {

			// Public methods
			destroy: function () {
				removeEvent( window, 'resize', this, false );

				removeEvent( navToggle, 'click', this, false );

				if ( 'querySelectorAll' in document ) {
					var links = navList.querySelectorAll('a'),
						self = this;

					forEach(links, function (i, el) {
						removeEvent(links[i], 'click', function (e) {
							e.preventDefault();
							e.stopPropagation();
							if (isCollapsed) {
								self.toggle();
								setTimeout( function () {
									window.location = links[i].href;
								}, 400);
							}
						}, false);
					});
				}
			},

			collapse: function () {
				addClass( nav, this.options.navCollapsedClass );
				isCollapsed = true;
			},

			expand: function () {
				removeClass( nav, this.options.navCollapsedClass );
				isCollapsed = false;
				//navList.style.height = 'auto';
			},

			open: function () {
				navList.style.height = this._calcHeight() + 'px';

			},

			close: function () {
				navList.removeAttribute( 'style' );
			},

			toggle: function () {
				if ( navList.offsetHeight > 0 ) {
					this.close();
				}
				else {
					this.open();
				}
			},

			resize: function (e) {
				this.expand();
				this._isWrapped() ?
					this.collapse() :
					this._resetHeight();
			},

			handleEvent: function (e) {
				var evt = e || window.event;

				switch (evt.type) {
					case 'click':
						this._preventDefault(evt);
						if ( evt.target == navToggle ) {
							this.toggle();
						}
						break;
					case 'keyup':
						this._onKeyUp(evt);
						break;
					case 'resize':
						this.resize(evt);
						break;
				}
			},

			// Private methods
			_init: function () {

				var self = this;

				self._isWrapped() ?
					self.collapse() :
					self._resetHeight();

				this._closeOnNavClick();

				addEvent( window, 'resize', this, false );

				addEvent( navToggle, 'click', this, false );

			},

			_addToggle: function () {

			},

			_calcWidth: function () {

				var children = nav.querySelectorAll( 'li' ),
					width = 0;

				for ( var i = 0; i < children.length; i++ ) {
					width += children[i].offsetWidth;

					if ( i > 0 ) {
						width += children[i].getBoundingClientRect().left - ( children[i-1].getBoundingClientRect().left + children[i-1].offsetWidth );
					}

				}

				return width;
			},

			_calcParentWidth: function () {
				var clientWidth = nav.parentNode.clientWidth,
					computedStyle = window.getComputedStyle( nav.parentNode ),
					paddingLeft = parseInt( computedStyle.getPropertyValue( 'padding-left' ), 10 ),
					paddingRight = parseInt( computedStyle.getPropertyValue( 'padding-right' ), 10 ),
					width = clientWidth - paddingLeft - paddingRight;

				return width;

			},

			_resetHeight: function () {

				navList.removeAttribute( 'style' );
			},

			_calcHeight: function () {

				var children = nav.querySelectorAll( 'li' ),
					height = 0;

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

				return height;
			},

			_isWrapped: function () {

				var isWrapped = this._calcWidth() >= this._calcParentWidth() ? true : false;

				return isWrapped;

			},

			_preventDefault: function(e) {
				if ( e.preventDefault ) {
					e.preventDefault();
					e.stopPropagation();
				} else {
					e.returnValue = false;
				}
			},

			_closeOnNavClick: function () {
				if ( 'querySelectorAll' in document ) {
					var links = navList.querySelectorAll('a'),
						self = this;

					forEach(links, function (i, el) {
						addEvent(links[i], 'click', function (e) {
							e.preventDefault();
							e.stopPropagation();
							if (isCollapsed) {
								self.toggle();
								setTimeout( function () {
									window.location = links[i].href;
								}, 400);
							}
						}, false);
					});
				}
			}

		};

		return new FitNav(el, options);

	};

	window.fitNav = fitNav;

}(document, window, 0));<commit_msg>Fixed anchor click event on non-collapsed menu<commit_after>/*! FitNav.js 1.0
 * https://github.com/stephansmith/FitNav.js
 *
 * Copyright (c) 2014 @_stpehansmith
 * Available under the MIT license
 */

 (function (document, window, index) {

	var fitNav = function ( el, options ) {

		var computed = !!window.getComputedStyle;

		// getComputedStyle polyfill
		if ( !computed ) {
			window.getComputedStyle = function(el) {
				this.el = el;
				this.getPropertyValue = function(prop) {
					var re = /(\-([a-z]){1})/g;
					if (prop === 'float') {
						prop = 'styleFloat';
					}
					if (re.test(prop)) {
						prop = prop.replace(re, function () {
							return arguments[2].toUpperCase();
						});
					}
					return el.currentStyle[prop] ? el.currentStyle[prop] : null;
				};
				return this;
			};
		}

		var addEvent = function (el, evt, fn, bubble) {
			if ('addEventListener' in el) {
				// BBOS6 doesn't support handleEvent, catch and polyfill
				try {
					el.addEventListener(evt, fn, bubble);
				} catch (e) {
					if (typeof fn === 'object' && fn.handleEvent) {
						el.addEventListener(evt, function (e) {
							// Bind fn as this and set first arg as event object
							fn.handleEvent.call(fn, e);
						}, bubble);
					} else {
						throw e;
					}
				}
			} else if ('attachEvent' in el) {
				// check if the callback is an object and contains handleEvent
				if (typeof fn === 'object' && fn.handleEvent) {
					el.attachEvent('on' + evt, function () {
						// Bind fn as this
						fn.handleEvent.call(fn);
					});
				} else {
					el.attachEvent('on' + evt, fn);
				}
			}
		},

		removeEvent = function (el, evt, fn, bubble) {
			if ('removeEventListener' in el) {
				try {
					el.removeEventListener(evt, fn, bubble);
				} catch (e) {
					if (typeof fn === 'object' && fn.handleEvent) {
						el.removeEventListener(evt, function (e) {
							fn.handleEvent.call(fn, e);
						}, bubble);
					} else {
						throw e;
					}
				}
			} else if ('detachEvent' in el) {
				if (typeof fn === 'object' && fn.handleEvent) {
					el.detachEvent('on' + evt, function () {
						fn.handleEvent.call(fn);
					});
				} else {
					el.detachEvent('on' + evt, fn);
				}
			}
		},

		addClass = function (el, cls) {
			if (el.className.indexOf(cls) !== 0) {
				el.className += ' ' + cls;
				el.className = el.className.replace(/(^\s*)|(\s*$)/g,'');
			}
		},

		removeClass = function (el, cls) {
			var reg = new RegExp('(\\s|^)' + cls + '(\\s|$)');
			el.className = el.className.replace(reg, ' ').replace(/(^\s*)|(\s*$)/g,'');
		},

		// forEach method that passes back the stuff we need
		forEach = function (array, callback, scope) {
			for ( var i = 0; i < array.length; i++ ) {
				callback.call( scope, i, array[i] );
			}
		};

		var nav,
			navList,
			navToggle,
			isCollapsed;

		var FitNav = function ( el, options ) {

			var i;

			// Default options
			this.options = {	 // Function: Close callback
				navToggleSelector: '.fitnav__toggle',
				navListSelector: '.fitnav__list',
				navCollapsedClass: 'collapsed'
			};

			// User defined options
			for (i in options) {
				 this.options[i] = options[i];
			}

			// Wrapper
			this.wrapperEl = el.replace( '#', '' );

			// Try selecting ID first
			if (document.getElementById( this.wrapperEl )) {
				this.wrapper = document.getElementById( this.wrapperEl );

			// If element with an ID doesn't exist, use querySelector
			} else if ( document.querySelector( this.wrapperEl ) ) {
				this.wrapper = document.querySelector( this.wrapperEl );

			// If element doesn't exists, stop here.
			} else {
				throw new Error( 'The element you are trying to select does not exist' );
			}

			nav = this.wrapper;
			navList = nav.querySelector( this.options.navListSelector );
			navToggle = nav.querySelector( this.options.navToggleSelector );

			// Init
			this._init(this);
		};

		FitNav.prototype = {

			// Public methods
			destroy: function () {
				removeEvent( window, 'resize', this, false );

				removeEvent( navToggle, 'click', this, false );

				if ( 'querySelectorAll' in document ) {
					var links = navList.querySelectorAll('a'),
						self = this;

					forEach(links, function (i, el) {
						removeEvent(links[i], 'click', function (e) {
							e.preventDefault();
							e.stopPropagation();
							if (isCollapsed) {
								self.toggle();
								setTimeout( function () {
									window.location = links[i].href;
								}, 400);
							}
						}, false);
					});
				}
			},

			collapse: function () {
				addClass( nav, this.options.navCollapsedClass );
				isCollapsed = true;
			},

			expand: function () {
				removeClass( nav, this.options.navCollapsedClass );
				isCollapsed = false;
				//navList.style.height = 'auto';
			},

			open: function () {
				navList.style.height = this._calcHeight() + 'px';

			},

			close: function () {
				navList.removeAttribute( 'style' );
			},

			toggle: function () {
				if ( navList.offsetHeight > 0 ) {
					this.close();
				}
				else {
					this.open();
				}
			},

			resize: function (e) {
				this.expand();
				this._isWrapped() ?
					this.collapse() :
					this._resetHeight();
			},

			handleEvent: function (e) {
				var evt = e || window.event;

				switch (evt.type) {
					case 'click':
						this._preventDefault(evt);
						if ( evt.target == navToggle ) {
							this.toggle();
						}
						break;
					case 'keyup':
						this._onKeyUp(evt);
						break;
					case 'resize':
						this.resize(evt);
						break;
				}
			},

			// Private methods
			_init: function () {

				var self = this;

				self._isWrapped() ?
					self.collapse() :
					self._resetHeight();

				this._closeOnNavClick();

				addEvent( window, 'resize', this, false );

				addEvent( navToggle, 'click', this, false );

			},

			_addToggle: function () {

			},

			_calcWidth: function () {

				var children = nav.querySelectorAll( 'li' ),
					width = 0;

				for ( var i = 0; i < children.length; i++ ) {
					width += children[i].offsetWidth;

					if ( i > 0 ) {
						width += children[i].getBoundingClientRect().left - ( children[i-1].getBoundingClientRect().left + children[i-1].offsetWidth );
					}

				}

				return width;
			},

			_calcParentWidth: function () {
				var clientWidth = nav.parentNode.clientWidth,
					computedStyle = window.getComputedStyle( nav.parentNode ),
					paddingLeft = parseInt( computedStyle.getPropertyValue( 'padding-left' ), 10 ),
					paddingRight = parseInt( computedStyle.getPropertyValue( 'padding-right' ), 10 ),
					width = clientWidth - paddingLeft - paddingRight;

				return width;

			},

			_resetHeight: function () {

				navList.removeAttribute( 'style' );
			},

			_calcHeight: function () {

				var children = nav.querySelectorAll( 'li' ),
					height = 0;

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

				return height;
			},

			_isWrapped: function () {

				var isWrapped = this._calcWidth() >= this._calcParentWidth() ? true : false;

				return isWrapped;

			},

			_preventDefault: function(e) {
				if ( e.preventDefault ) {
					e.preventDefault();
					e.stopPropagation();
				} else {
					e.returnValue = false;
				}
			},

			_closeOnNavClick: function () {
				if ( 'querySelectorAll' in document ) {
					var links = navList.querySelectorAll('a'),
						self = this;

					forEach(links, function (i, el) {
						addEvent(links[i], 'click', function (e) {
							e.preventDefault();
							e.stopPropagation();
							if (isCollapsed) {
								self.toggle();
								setTimeout( function () {
									window.location = links[i].href;
								}, 400);
								return;
							}
							window.location = links[i].href;
						}, false);
					});
				}
			}

		};

		return new FitNav(el, options);

	};

	window.fitNav = fitNav;

}(document, window, 0));
<|endoftext|>


<commit_before>// @flow

import fs from 'fs';
import path from 'path';
import globby from 'globby';
import chokidar from 'chokidar';
import debounce from 'lodash.debounce';
import { clone, merge, addLast } from 'timm';
import { mainStory, chalk } from 'storyboard';
import type { StoryT } from 'storyboard';
import slash from 'slash';
import { HTML_PREVIEW_SEPARATOR } from '../serializer';
import type {
  FilePathT,
  FolderPathT,
  FolderT,
  SnapshotSuiteT,
} from '../common/types';

type ConfigT = {|
  snapshotPatterns: Array<string>,
  cssPatterns: Array<string>,
  watch: boolean,
  socketioServer: Object,
|};

type SnapshotSuiteDictT = { [filePath: FilePathT]: SnapshotSuiteT };
type FolderDictT = { [folderPath: FolderPathT]: FolderT };

const FOLDER_PATH_ATTR = '__folderPath';

let _config: ConfigT;
let _folderDict: FolderDictT = {};
let _watchers = null;

const configure = (newConfig: $Shape<ConfigT>) => {
  if (!_config) {
    _config = clone(newConfig);
    return;
  }
  _config = (merge(_config, newConfig): any);
};

const start = ({ story = mainStory }: { story: StoryT } = {}) =>
  Promise.resolve()
  .then(() => loadCommonCss(story))
  .then(() => loadAllSnapshots(story))
  .then(() => { if (_config.watch) watchStart(story); })
  .then(() => broadcastSignal());

// ---------------------------------
// CSS
// ---------------------------------
let _snapshotSuiteDict: SnapshotSuiteDictT = {};

const loadAllSnapshots = (story: StoryT = mainStory) => {
  const childStory = story.child({ src: 'extractor', title: 'Refresh snapshots' });
  return Promise.resolve()
  .then(() => globby(_config.snapshotPatterns))
  .then((filePaths) => {
    childStory.info('extractor', 'Reading snapshot files...');
    const commonCss = getCommonCss();
    _snapshotSuiteDict = {};
    filePaths.forEach((filePath) => {
      loadSuite(filePath, commonCss, childStory);
    });
    buildFolderDict(childStory);
    childStory.debug('extractor', 'Snapshot tree:', { attach: _folderDict });
    childStory.close();
  })
  .catch((err) => {
    childStory.close();
    throw err;
  });
};

const updateSnapshotCss = (story: StoryT = mainStory) => {
  const commonCss = getCommonCss();
  Object.keys(_snapshotSuiteDict).forEach((filePath) => {
    const suite = _snapshotSuiteDict[filePath];
    const suiteCss = getSuiteCss(filePath, story);
    Object.keys(suite).forEach((id) => {
      if (id === FOLDER_PATH_ATTR) return;
      const snapshot = suite[id];
      const css = suiteCss != null ? addLast(commonCss, suiteCss) : commonCss;
      snapshot.css = css;
    });
  });
};

const loadSuite = (filePath: string, commonCss: Array<string>, story: StoryT) => {
  story.info('extractor', `Processing ${chalk.cyan.bold(filePath)}...`);
  const absPath = path.resolve(process.cwd(), filePath);
  const rawSnapshots = loadSnapshot(absPath);
  const suiteCss = getSuiteCss(filePath, story);
  const suite: SnapshotSuiteT = {};
  Object.keys(rawSnapshots).forEach((id) => {
    const snapshot = rawSnapshots[id];
    const [snap, html] = snapshot.split(HTML_PREVIEW_SEPARATOR);
    const css = suiteCss != null ? addLast(commonCss, suiteCss) : commonCss;
    suite[id] = { id, snap, html, css };
  });
  story.debug('extractor', `Found ${Object.keys(rawSnapshots).length} snapshots`);
  const finalFilePath = `-/${filePath}`;
  suite[FOLDER_PATH_ATTR] = path.dirname(finalFilePath);
  _snapshotSuiteDict[finalFilePath] = suite;
};

const loadSnapshot = (absPath: string): Object => {
  /* eslint-disable global-require, import/no-dynamic-require */
  delete require.cache[require.resolve(absPath)];
  return require(absPath);
  /* eslint-enable global-require */
};

// ---------------------------------
// CSS
// ---------------------------------
let _commonCss: Array<string> = [];

const getCommonCss = () => _commonCss;

const loadCommonCss = (story: StoryT = mainStory): Promise<void> => {
  story.info('extractor', 'Extracting common CSS...');
  return Promise.resolve()
  .then(() => globby(_config.cssPatterns))
  .then((cssPaths) => {
    _commonCss = cssPaths.map((cssPath) => {
      story.info('extractor', `Processing ${chalk.cyan.bold(cssPath)}...`);
      return fs.readFileSync(cssPath, 'utf8');
    });
  });
};

const getSuiteCss = (filePath: string, story: StoryT): ?string => {
  let suiteCss;
  try {
    const cssPath = getCssPathForSuite(filePath);
    story.debug('extractor', `Trying to read ${chalk.cyan.bold(cssPath)}...`);
    suiteCss = fs.readFileSync(cssPath, 'utf8');
    story.info('extractor', `Found custom CSS in ${chalk.cyan.bold(cssPath)}`);
  } catch (err) { /* no file exists */ }
  return suiteCss;
};

const getCssPathForSuite = (filePath: FilePathT): string => {
  const { dir, name } = path.parse(filePath);
  return path.join(dir, `${name}.css`);
};

// ---------------------------------
// Folder dictionary
// ---------------------------------
const buildFolderDict = (story: StoryT) => {
  story.info('extractor', 'Building folder tree...');
  const nextDict: FolderDictT = {};

  // Sort file paths to simplify tree generation
  const filePaths: Array<FilePathT> = Object.keys(_snapshotSuiteDict).sort();

  // Create root node
  let curFolderPath: FolderPathT = '-';
  let curFolder: FolderT = {
    folderPath: '-',
    filePaths: [],
    parentFolderPath: null,
    childrenFolderPaths: [],
  };
  nextDict['-'] = curFolder;

  story.debug('extractor', 'Snapshot tree:', { attach: nextDict });

  // Process all file paths
  filePaths.forEach((filePath) => {
    story.debug('extractor', `File: ${filePath}`);
    const folderPath: FolderPathT = path.dirname(filePath);
    if (folderPath === curFolderPath) {
      curFolder.filePaths.push(filePath);
    } else {
      let parentFolderPath = folderPath;
      let fFound = false;
      while (!fFound) {
        if (parentFolderPath === '-') break;
        parentFolderPath = path.dirname(parentFolderPath);
        if (nextDict[parentFolderPath]) {
          fFound = true;
          break;
        }
      }
      if (!fFound) throw new Error('Error building path tree');
      nextDict[parentFolderPath].childrenFolderPaths.push(folderPath);
      curFolder = nextDict[folderPath] = {
        folderPath,
        filePaths: [filePath],
        parentFolderPath,
        childrenFolderPaths: [],
      };
      curFolderPath = folderPath;
    }
  });
  _folderDict = nextDict;
};

// ---------------------------------
// Watching and real-time
// ---------------------------------
const watchStart = (story: StoryT) => {
  if (_watchers) return;
  _watchers = {};
  // const glob = _config.snapshotPatterns.concat(_config.cssPatterns);
  const options = {
    ignored: /[\/\\]\./,
    ignoreInitial: true,
  };
  _watchers.css = chokidar.watch(_config.cssPatterns, options);
  _watchers.snap = chokidar.watch(_config.snapshotPatterns, options);
  ['change', 'add', 'unlink'].forEach((type) => {
    // $FlowFixMe
    _watchers.css.on(type, (filePath) => { cssWatchEvent(type, filePath); });
    // $FlowFixMe
    _watchers.snap.on(type, (filePath) => { snapWatchEvent(type, filePath); });
  });
  story.info('extractor', 'Started watching over snapshot and CSS files');
};

// const watchStop = (story: StoryT) => {
//   if (_watchers == null) return;
//   _watchers.close();
//   _watchers = null;
//   story.info('extractor', 'Stopped file watcher');
// };

const cssWatchEvent = (type: string, filePath0: string) => {
  const filePath = slash(filePath0);
  mainStory.debug('extractor', 'CSS watch fired: ' +
    `${chalk.bold(type.toUpperCase())} ${chalk.cyan.bold(filePath)}`);
  debouncedCssRefresh();
};

const debouncedCssRefresh = debounce(() =>
  Promise.resolve()
  .then(() => loadCommonCss())
  .then(() => updateSnapshotCss())
  .then(() => broadcastSignal())
, 300);

const snapWatchEvent = (type: string, filePath0: string) => {
  const filePath = slash(filePath0);
  mainStory.debug('extractor', 'Snapshot watch fired: ' +
    `${chalk.bold(type.toUpperCase())} ${chalk.cyan.bold(filePath)}`);
  Promise.resolve()
  .then(() => {
    const commonCss = getCommonCss();
    switch (type) {
      case 'change':
        return loadSuite(filePath, commonCss, mainStory);
      case 'add':
        return Promise.resolve()
        .then(() => loadSuite(filePath, commonCss, mainStory))
        .then(() => buildFolderDict(mainStory));
      case 'unlink':
        return Promise.resolve()
        .then(() => { delete _snapshotSuiteDict[`-/${filePath}`]; })
        .then(() => buildFolderDict(mainStory));
      default:
        break;
    }
    return null;
  })
  .then(() => broadcastSignal());
};

const broadcastSignal = () => {
  if (!_config.socketioServer) return;
  _config.socketioServer.emit('REFRESH');
};

// ---------------------------------
// Others
// ---------------------------------
const getFolder = (folderPath: FolderPathT): ?FolderT => _folderDict[folderPath];
const getSnapshotSuite = (filePath: FilePathT): ?SnapshotSuiteT => _snapshotSuiteDict[filePath];

// =============================================
// Public API
// =============================================
export {
  configure,
  start,
  getFolder,
  getSnapshotSuite,
};
<commit_msg>Snapshot extractor: normalize file and folder paths before adding to the data structures<commit_after>// @flow

import fs from 'fs';
import path from 'path';
import globby from 'globby';
import chokidar from 'chokidar';
import debounce from 'lodash.debounce';
import { clone, merge, addLast } from 'timm';
import { mainStory, chalk } from 'storyboard';
import type { StoryT } from 'storyboard';
import slash from 'slash';
import { HTML_PREVIEW_SEPARATOR } from '../serializer';
import type {
  FilePathT,
  FolderPathT,
  FolderT,
  SnapshotSuiteT,
} from '../common/types';

type ConfigT = {|
  snapshotPatterns: Array<string>,
  cssPatterns: Array<string>,
  watch: boolean,
  socketioServer: Object,
|};

type SnapshotSuiteDictT = { [filePath: FilePathT]: SnapshotSuiteT };
type FolderDictT = { [folderPath: FolderPathT]: FolderT };

const FOLDER_PATH_ATTR = '__folderPath';

let _config: ConfigT;
let _folderDict: FolderDictT = {};
let _watchers = null;

const configure = (newConfig: $Shape<ConfigT>) => {
  if (!_config) {
    _config = clone(newConfig);
    return;
  }
  _config = (merge(_config, newConfig): any);
};

const start = ({ story = mainStory }: { story: StoryT } = {}) =>
  Promise.resolve()
  .then(() => loadCommonCss(story))
  .then(() => loadAllSnapshots(story))
  .then(() => { if (_config.watch) watchStart(story); })
  .then(() => broadcastSignal());

// ---------------------------------
// Snapshots
// ---------------------------------
let _snapshotSuiteDict: SnapshotSuiteDictT = {};

const loadAllSnapshots = (story: StoryT = mainStory) => {
  const childStory = story.child({ src: 'extractor', title: 'Refresh snapshots' });
  return Promise.resolve()
  .then(() => globby(_config.snapshotPatterns))
  .then((filePaths) => {
    childStory.info('extractor', 'Reading snapshot files...');
    const commonCss = getCommonCss();
    _snapshotSuiteDict = {};
    filePaths.forEach((filePath) => {
      loadSuite(filePath, commonCss, childStory);
    });
    buildFolderDict(childStory);
    childStory.debug('extractor', 'Snapshot tree:', { attach: _folderDict });
    childStory.close();
  })
  .catch((err) => {
    childStory.close();
    throw err;
  });
};

const updateSnapshotCss = (story: StoryT = mainStory) => {
  const commonCss = getCommonCss();
  Object.keys(_snapshotSuiteDict).forEach((filePath) => {
    const suite = _snapshotSuiteDict[filePath];
    const suiteCss = getSuiteCss(filePath, story);
    Object.keys(suite).forEach((id) => {
      if (id === FOLDER_PATH_ATTR) return;
      const snapshot = suite[id];
      const css = suiteCss != null ? addLast(commonCss, suiteCss) : commonCss;
      snapshot.css = css;
    });
  });
};

const loadSuite = (filePath: string, commonCss: Array<string>, story: StoryT) => {
  story.info('extractor', `Processing ${chalk.cyan.bold(filePath)}...`);
  const absPath = path.resolve(process.cwd(), filePath);
  const rawSnapshots = loadSnapshot(absPath);
  const suiteCss = getSuiteCss(filePath, story);
  const suite: SnapshotSuiteT = {};
  Object.keys(rawSnapshots).forEach((id) => {
    const snapshot = rawSnapshots[id];
    const [snap, html] = snapshot.split(HTML_PREVIEW_SEPARATOR);
    const css = suiteCss != null ? addLast(commonCss, suiteCss) : commonCss;
    suite[id] = { id, snap, html, css };
  });
  story.debug('extractor', `Found ${Object.keys(rawSnapshots).length} snapshots`);
  const finalFilePath = `-/${filePath.normalize()}`;
  suite[FOLDER_PATH_ATTR] = path.dirname(finalFilePath);
  _snapshotSuiteDict[finalFilePath] = suite;
};

const loadSnapshot = (absPath: string): Object => {
  /* eslint-disable global-require, import/no-dynamic-require */
  delete require.cache[require.resolve(absPath)];
  return require(absPath);
  /* eslint-enable global-require */
};

// ---------------------------------
// CSS
// ---------------------------------
let _commonCss: Array<string> = [];

const getCommonCss = () => _commonCss;

const loadCommonCss = (story: StoryT = mainStory): Promise<void> => {
  story.info('extractor', 'Extracting common CSS...');
  return Promise.resolve()
  .then(() => globby(_config.cssPatterns))
  .then((cssPaths) => {
    _commonCss = cssPaths.map((cssPath) => {
      story.info('extractor', `Processing ${chalk.cyan.bold(cssPath)}...`);
      return fs.readFileSync(cssPath, 'utf8');
    });
  });
};

const getSuiteCss = (filePath: string, story: StoryT): ?string => {
  let suiteCss;
  try {
    const cssPath = getCssPathForSuite(filePath);
    story.debug('extractor', `Trying to read ${chalk.cyan.bold(cssPath)}...`);
    suiteCss = fs.readFileSync(cssPath, 'utf8');
    story.info('extractor', `Found custom CSS in ${chalk.cyan.bold(cssPath)}`);
  } catch (err) { /* no file exists */ }
  return suiteCss;
};

const getCssPathForSuite = (filePath: FilePathT): string => {
  const { dir, name } = path.parse(filePath);
  return path.join(dir, `${name}.css`);
};

// ---------------------------------
// Folder dictionary
// ---------------------------------
const buildFolderDict = (story: StoryT) => {
  story.info('extractor', 'Building folder tree...');
  const nextDict: FolderDictT = {};

  // Sort file paths to simplify tree generation
  const filePaths: Array<FilePathT> = Object.keys(_snapshotSuiteDict).sort();

  // Create root node
  let curFolderPath: FolderPathT = '-';
  let curFolder: FolderT = {
    folderPath: '-',
    filePaths: [],
    parentFolderPath: null,
    childrenFolderPaths: [],
  };
  nextDict['-'] = curFolder;

  story.debug('extractor', 'Snapshot tree:', { attach: nextDict });

  // Process all file paths
  filePaths.forEach((filePath) => {
    story.debug('extractor', `File: ${filePath}`);
    const folderPath: FolderPathT = path.dirname(filePath);
    if (folderPath === curFolderPath) {
      curFolder.filePaths.push(filePath);
    } else {
      let parentFolderPath = folderPath;
      let fFound = false;
      while (!fFound) {
        if (parentFolderPath === '-') break;
        parentFolderPath = path.dirname(parentFolderPath);
        if (nextDict[parentFolderPath]) {
          fFound = true;
          break;
        }
      }
      if (!fFound) throw new Error('Error building path tree');
      nextDict[parentFolderPath].childrenFolderPaths.push(folderPath);
      curFolder = nextDict[folderPath] = {
        folderPath,
        filePaths: [filePath],
        parentFolderPath,
        childrenFolderPaths: [],
      };
      curFolderPath = folderPath;
    }
  });
  _folderDict = nextDict;
};

// ---------------------------------
// Watching and real-time
// ---------------------------------
const watchStart = (story: StoryT) => {
  if (_watchers) return;
  _watchers = {};
  // const glob = _config.snapshotPatterns.concat(_config.cssPatterns);
  const options = {
    ignored: /[\/\\]\./,
    ignoreInitial: true,
  };
  _watchers.css = chokidar.watch(_config.cssPatterns, options);
  _watchers.snap = chokidar.watch(_config.snapshotPatterns, options);
  ['change', 'add', 'unlink'].forEach((type) => {
    // $FlowFixMe
    _watchers.css.on(type, (filePath) => { cssWatchEvent(type, filePath); });
    // $FlowFixMe
    _watchers.snap.on(type, (filePath) => { snapWatchEvent(type, filePath); });
  });
  story.info('extractor', 'Started watching over snapshot and CSS files');
};

// const watchStop = (story: StoryT) => {
//   if (_watchers == null) return;
//   _watchers.close();
//   _watchers = null;
//   story.info('extractor', 'Stopped file watcher');
// };

const cssWatchEvent = (type: string, filePath0: string) => {
  const filePath = slash(filePath0);
  mainStory.debug('extractor', 'CSS watch fired: ' +
    `${chalk.bold(type.toUpperCase())} ${chalk.cyan.bold(filePath)}`);
  debouncedCssRefresh();
};

const debouncedCssRefresh = debounce(() =>
  Promise.resolve()
  .then(() => loadCommonCss())
  .then(() => updateSnapshotCss())
  .then(() => broadcastSignal())
, 300);

const snapWatchEvent = (type: string, filePath0: string) => {
  const filePath = slash(filePath0);
  mainStory.debug('extractor', 'Snapshot watch fired: ' +
    `${chalk.bold(type.toUpperCase())} ${chalk.cyan.bold(filePath)}`);
  Promise.resolve()
  .then(() => {
    const commonCss = getCommonCss();
    switch (type) {
      case 'change':
        return loadSuite(filePath, commonCss, mainStory);
      case 'add':
        return Promise.resolve()
        .then(() => loadSuite(filePath, commonCss, mainStory))
        .then(() => buildFolderDict(mainStory));
      case 'unlink':
        return Promise.resolve()
        .then(() => { delete _snapshotSuiteDict[`-/${filePath.normalize()}`]; })
        .then(() => buildFolderDict(mainStory));
      default:
        break;
    }
    return null;
  })
  .then(() => broadcastSignal());
};

const broadcastSignal = () => {
  if (!_config.socketioServer) return;
  _config.socketioServer.emit('REFRESH');
};

// ---------------------------------
// Others
// ---------------------------------
const getFolder = (folderPath: FolderPathT): ?FolderT =>
  _folderDict[folderPath.normalize()];
const getSnapshotSuite = (filePath: FilePathT): ?SnapshotSuiteT =>
  _snapshotSuiteDict[filePath.normalize()];

// =============================================
// Public API
// =============================================
export {
  configure,
  start,
  getFolder,
  getSnapshotSuite,
};
<|endoftext|>


<commit_before>import { inject } from 'aurelia-framework';
import { HttpClient } from 'aurelia-http-client';
import { NiceAPI } from './nice-api';
import { randomInteger } from 'resources/utility';
import { State } from './state';
import { EventAggregator } from 'aurelia-event-aggregator';
import { ApiStatus } from 'resources/messages';

let nouser = {
  meta: {
    "min_id": "2",
    "code": 200,
    "max_id": "1",
    "more": true
  },
  data: [
    {
      "created_at": "1790-05-10T15:16:44Z",
      "num_stars": 0,
      "num_replies": 0,
      "num_reposts": 0,
      "entities": {
        "mentions": [],
        "hashtags": [],
        "links": []
      },
      "user": {
        "username": "theabominablesnowman",
        "avatar_image": {
          "url": "/styles/abominablesnowman.jpg",
          "width": 200,
          "is_default": false,
          "height": 200
        },
        "description": {
          "html": "Oh Crap! you found me... Snowcone?

#snow ",
          "entities": {
            "mentions": [],
            "hashtags": [
              {
                "name": "snow",
                "len": 15,
                "pos": 65
              }
            ],
            "links": []
          }
        },
        "verified_link": "http://en.wikipedia.org/wiki/Yeti",
        "canonical_url": "https://alpha.app.net/",
        "verified_domain": "en.wikipedia.org/wiki/Yeti",
        "cover_image": {
          "url": "/styles/adventure-journal-abominable-snowman.jpg",
          "width": 3356,
          "is_default": false,
          "height": 988
        },
        "counts": {
          "following": 3,
          "posts": 1,
          "followers": 0,
          "stars": 102045
        },
        "type": "snowman",
        "id": "59521",
        "name": "The Abominable Snowman"
      }
    }
  ]
};

let apiURL = 'https://api.app.net';
let count = 20;

@inject(HttpClient, State, EventAggregator)
export class AdnAPI {
  constructor(http, state, ea) {
    this.http = http;
    this.state = state;
    this.ea = ea;
  }

  meta = [];

  getToken() {

    return new Promise(function (resolve, reject) {
      let token = localStorage.getItem("access_token");
      token ? resolve(token) : reject();
    }).then((token) => {
      this.isRequesting = true;
      return this.http.get(`${apiURL}/token?access_token=${token}`).then((response) => {
        this.isRequesting = false;
        this.state.tokenReturned = response.content.data;
        //this.ea.publish(new ApiStatus('Token Retrieved', { status: 'success' }));
        return response.content.data;

      }).catch((err) => {
        this.isRequesting = false;
        this.ea.publish(new ApiStatus('Invalid Token', { status: 'error' }));
        return {};
      });

    }).catch((err) => {
      //this.ea.publish(new ApiStatus('No Token Found', { status: 'info' }));
      return {};
    });
  }

  createPost(text, options) {

    this.ea.publish(new ApiStatus('Creating Post', { status: 'info' }));
    var jsonText = {
      text: text,
      entities: {
        parse_links: true,
        parse_markdown_links: true
      }
    };
    if (options.reply_to) {
      jsonText.reply_to = options.reply_to;
    }
    this.isRequesting = true;
    return this.http.configure(x => {
      x.withHeader('Authorization', 'Bearer ' + this.state.token);
      x.withHeader('Content-Type', 'application/json');
    }).post(`https://api.app.net/posts`, jsonText).then((response) => {
      this.meta = response.content.meta;
      this.isRequesting = false;
      this.ea.publish(new ApiStatus('Post Created', { status: 'success' }));
      return response.content.data;

    }).catch((err) => {
      this.isRequesting = false;
      this.ea.publish(new ApiStatus('Unable to post', { status: 'error' }));
      return {};
    });
  }

  reportPost(id) {

    this.ea.publish(new ApiStatus('Reporting Post', { status: 'info' }));
    this.isRequesting = true;
    return this.http.configure(x => {
      x.withHeader('Authorization', 'Bearer ' + this.state.token);
      x.withHeader('Content-Type', 'application/json');
    }).post(`https://api.app.net/posts/${id}/report`).then((response) => {
      this.meta = response.content.meta;
      this.isRequesting = false;
      this.ea.publish(new ApiStatus('Post Reported', { status: 'success' }));
      return response.content.data;

    }).catch((err) => {
      this.isRequesting = false;
      this.ea.publish(new ApiStatus('Unable to report post', { status: 'error' }));
      return {};
    });
  }

  textProcess(text) {

    let jsonText = { text: text };

    this.isRequesting = true;

    return this.http.configure(x => {
      x.withHeader('Authorization', 'Bearer ' + this.state.token);
      x.withHeader('Content-Type', 'application/json');
    }).post(`https://api.app.net/text/process?parse_links=true&parse_markdown_links=true`, jsonText).then((response) => {
      this.meta = response.content.meta;
      this.isRequesting = false;
      return response.content.data;

    }).catch((err) => {
      this.isRequesting = false;
      return nouser.data;
    });
  }

  loadPosts(id, more) {

    this.isRequesting = true;

    return this.getRandomUserId().then((user) => {
      let getUser = this.state.user_id || id;
      if (!getUser || getUser === ' ') { getUser = user; }

      return this.http.get(this.urlBuilder('posts', { id: getUser, more: more })).then((response) => {
        this.meta = response.content.meta;
        this.isRequesting = false;
        this.ea.publish(new ApiStatus((more ? 'Retrieved more Posts' : 'Retrieved Posts'), { status: 'success' }));
        return response.content.data;

      }).catch((err) => {
        this.isRequesting = false;
        this.ea.publish(new ApiStatus('Username not found, restoring known user', { status: 'error' }));
        return nouser.data;
      });
    });
  }

  loadLastPost() {
    return this.getToken().then(() => {
      this.isRequesting = true;
      return this.http.get(this.urlBuilder('lastposts', { id: this.state.tokenReturned.user.id, count: 1 })).then((response) => {
        this.isRequesting = false;
        this.ea.publish(new ApiStatus('Retrieved Last Post', { status: 'success' }));
        return response.content.data;

      }).catch((err) => {
        this.isRequesting = false;
        this.ea.publish(new ApiStatus('Unable to retrieve last post', { status: 'error' }));
        return nouser.data;
      });
    });
  }

  loadProfile(id, more) {

    this.isRequesting = true;

    return this.getRandomUserId().then((user) => {
      let getUser = this.state.user_id || id;
      if (!getUser || getUser === ' ') { getUser = user; }

      return this.http.get(this.urlBuilder('users', { id: getUser, more: more })).then((response) => {
        this.isRequesting = false;
        this.ea.publish(new ApiStatus('Retrieved Profile', { status: 'success' }));
        return response.content.data;

      }).catch((err) => {
        this.isRequesting = false;
        this.ea.publish(new ApiStatus('Username not found, restoring known user', { status: 'error' }));
        return nouser.data;
      });
    });
  }

  getAllUsers() {

    return this.http.get('https://api.nice.social/user/nicesummary').then((response) => {
      return response.content.data;

    }).catch((err) => {
      this.ea.publish(new ApiStatus('Nice.Social API Issue', { status: 'error' }));
      return 'berg';
    });
  }

  getRandomUserId() {

    return this.http.get('https://api.nice.social/user/nicesummary').then((response) => {
      var randomIndex = randomInteger(response.content.data.length);
      return response.content.data[randomIndex].user_id;

    }).catch((err) => {
      this.ea.publish(new ApiStatus('Nice.Social API Issue', { status: 'error' }));
      return 'berg';
    });
  }

  toggleStar(id, isTrue) {
    return this.toggleEntity(id, isTrue, 'star', isTrue ? 'Post Starred' : 'Post Unstarred');
  }

  toggleFollow(user, isTrue) {
    return this.toggleEntity(user.id, isTrue, 'follow', isTrue ? `Followed ${user.username}` : `Unfollowed ${user.username}`);
  }

  toggleRepost(id, isTrue) {
    return this.toggleEntity(id, isTrue, 'repost', isTrue ? 'Reposted' : 'Repost Removed');
  }

  toggleMute(user, isTrue) {
    return this.toggleEntity(user.id, isTrue, 'mute', isTrue ? `${user.username} Muted` : `${user.username} Unmuted`);
  }

  toggleBlock(user, isTrue) {
    return this.toggleEntity(user.id, isTrue, 'block', isTrue ? `${user.username} Blocked` : `${user.username} Unblocked`);
  }

  toggleEntity(id, isTrue, entity, successMsg) {

    this.isRequesting = true;
    return this.http[isTrue ? 'post' : 'delete'](this.urlBuilder(entity, { id: id })).then((response) => {
      this.isRequesting = false;
      this.ea.publish(new ApiStatus(successMsg, { status: 'success' }));
      return response.content.data;

    }).catch((err) => {
      this.isRequesting = false;
      this.ea.publish(new ApiStatus(`Unable to ${entity}`, { status: 'error' }));
      return {};
    });
  }

  load(url, params) {

    this.isRequesting = true;
    return this.http.get(this.urlBuilder(url, params)).then((response) => {
      this.meta = response.content.meta;
      this.isRequesting = false;
      this.ea.publish(new ApiStatus(`Retrieved ${url}`, { status: 'success' }));
      return response.content.data;

    }).catch((err) => {
      this.isRequesting = false;
      this.ea.publish(new ApiStatus(`Unable to load ${url}`, { status: 'success' }));
      return {};
    });
  }

  urlBuilder(action, params) {
    let standardParams = action==='thead' ? `include_post_annotations=1&include_deleted=0` : `include_post_annotations=1&include_deleted=1`;
    let accessTokenLS = this.state.token;
    let accessToken = accessTokenLS !== "undefined" && accessTokenLS !== null ? `access_token=${accessTokenLS}&` : "";
    let moreParam = params.more ? `before_id=${this.meta.min_id}&` : "";
    let countParam = !params.count? count : params.count;
    let endpoints = {
      conversations: `${apiURL}/posts/stream/explore/conversations`,
      photos: `${apiURL}/posts/stream/explore/photos`,
      trending: `${apiURL}/posts/stream/explore/trending`,
      checkins: `${apiURL}/posts/stream/explore/checkins`,
      star: `${apiURL}/posts/${params.id}/star`,
      repost: `${apiURL}/posts/${params.id}/repost`,
      post: `${apiURL}/post/${params.id}`,
      posts: `${apiURL}/users/${params.id}/posts`,
      stars: `${apiURL}/users/${params.id}/stars`,
      users: `${apiURL}/users/${params.id}`,
      interactions: `${apiURL}/users/me/interactions`,
      mentions: `${apiURL}/users/me/mentions`,
      followers: `${apiURL}/users/${params.id}/followers`,
      following: `${apiURL}/users/${params.id}/following`,
      follow: `${apiURL}/users/${params.id}/follow`,
      mute: `${apiURL}/users/${params.id}/mute`,
      block: `${apiURL}/users/${params.id}/block`,
      lastposts: `${apiURL}/users/${params.id}/posts`,
      unified: `${apiURL}/posts/stream/unified`,
      thread: `${apiURL}/posts/${params.id}/replies`,
      report: `${apiURL}/posts/${params.id}/report`,
      global: `${apiURL}/posts/stream/global`
    };

    if (action !== 'users' && action !== 'followers' && action !== 'report') {
      return `${endpoints[action]}?count=${countParam}&${accessToken}${moreParam}${standardParams}`;
    } else {
      return `${endpoints[action]}?${accessToken}`;
    }
  }
}<commit_msg>Restructured url builder<commit_after>import { inject } from 'aurelia-framework';
import { HttpClient } from 'aurelia-http-client';
import { NiceAPI } from './nice-api';
import { randomInteger } from 'resources/utility';
import { State } from './state';
import { EventAggregator } from 'aurelia-event-aggregator';
import { ApiStatus } from 'resources/messages';

let nouser = {
  meta: {
    "min_id": "2",
    "code": 200,
    "max_id": "1",
    "more": true
  },
  data: [
    {
      "created_at": "1790-05-10T15:16:44Z",
      "num_stars": 0,
      "num_replies": 0,
      "num_reposts": 0,
      "entities": {
        "mentions": [],
        "hashtags": [],
        "links": []
      },
      "user": {
        "username": "theabominablesnowman",
        "avatar_image": {
          "url": "/styles/abominablesnowman.jpg",
          "width": 200,
          "is_default": false,
          "height": 200
        },
        "description": {
          "html": "Oh Crap! you found me... Snowcone?

#snow ",
          "entities": {
            "mentions": [],
            "hashtags": [
              {
                "name": "snow",
                "len": 15,
                "pos": 65
              }
            ],
            "links": []
          }
        },
        "verified_link": "http://en.wikipedia.org/wiki/Yeti",
        "canonical_url": "https://alpha.app.net/",
        "verified_domain": "en.wikipedia.org/wiki/Yeti",
        "cover_image": {
          "url": "/styles/adventure-journal-abominable-snowman.jpg",
          "width": 3356,
          "is_default": false,
          "height": 988
        },
        "counts": {
          "following": 3,
          "posts": 1,
          "followers": 0,
          "stars": 102045
        },
        "type": "snowman",
        "id": "59521",
        "name": "The Abominable Snowman"
      }
    }
  ]
};

let apiURL = 'https://api.app.net';
let count = 20;

@inject(HttpClient, State, EventAggregator)
export class AdnAPI {
  constructor(http, state, ea) {
    this.http = http;
    this.state = state;
    this.ea = ea;
  }

  meta = [];

  getToken() {

    return new Promise(function (resolve, reject) {
      let token = localStorage.getItem("access_token");
      token ? resolve(token) : reject();
    }).then((token) => {
      this.isRequesting = true;
      return this.http.get(`${apiURL}/token?access_token=${token}`).then((response) => {
        this.isRequesting = false;
        this.state.tokenReturned = response.content.data;
        //this.ea.publish(new ApiStatus('Token Retrieved', { status: 'success' }));
        return response.content.data;

      }).catch((err) => {
        this.isRequesting = false;
        this.ea.publish(new ApiStatus('Invalid Token', { status: 'error' }));
        return {};
      });

    }).catch((err) => {
      //this.ea.publish(new ApiStatus('No Token Found', { status: 'info' }));
      return {};
    });
  }

  createPost(text, options) {

    this.ea.publish(new ApiStatus('Creating Post', { status: 'info' }));
    var jsonText = {
      text: text,
      entities: {
        parse_links: true,
        parse_markdown_links: true
      }
    };
    if (options.reply_to) {
      jsonText.reply_to = options.reply_to;
    }
    this.isRequesting = true;
    return this.http.configure(x => {
      x.withHeader('Authorization', 'Bearer ' + this.state.token);
      x.withHeader('Content-Type', 'application/json');
    }).post(`https://api.app.net/posts`, jsonText).then((response) => {
      this.meta = response.content.meta;
      this.isRequesting = false;
      this.ea.publish(new ApiStatus('Post Created', { status: 'success' }));
      return response.content.data;

    }).catch((err) => {
      this.isRequesting = false;
      this.ea.publish(new ApiStatus('Unable to post', { status: 'error' }));
      return {};
    });
  }

  reportPost(id) {

    this.ea.publish(new ApiStatus('Reporting Post', { status: 'info' }));
    this.isRequesting = true;
    return this.http.configure(x => {
      x.withHeader('Authorization', 'Bearer ' + this.state.token);
      x.withHeader('Content-Type', 'application/json');
    }).post(`https://api.app.net/posts/${id}/report`).then((response) => {
      this.meta = response.content.meta;
      this.isRequesting = false;
      this.ea.publish(new ApiStatus('Post Reported', { status: 'success' }));
      return response.content.data;

    }).catch((err) => {
      this.isRequesting = false;
      this.ea.publish(new ApiStatus('Unable to report post', { status: 'error' }));
      return {};
    });
  }

  textProcess(text) {

    let jsonText = { text: text };

    this.isRequesting = true;

    return this.http.configure(x => {
      x.withHeader('Authorization', 'Bearer ' + this.state.token);
      x.withHeader('Content-Type', 'application/json');
    }).post(`https://api.app.net/text/process?parse_links=true&parse_markdown_links=true`, jsonText).then((response) => {
      this.meta = response.content.meta;
      this.isRequesting = false;
      return response.content.data;

    }).catch((err) => {
      this.isRequesting = false;
      return nouser.data;
    });
  }

  loadPosts(id, more) {

    this.isRequesting = true;

    return this.getRandomUserId().then((user) => {
      let getUser = this.state.user_id || id;
      if (!getUser || getUser === ' ') { getUser = user; }

      return this.http.get(this.urlBuilder('posts', { id: getUser, more: more })).then((response) => {
        this.meta = response.content.meta;
        this.isRequesting = false;
        this.ea.publish(new ApiStatus((more ? 'Retrieved more Posts' : 'Retrieved Posts'), { status: 'success' }));
        return response.content.data;

      }).catch((err) => {
        this.isRequesting = false;
        this.ea.publish(new ApiStatus('Username not found, restoring known user', { status: 'error' }));
        return nouser.data;
      });
    });
  }

  loadLastPost() {
    return this.getToken().then(() => {
      this.isRequesting = true;
      return this.http.get(this.urlBuilder('lastposts', { id: this.state.tokenReturned.user.id, count: 1 })).then((response) => {
        this.isRequesting = false;
        this.ea.publish(new ApiStatus('Retrieved Last Post', { status: 'success' }));
        return response.content.data;

      }).catch((err) => {
        this.isRequesting = false;
        this.ea.publish(new ApiStatus('Unable to retrieve last post', { status: 'error' }));
        return nouser.data;
      });
    });
  }

  loadProfile(id, more) {

    this.isRequesting = true;

    return this.getRandomUserId().then((user) => {
      let getUser = this.state.user_id || id;
      if (!getUser || getUser === ' ') { getUser = user; }

      return this.http.get(this.urlBuilder('users', { id: getUser, more: more })).then((response) => {
        this.isRequesting = false;
        this.ea.publish(new ApiStatus('Retrieved Profile', { status: 'success' }));
        return response.content.data;

      }).catch((err) => {
        this.isRequesting = false;
        this.ea.publish(new ApiStatus('Username not found, restoring known user', { status: 'error' }));
        return nouser.data;
      });
    });
  }

  getAllUsers() {

    return this.http.get('https://api.nice.social/user/nicesummary').then((response) => {
      return response.content.data;

    }).catch((err) => {
      this.ea.publish(new ApiStatus('Nice.Social API Issue', { status: 'error' }));
      return 'berg';
    });
  }

  getRandomUserId() {

    return this.http.get('https://api.nice.social/user/nicesummary').then((response) => {
      var randomIndex = randomInteger(response.content.data.length);
      return response.content.data[randomIndex].user_id;

    }).catch((err) => {
      this.ea.publish(new ApiStatus('Nice.Social API Issue', { status: 'error' }));
      return 'berg';
    });
  }

  toggleStar(id, isTrue) {
    return this.toggleEntity(id, isTrue, 'star', isTrue ? 'Post Starred' : 'Post Unstarred');
  }

  toggleFollow(user, isTrue) {
    return this.toggleEntity(user.id, isTrue, 'follow', isTrue ? `Followed ${user.username}` : `Unfollowed ${user.username}`);
  }

  toggleRepost(id, isTrue) {
    return this.toggleEntity(id, isTrue, 'repost', isTrue ? 'Reposted' : 'Repost Removed');
  }

  toggleMute(user, isTrue) {
    return this.toggleEntity(user.id, isTrue, 'mute', isTrue ? `${user.username} Muted` : `${user.username} Unmuted`);
  }

  toggleBlock(user, isTrue) {
    return this.toggleEntity(user.id, isTrue, 'block', isTrue ? `${user.username} Blocked` : `${user.username} Unblocked`);
  }

  toggleEntity(id, isTrue, entity, successMsg) {

    this.isRequesting = true;
    return this.http[isTrue ? 'post' : 'delete'](this.urlBuilder(entity, { id: id })).then((response) => {
      this.isRequesting = false;
      this.ea.publish(new ApiStatus(successMsg, { status: 'success' }));
      return response.content.data;

    }).catch((err) => {
      this.isRequesting = false;
      this.ea.publish(new ApiStatus(`Unable to ${entity}`, { status: 'error' }));
      return {};
    });
  }

  load(url, params) {

    this.isRequesting = true;
    return this.http.get(this.urlBuilder(url, params)).then((response) => {
      this.meta = response.content.meta;
      this.isRequesting = false;
      this.ea.publish(new ApiStatus(`Retrieved ${url}`, { status: 'success' }));
      return response.content.data;

    }).catch((err) => {
      this.isRequesting = false;
      this.ea.publish(new ApiStatus(`Unable to load ${url}`, { status: 'success' }));
      return {};
    });
  }

  urlBuilder(action, params) {
    let standardParams = action==='thead' ? `include_post_annotations=1&include_deleted=0` : `include_post_annotations=1&include_deleted=1`;
    let accessTokenLS = this.state.token;
    let accessToken = accessTokenLS !== "undefined" && accessTokenLS !== null ? `access_token=${accessTokenLS}&` : "";
    let moreParam = params.more ? `before_id=${this.meta.min_id}&` : "";
    let countParam = !params.count? count : params.count;
    let endpoints = {
      conversations: `${apiURL}/posts/stream/explore/conversations?`,
      photos: `${apiURL}/posts/stream/explore/photos?`,
      trending: `${apiURL}/posts/stream/explore/trending?`,
      checkins: `${apiURL}/posts/stream/explore/checkins?`,
      star: `${apiURL}/posts/${params.id}/star?`,
      repost: `${apiURL}/posts/${params.id}/repost?`,
      post: `${apiURL}/post/${params.id}?`,
      posts: `${apiURL}/users/${params.id}/posts?`,
      stars: `${apiURL}/users/${params.id}/stars?`,
      users: `${apiURL}/users/${params.id}?`,
      interactions: `${apiURL}/users/me/interactions?interaction_actions=star,repost,follow&`,
      mentions: `${apiURL}/users/me/mentions?`,
      followers: `${apiURL}/users/${params.id}/followers?`,
      following: `${apiURL}/users/${params.id}/following?`,
      follow: `${apiURL}/users/${params.id}/follow?`,
      mute: `${apiURL}/users/${params.id}/mute?`,
      block: `${apiURL}/users/${params.id}/block?`,
      lastposts: `${apiURL}/users/${params.id}/posts?`,
      unified: `${apiURL}/posts/stream/unified?`,
      thread: `${apiURL}/posts/${params.id}/replies?`,
      report: `${apiURL}/posts/${params.id}/report?`,
      global: `${apiURL}/posts/stream/global?`
    };

    if (action !== 'users' && action !== 'followers' && action !== 'report') {
      return `${endpoints[action]}count=${countParam}&${accessToken}${moreParam}${standardParams}`;
    } else {
      return `${endpoints[action]}${accessToken}`;
    }
  }
}<|endoftext|>


<commit_before>import {inject, ObserverLocator} from 'aurelia-framework';
import {Cache} from './cache';

@inject(ObserverLocator, Cache)
export class Culture {
  options = ['en-US', 'pt-BR'];
  _handlers = [];

  constructor(observerLocator, cache) {
    this.cache = cache;
    this.base = this.options[0];
    this.current = cache.getItem('culture.current') || this.base;

    observerLocator.getObserver(this, 'current')
      .subscribe(newValue => this._currentChanged(newValue));
  }

  _currentChanged(newValue) {
    this.cache.setItem('culture.current', newValue);
    this._handlers.forEach(x => x(newValue));
  }

  onChange(callback) {
    this._handlers.push(callback);

    return {
      dispose() {
        let index = this._handlers.indexOf(callback);
        this._handlers.splice(index, 1);
      }
    };
  }
}
<commit_msg>fix(culture): use correct object instance to reference event handlers<commit_after>import {inject, ObserverLocator} from 'aurelia-framework';
import {Cache} from './cache';

@inject(ObserverLocator, Cache)
export class Culture {
  options = ['en-US', 'pt-BR'];
  _handlers = [];

  constructor(observerLocator, cache) {
    this.cache = cache;
    this.base = this.options[0];
    this.current = cache.getItem('culture.current') || this.base;

    observerLocator.getObserver(this, 'current')
      .subscribe(newValue => this._currentChanged(newValue));
  }

  _currentChanged(newValue) {
    this.cache.setItem('culture.current', newValue);
    this._handlers.forEach(x => x(newValue));
  }

  onChange(callback) {
    let handlers = this._handlers;

    handlers.push(callback);

    return {
      dispose() {
        let index = handlers.indexOf(callback);

        if (index !== -1) {
          handlers.splice(index, 1);
        }
      }
    };
  }
}
<|endoftext|>


<commit_before>import config from '../../index';
import express from 'express';
import request from 'request';

class WebhookService {
  constructor () {};

  static tokenVerify (req, res) {
    if (!req.query['hub.verify_token'] === config.FACEBOOK_PAGE_ACCESS_TOKEN) {
      return res.send('Error, wrong token');
    }

    return res.send(req.query['hub.challenge']);
  }

  static messageEvent (req, res) {
    let messagingEvents = req.body.entry[0].messaging;

    console.log(req.body)

    messagingEvents.map(messagingEvent => {
      let message = messagingEvent.message.text;
      let sender = messagingEvent.sender.id;

      console.log('Destinatário: ', sender, 'Mensagem: ', message)
      WebhookService.textMessage(sender, message);
    })

    res.sendStatus(200);
  }

  static textMessage (sender, text) {
    request({
      url: 'https://graph.facebook.com/v2.6/me/messages',
      qs: { access_token: config.FACEBOOK_PAGE_ACCESS_TOKEN },
      method: 'POST',
      json: {
        recipient: { id: sender },
        message: { text: text },
      }
    }, (error, response, body) => {
      if (error) {
        console.log('Error sending messages: ', error)
        return;
      }
      if (response.body.error) {
        console.log('Error: ', response.body.error)
        return;
      }
    })
  }
}

export default WebhookService;<commit_msg>Add for loop only for test<commit_after>import config from '../configs/config';
import express from 'express';
import request from 'request';

class WebhookService {
  constructor () {};

  static tokenVerify (req, res) {
    if (!req.query['hub.verify_token'] === config.FACEBOOK_PAGE_ACCESS_TOKEN) {
      return res.send('Error, wrong token');
    }

    return res.send(req.query['hub.challenge']);
  }

  static messageEvent (req, res) {
    let messagingEvents = req.body.entry[0].messaging;

    console.log(req.body)

    /*messagingEvents.map(messagingEvent => {
      console.log(req.body)
      let messages = messagingEvent.message;
      let sender = messagingEvent.sender;

      // console.log('Destinatário: ', sender, 'Mensagem: ', messages)
    })*/
    for (let i = 0; i < messagingEvents.length; i++) {
       let event = req.body.entry[0].messaging[i];
       let sender = event.sender.id;

      WebhookService.textMessage(sender, event.message.text);
    }

    res.sendStatus(200);
  }

  static textMessage (sender, messages) {
    request({
      url: 'https://graph.facebook.com/v2.6/me/messages',
      qs: { access_token: config.FACEBOOK_PAGE_ACCESS_TOKEN },
      method: 'POST',
      json: {
        recipient: { id: sender },
        message: { text: messages },
      }
    }, (error, response, body) => {
      if (error) {
        console.log('Error sending messages: ', error)
        return;
      }
      if (response.body.error) {
        console.log('Error: ', response.body.error)
        return;
      }
    })
  }
}

export default WebhookService;<|endoftext|>


<commit_before>var FrameManager = (function () {

var nativeRequestAnimFrame =
	window.requestAnimationFrame ||
	window.webkitRequestAnimationFrame ||
	window.mozRequestAnimationFrame ||
	window.oRequestAnimationFrame ||
	window.msRequestAnimationFrame ||
	function (callback, element) {
		window.setTimeout(function () {
			return callback((new Date()).valueOf() + 16);
		}, 16);
	};

var FrameManager = function (callback) {
	// callback's args are those of window.requestAnimationFrame.
	// Its return value answers "Do I need to draw again?".
	// "this" is the FrameManager instance, which precomputes:
	//     this.prev_time = scheduled time for the frame that
	//         triggered this one via its return value
	//     this.dt = ticks since prev_time || this.DEFAULT_DT
	this.next_id = null;
	this.prev_time = null;
	this.dt = this.DEFAULT_DT;
	this.drawCallback = callback || function () {};
	this.drawWrapper = this.drawWrapper.bind(this);
};

FrameManager.prototype = {
	DEFAULT_DT: 16,

	requestFrame: function () {
		if (!this.next_id) {
			this.next_id = nativeRequestAnimFrame(this.drawWrapper);
		}
		return this.next_id;
	},
	drawWrapper: function (time_scheduled) {
		this.next_id = null;
		this.dt = (time_scheduled - this.prev_time) || this.DEFAULT_DT;
		// Call the user-supplied callback. Might take a while.
		var draw_again = this.drawCallback.apply(this, arguments);
		// Request another frame if we need to.
		// Is after the callback for better chances of making the deadline.
		if (draw_again) {
			this.prev_time = time_scheduled;
			this.requestFrame();
		} else {
			this.prev_time = null;
		}
	},
};

return FrameManager;
})();
<commit_msg>FrameManager: Avoid big jumps after long pauses.<commit_after>var FrameManager = (function () {

var nativeRequestAnimFrame =
	window.requestAnimationFrame ||
	window.webkitRequestAnimationFrame ||
	window.mozRequestAnimationFrame ||
	window.oRequestAnimationFrame ||
	window.msRequestAnimationFrame ||
	function (callback, element) {
		window.setTimeout(function () {
			return callback((new Date()).valueOf() + 16);
		}, 16);
	};

var FrameManager = function (callback) {
	// callback's args are those of window.requestAnimationFrame.
	// Its return value answers "Do I need to draw again?".
	// "this" is the FrameManager instance, which precomputes:
	//     this.prev_time = scheduled time for the frame that
	//         triggered this one via its return value
	//     this.dt = ticks since prev_time || this.DEFAULT_DT
	this.next_id = NaN;
	this.prev_time = NaN;
	this.dt = this.DEFAULT_DT;
	this.drawCallback = callback || function () {};
	this.drawWrapper = this.drawWrapper.bind(this);
};

FrameManager.prototype = {
	DEFAULT_DT: 16,

	requestFrame: function () {
		if (!this.next_id) {
			this.next_id = nativeRequestAnimFrame(this.drawWrapper);
		}
		return this.next_id;
	},
	drawWrapper: function (time_scheduled) {
		this.next_id = NaN;
		this.dt = (time_scheduled - this.prev_time) || this.DEFAULT_DT;
		// Call the user-supplied callback. Might take a while.
		var draw_again = this.drawCallback.apply(this, arguments);
		// Request another frame if we need to.
		// Is after the callback for better chances of making the deadline.
		if (draw_again) {
			this.prev_time = time_scheduled;
			this.requestFrame();
		} else {
			this.prev_time = NaN;
		}
	},
};

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


<commit_before>var Nightmare = require('nightmare');
var nightmare = Nightmare({ show: false });
var request = require('request');
var cheerio = require('cheerio');

const URL = "http://mrp.gov.ro/web/category/consultare-publica/";
nightmare
    .goto(URL)
    .wait("article")
    .evaluate( () => {
        return [... document.querySelectorAll('article')].map(elem => {
            var postCategory = elem.querySelector('.post-category').innerText;
            var timestamp = elem.querySelector('time').getAttribute('datetime');
            var titlu = elem.querySelector('.post-title a').innerText;
            var detailLink = elem.querySelector('.post-title a').getAttribute('href');

            // request(detailLink, function(err, resp, body){
            //     var $ = cheerio.load(body);
            //     var linkElement = $('.post-inner a');
            //     var link = linkElement.attr('href');
            //     console.log(link);
            //
            //     });

            result = {
                postcat:postCategory,
                timestamp : timestamp,
                titlu : titlu,
                link : detailLink,

            };

            return result;

        }
        )

})
    .end()

    .then(function (result) {
        var count = 0;
        result.forEach(function(item) {
           require('nightmare')({show: false})
               .goto(item.link)
               .wait("a")
               .evaluate((uri) => {
                   var docLinks = {};
                   docLinks[uri] = [];
                   [... document.querySelectorAll('.entry-inner a[target="_blank"]')].map( (aHref) => {
                       docLinks[uri].push(aHref.href);
                   });

                   [... document.querySelectorAll('.entry-inner a.gde-link')].map( (aHref) => {
                       docLinks[uri].push(aHref.href);
                   });

                   return docLinks;
               },item.link)
               .end()
               .then(function (res) {
                   count++;
                   result.map((value)=> {
                       for(let key in res) {
                           if(key == value.link) {
                               value.docLinks = res[value.link];
                               break;
                           }
                       }
                   });

                   if(result.length == count) {
                       console.log("DONE: ", result);
                   }
                   // finalJson = result;
                    // console.log("result: ", result);
                   // console.log(result);
                   // result.docLinks.map((element) => {console.log("PAGE LINKS " ,element)})

               });

        });
        // console.log("final: ", finalJson);

    });<commit_msg>work still in progress<commit_after>var Nightmare = require('nightmare');
var nightmare = Nightmare({ show: false });
var request = require('request');
var cheerio = require('cheerio');

var finalJson = [];

const URL = "http://mrp.gov.ro/web/category/consultare-publica/";
nightmare
    .goto(URL)
    .wait(".wp-pagenavi")
    .evaluate(() => {
        var pageLink = [];
        [... document.querySelectorAll('.wp-pagenavi')].map( link => {
         pageLink.push(link.querySelector('a').getAttribute(('href')));

        });
        console.log('URL IZ THIS ', pageLink);
        return pageLink;
    })
.end()
    .then(function(pageUrl) {
        pageUrl = [].concat([URL], pageUrl);
        pageUrl.forEach(function (page) {

            require('nightmare')({show: false})
                .goto(page)
                .wait("article")
                .evaluate(() => {
                return [... document.querySelectorAll('article')].map(elem => {
                var postCategory = elem.querySelector('.post-category').innerText;
            var timestamp = elem.querySelector('time').getAttribute('datetime');
            var titlu = elem.querySelector('.post-title a').innerText;
            var rezumat = elem.querySelector('.excerpt p').innerText;
            var detailLink = elem.querySelector('.post-title a').getAttribute('href');

            // request(detailLink, function(err, resp, body){
            //     var $ = cheerio.load(body);
            //     var linkElement = $('.post-inner a');
            //     var link = linkElement.attr('href');
            //     console.log(link);
            //
            //     });

            result = {
                postcat: postCategory,
                timestamp: timestamp,
                titlu: titlu,
                rezumat : rezumat,
                link: detailLink,

            };

            return result;

        }
            )

        })
            .end()

                .then(function (result) {
                    var count = 0;
                    result.forEach(function(item) {
                        require('nightmare')({show: false})
                            .goto(item.link)
                            .wait("a")
                            .evaluate((uri) => {
                            var docLinks = {};
                        docLinks[uri] = [];
                        [... document.querySelectorAll('.entry-inner a[target="_blank"]')].map( (aHref) => {
                            docLinks[uri].push(aHref.href);
                    });

                        [... document.querySelectorAll('.entry-inner a.gde-link')].map( (aHref) => {
                            docLinks[uri].push(aHref.href);
                    });

                        return docLinks;
                    },item.link)
                        .end()
                            .then(function (res) {
                                count++;
                                result.map((value)=> {
                                    for(let key in res) {
                                    if(key == value.link) {
                                        value.docLinks = res[value.link];
                                        break;
                                    }
                                }
                            });

                                if(result.length == count) {
                                    console.log("DONE: ", result);
                                    result.map((item) => {

                                    var postResult = {
                                        "identifier" : item.link,
                                        "title" : item.titlu,
                                        "institution" : "relparlament",
                                        "date" : item.timestamp.slice(0,10),
                                        "decription" : item.rezumat,
                                        "feedback_days" : null,
                                        "contact" : null,
                                        "documents" : item.docLinks

                                    };

                                    console.log(postResult);
                                    })
                                    // finalJson.push(result);
                                    // console.log(finalJson);
                                }
                                // finalJson = result;
                                // console.log("result: ", result);
                                // console.log(result);
                                // result.docLinks.map((element) => {console.log("PAGE LINKS " ,element)})

                            });

                    });
                    // console.log("final: ", finalJson);

                })
        })
    });
<|endoftext|>


<commit_before>// TODO
// remove punctuation
// ignore blank replies
giphyToken = "dc6zaTOxFJmzC"

Slack = require("slack-client")
giphy = require("giphy")(giphyToken)

Gifbot = function (slackToken) {
	this.slackToken = slackToken
	this.slack = new Slack(slackToken, true, true)

	this.slack.login()
	this.slack.on("error", function (err) {
		console.error("error! oh noez!")
		console.error(err);
		throw "this is an error"
	})
}

module.exports = Gifbot

// slack.on("open", function() {
// 	// console.log("opened!")
// 	// console.log(slack)
// })

makeMention = function(userId) {
	// wraps userid in slack tag
	return '<@' + userId + '>';
};

isDirect = function(userId, messageText) {
	var userTag = makeMention(userId);

	return (messageText.indexOf(userTag) != -1)
};

// function dmGif(searchTerms, userId, channelId) {
// 	giphy.random({
// 		tag: searchTerms,
// 		rating: "pg-13"
// 	}, function(err, gif, res) {
// 		// 2. direct message it to the user
// 		dm = slack.getDMByName(slack.getUserByID(userId).name)
// 		dm.send("> How does this one look? \n> " + gif.data.url)
// 		users[userId] = {
// 			"search": searchTerms,
// 			"latestGif": gif.data.url,
// 			"channelId": channelId
// 		}
// 		// console.log(users);
// 	})
// }
//
// slack.on("message", function(message) {
// 	// console.log(message);
// 	// console.log(users)
// 	var channel = slack.getChannelGroupOrDMByID(message.channel);
// 	var user = slack.getUserByID(message.user);
// 	// console.log(channel)
// 	if (message.type === 'message' && !("subtype" in message) && isDirect(slack.self.id, message.text)) {
// 		searchTerms = message.text.replace(makeMention(slack.self.id), "")
// 			// 1. grab a random gif from the giphy api
// 		dmGif(searchTerms, user.id, channel.id)
// 			// 3. if the user responds "yes", post the gif to the channel
// 			// 4. if the user responds "no", goto step 1
// 	}
//
//
// 	// ignore message events sent by posting to the DM channel
// 	else if ((message.user != slack.self.id) && !("subtype" in message)) {
// 		// console.log("got a dm");
// 		if (message.text == "yes") {
// 			// console.log("posting gif");
// 			// post the latest gif for the user into the channel
// 			slack.getChannelGroupOrDMByID(users[message.user].channelId).send(users[message.user].latestGif)
// 		} else {
// 			dmGif(users[message.user].search, user.id, users[message.user].channelId)
// 		}
// 	}
//
// })
//
// users = {}
<commit_msg>not autoreconnecting to slack<commit_after>// TODO
// remove punctuation
// ignore blank replies
giphyToken = "dc6zaTOxFJmzC"

Slack = require("slack-client")
giphy = require("giphy")(giphyToken)
var events = require('events');

Gifbot = function (slackToken) {
	// see link below...
	events.EventEmitter.call(this)
	this.slackToken = slackToken
	this.slack = new Slack(slackToken, false, true)

	this.slack.login()
	this.slack.on("error", function (err) {
		console.error("error! oh noez!")
		console.error(err);
	}.bind(this))
}

// ???
// http://www.sitepoint.com/nodejs-events-and-eventemitter/
Gifbot.prototype.__proto__ = events.EventEmitter.prototype

module.exports = Gifbot

// slack.on("open", function() {
// 	// console.log("opened!")
// 	// console.log(slack)
// })

makeMention = function(userId) {
	// wraps userid in slack tag
	return '<@' + userId + '>';
};

isDirect = function(userId, messageText) {
	var userTag = makeMention(userId);

	return (messageText.indexOf(userTag) != -1)
};

// function dmGif(searchTerms, userId, channelId) {
// 	giphy.random({
// 		tag: searchTerms,
// 		rating: "pg-13"
// 	}, function(err, gif, res) {
// 		// 2. direct message it to the user
// 		dm = slack.getDMByName(slack.getUserByID(userId).name)
// 		dm.send("> How does this one look? \n> " + gif.data.url)
// 		users[userId] = {
// 			"search": searchTerms,
// 			"latestGif": gif.data.url,
// 			"channelId": channelId
// 		}
// 		// console.log(users);
// 	})
// }
//
// slack.on("message", function(message) {
// 	// console.log(message);
// 	// console.log(users)
// 	var channel = slack.getChannelGroupOrDMByID(message.channel);
// 	var user = slack.getUserByID(message.user);
// 	// console.log(channel)
// 	if (message.type === 'message' && !("subtype" in message) && isDirect(slack.self.id, message.text)) {
// 		searchTerms = message.text.replace(makeMention(slack.self.id), "")
// 			// 1. grab a random gif from the giphy api
// 		dmGif(searchTerms, user.id, channel.id)
// 			// 3. if the user responds "yes", post the gif to the channel
// 			// 4. if the user responds "no", goto step 1
// 	}
//
//
// 	// ignore message events sent by posting to the DM channel
// 	else if ((message.user != slack.self.id) && !("subtype" in message)) {
// 		// console.log("got a dm");
// 		if (message.text == "yes") {
// 			// console.log("posting gif");
// 			// post the latest gif for the user into the channel
// 			slack.getChannelGroupOrDMByID(users[message.user].channelId).send(users[message.user].latestGif)
// 		} else {
// 			dmGif(users[message.user].search, user.id, users[message.user].channelId)
// 		}
// 	}
//
// })
//
// users = {}
<|endoftext|>


<commit_before>module.exports = {
  'GitHub example testing': function (test) {
  test
    .open('https://github.com')
    .waitForElement('input[name=q]')
    .assert.title().is('GitHub · Build software better, together.', 'GitHub has the correct page title!')
    .type('input[name=q]', 'dalekjs')
    .submit('.js-site-search-form')
    .assert.text('.sort-bar > h3:nth-child(2)', 'We\'ve found 53 repository results')
    .execute(function() {
      return $('li.repo-list-item:nth-child(1) > h3:nth-child(2) > a:nth-child(1)').text() === 'dalekjs/dalek'
    })
    .done();
  }
};
<commit_msg>Use waitFor that actually validates something instead of misplaced execute<commit_after>module.exports = {
  'GitHub example testing': function (test) {
  test
    .open('https://github.com')
    .waitForElement('input[name=q]')
    .assert.title().is('GitHub · Build software better, together.', 'GitHub has the correct page title!')
    .type('input[name=q]', 'dalekjs')
    .submit('.js-site-search-form')
    .assert.text('.sort-bar > h3:nth-child(2)', 'We\'ve found 53 repository results')
    .waitFor(function() {
      return $('li.repo-list-item:nth-child(1) > h3:nth-child(2) > a:nth-child(1)').text() === 'dalekjs/dalek'
    })
    .done();
  }
};
<|endoftext|>