<commit_before>import React, { PropTypes } from 'react';
import { Link } from 'react-router';

import { getName, getProductMainImage } from 'commons/utils/productUtil';

export default React.createClass({
  propTypes: {
    order: PropTypes.object,
    addresses: PropTypes.object,
    activeAddressId: PropTypes.number,
    inipay: PropTypes.func.isRequired,
  },
  contextTypes: {
    activeLocale: PropTypes.string,
    activeCurrency: PropTypes.string,
  },
  getInitialState() {
    return { showPay: false };
  },
  toggle() {
    if (!this.state.showPay) {
      $('#root').css({
        height: $(window).height(),
        overflow: 'hidden',
      });
    } else {
      $('#root').css({
        height: 'auto',
        overflow: 'auto',
      });
    }
    this.setState({ showPay: !this.state.showPay });
  },
  inipay() {
    const { addresses, activeAddressId } = this.props;
    if (!addresses || !Object.keys(addresses).length || !activeAddressId) {
      // FIXME no alert
      alert('please input shipping address');
      return;
    }

    this.props.inipay(this.props.order.id).then((res) => {
      this.refs.mid.value = res.mid;
      this.refs.oid.value = res.oid;
      this.refs.amt.value = res.price;
      this.refs.email.value = res.buyeremail;
      this.refs.mobile.value = res.buyertel;
      this.refs.uname.value = res.buyername;
      this.refs.nextUrl.value = res.returnUrl;
      this.refs.inipay.action = `https://mobile.inicis.com/smart/${'wcard'}/`;
      this.refs.inipay.submit();
    });
  },
  renderAddresses() {
    const { order, addresses, activeAddressId } = this.props;
    if (!addresses || !Object.keys(addresses).length) {
      return (

            <Link id="change-address" to={`/orders/${order.id}/address/add`}>Add Shipping Address</Link>

        );
    }
    let selectedAddress = null;
    for (const i in addresses) {
      /* if no default address */
      if (!selectedAddress) {
        selectedAddress = addresses[i];
      }
      if (addresses[i].id === activeAddressId) {
        selectedAddress = addresses[i];
      }
    }

    return (

{selectedAddress.detail.name}

            {selectedAddress.detail.streetAddress}

 {selectedAddress.detail.city}

 {selectedAddress.countryCode}

 {selectedAddress.detail.postalCode}

          <Link id="change-address" to={`/orders/${order.id}/address`}>Change Shipping Address</Link>

      );
  },
  renderProducts() {
    const { order } = this.props;
    const { activeLocale, activeCurrency } = this.context;
    // FIXME orderProduct -> orderProductVariant
    if (order && order.orderProducts && order.orderProducts.length) {
      return order.orderProducts.map((orderProduct) => {
        const renderBrand = () => {
          if (orderProduct.brand && orderProduct.brand.name) {
            return (

Seller:
                <Link className="company-name" to={`/brand/${orderProduct.brand.id}`}>
                  {orderProduct.brand.name.en}
                </Link>

              );
          }
          return null;
        };
        let variantStr = '';
        if (orderProduct.productVariant && orderProduct.productVariant.data) {
          const variantData = orderProduct.productVariant.data;
          for (const key of Object.keys(variantData)) {
            variantStr += `${variantData[key]} `;
          }
        } else {
          variantStr = orderProduct.productVariant.sku;
        }

        return (

            {renderBrand()}

                  {getName(orderProduct.product)}

Options:

                  {variantStr}

Price:

{activeCurrency} {orderProduct[activeCurrency]}

Quantity:

                  {orderProduct.quantity}
                  piece
                  { /*
only 9999 units available
 */ }

              { /*

Shipping :

                      US $0.84 via Seller's Shipping Method

                            <input id="scs-8964239302-CAINIAO_STANDARD_E" name="scs-8964239302-company" value="CAINIAO_STANDARD_E" type="radio" className="shipping-radio" checked="checked"/>

                              15-34 Days
                              $0
                              CAINIAO_STANDARD_E

                      Delivery Time : 39 -60 days

 Processing Time : 6 days

 */ }

              { /*

Message to Supplier: (Optional)

                    <textarea className="change-message" rows="1"></textarea>

 */ }

Number is error

              { /*

                Subtotal:
                US $6.84

                Shipping:
                US $0.84

 */ }

                Total:

                  {activeCurrency} {orderProduct[`total${activeCurrency}`]}

          );
      });
    }
    return
;
  },
  render() {
    const { order } = this.props;
    const { activeLocale, activeCurrency } = this.context;
    if (!order) {
      return
;
    }

    return (

shipping address :

          {this.renderAddresses()}

        {this.renderProducts()}

order summary

              상품금액:

                {activeCurrency} {order[`subtotal${activeCurrency}`]}

              부가세:

                {activeCurrency} {order[`tax${activeCurrency}`]}

              사입비:

                {activeCurrency} {order[`handlingFee${activeCurrency}`]}

              배송비:

                {activeCurrency} {order[`shippingCost${activeCurrency}`]}

            { /*

              AliExpress Coupon:
              Please enter Coupon code

            */ }

              Grand total :

                {activeCurrency} {order[`total${activeCurrency}`]}

          <form method="post" acceptCharset="euc-kr" ref="inipay">
            <input type="hidden" name="P_MID" ref="mid" />
            <input type="hidden" name="P_OID" ref="oid" />
            <input type="hidden" name="P_AMT" ref="amt" />
            <input type="hidden" name="P_MNAME" ref="mname" value="LINKSHOPS" />
            <input type="hidden" name="P_UNAME" ref="uname" />
            <input type="hidden" name="P_GOODS" ref="goods" value="의류" />
            <input type="hidden" name="P_MOBILE" ref="mobile" />
            <input type="hidden" name="P_EMAIL" ref="email" />
            <input type="hidden" name="P_NEXT_URL" ref="nextUrl" />
            <input type="hidden" name="P_NOTI_URL" ref="notiUrl" />
            <input type="hidden" name="P_CHARSET" value="utf8" />
            <input type="hidden" name="P_RESERVED" value="twotrs_isp=Y&block_isp=Y&twotrs_isp_noti=N" />
          </form>
          <input
            type="submit"
            id="create-order"
            value="place order "
            className="ui-button ui-button-main"
            onClick={this.toggle}
          />

            Payment Method

              신용카드

            { /* TODO

              핸드폰 결제

              계좌이체

              무통장입금

 */ }

    );
  },
});
<commit_msg>add commented out mobile vacct noti url.<commit_after>import React, { PropTypes } from 'react';
import { Link } from 'react-router';

import { getName, getProductMainImage } from 'commons/utils/productUtil';

export default React.createClass({
  propTypes: {
    order: PropTypes.object,
    addresses: PropTypes.object,
    activeAddressId: PropTypes.number,
    inipay: PropTypes.func.isRequired,
  },
  contextTypes: {
    activeLocale: PropTypes.string,
    activeCurrency: PropTypes.string,
  },
  getInitialState() {
    return { showPay: false };
  },
  toggle() {
    if (!this.state.showPay) {
      $('#root').css({
        height: $(window).height(),
        overflow: 'hidden',
      });
    } else {
      $('#root').css({
        height: 'auto',
        overflow: 'auto',
      });
    }
    this.setState({ showPay: !this.state.showPay });
  },
  inipay() {
    const { addresses, activeAddressId } = this.props;
    if (!addresses || !Object.keys(addresses).length || !activeAddressId) {
      // FIXME no alert
      alert('please input shipping address');
      return;
    }

    this.props.inipay(this.props.order.id).then((res) => {
      this.refs.mid.value = res.mid;
      this.refs.oid.value = res.oid;
      this.refs.amt.value = res.price;
      this.refs.email.value = res.buyeremail;
      this.refs.mobile.value = res.buyertel;
      this.refs.uname.value = res.buyername;
      this.refs.nextUrl.value = res.returnUrl;
      this.refs.inipay.action = `https://mobile.inicis.com/smart/${'wcard'}/`;
      // FIXME
      // this.refs.notiUrl.value = 'https://www.linkshops.com/api/v1/inipay/vacct_mobile';
      // this.refs.inipay.action = `https://mobile.inicis.com/smart/${'vbank'}/`;
      this.refs.inipay.submit();
    });
  },
  renderAddresses() {
    const { order, addresses, activeAddressId } = this.props;
    if (!addresses || !Object.keys(addresses).length) {
      return (

            <Link id="change-address" to={`/orders/${order.id}/address/add`}>Add Shipping Address</Link>

        );
    }
    let selectedAddress = null;
    for (const i in addresses) {
      /* if no default address */
      if (!selectedAddress) {
        selectedAddress = addresses[i];
      }
      if (addresses[i].id === activeAddressId) {
        selectedAddress = addresses[i];
      }
    }

    return (

{selectedAddress.detail.name}

            {selectedAddress.detail.streetAddress}

 {selectedAddress.detail.city}

 {selectedAddress.countryCode}

 {selectedAddress.detail.postalCode}

          <Link id="change-address" to={`/orders/${order.id}/address`}>Change Shipping Address</Link>

      );
  },
  renderProducts() {
    const { order } = this.props;
    const { activeLocale, activeCurrency } = this.context;
    // FIXME orderProduct -> orderProductVariant
    if (order && order.orderProducts && order.orderProducts.length) {
      return order.orderProducts.map((orderProduct) => {
        const renderBrand = () => {
          if (orderProduct.brand && orderProduct.brand.name) {
            return (

Seller:
                <Link className="company-name" to={`/brand/${orderProduct.brand.id}`}>
                  {orderProduct.brand.name.en}
                </Link>

              );
          }
          return null;
        };
        let variantStr = '';
        if (orderProduct.productVariant && orderProduct.productVariant.data) {
          const variantData = orderProduct.productVariant.data;
          for (const key of Object.keys(variantData)) {
            variantStr += `${variantData[key]} `;
          }
        } else {
          variantStr = orderProduct.productVariant.sku;
        }

        return (

            {renderBrand()}

                  {getName(orderProduct.product)}

Options:

                  {variantStr}

Price:

{activeCurrency} {orderProduct[activeCurrency]}

Quantity:

                  {orderProduct.quantity}
                  piece
                  { /*
only 9999 units available
 */ }

              { /*

Shipping :

                      US $0.84 via Seller's Shipping Method

                            <input id="scs-8964239302-CAINIAO_STANDARD_E" name="scs-8964239302-company" value="CAINIAO_STANDARD_E" type="radio" className="shipping-radio" checked="checked"/>

                              15-34 Days
                              $0
                              CAINIAO_STANDARD_E

                      Delivery Time : 39 -60 days

 Processing Time : 6 days

 */ }

              { /*

Message to Supplier: (Optional)

                    <textarea className="change-message" rows="1"></textarea>

 */ }

Number is error

              { /*

                Subtotal:
                US $6.84

                Shipping:
                US $0.84

 */ }

                Total:

                  {activeCurrency} {orderProduct[`total${activeCurrency}`]}

          );
      });
    }
    return
;
  },
  render() {
    const { order } = this.props;
    const { activeLocale, activeCurrency } = this.context;
    if (!order) {
      return
;
    }

    return (

shipping address :

          {this.renderAddresses()}

        {this.renderProducts()}

order summary

              상품금액:

                {activeCurrency} {order[`subtotal${activeCurrency}`]}

              부가세:

                {activeCurrency} {order[`tax${activeCurrency}`]}

              사입비:

                {activeCurrency} {order[`handlingFee${activeCurrency}`]}

              배송비:

                {activeCurrency} {order[`shippingCost${activeCurrency}`]}

            { /*

              AliExpress Coupon:
              Please enter Coupon code

            */ }

              Grand total :

                {activeCurrency} {order[`total${activeCurrency}`]}

          <form method="post" acceptCharset="euc-kr" ref="inipay">
            <input type="hidden" name="P_MID" ref="mid" />
            <input type="hidden" name="P_OID" ref="oid" />
            <input type="hidden" name="P_AMT" ref="amt" />
            <input type="hidden" name="P_MNAME" ref="mname" value="LINKSHOPS" />
            <input type="hidden" name="P_UNAME" ref="uname" />
            <input type="hidden" name="P_GOODS" ref="goods" value="의류" />
            <input type="hidden" name="P_MOBILE" ref="mobile" />
            <input type="hidden" name="P_EMAIL" ref="email" />
            <input type="hidden" name="P_NEXT_URL" ref="nextUrl" />
            <input type="hidden" name="P_NOTI_URL" ref="notiUrl" />
            <input type="hidden" name="P_CHARSET" value="utf8" />
            <input type="hidden" name="P_RESERVED" value="twotrs_isp=Y&block_isp=Y&twotrs_isp_noti=N" />
          </form>
          <input
            type="submit"
            id="create-order"
            value="place order "
            className="ui-button ui-button-main"
            onClick={this.toggle}
          />

            Payment Method

              신용카드

            { /* TODO

              핸드폰 결제

              계좌이체

              무통장입금

 */ }

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


<commit_before>const { ipcRenderer } = require('electron');

/**
 * Find a download by its uid from the list of downloads.
 * Returns null if not found.
 *
 * @param  {String} uid
 * @param  {Array} downloads
 * @return {Object|null}
 */
function findDownload(uid, downloads) {
	for (let i = 0; i < downloads.length; i++) {
		if (uid == downloads[i].uid) {
			return {
				download: downloads[i],
				index: i
			};
		}
	}

	return null;
}

/**
 * Handle the user's request to cancel a download
 *
 * @param  {Object} download
 * @param  {DownloadQueue} downloadQueue
 */
function handleCancel(download, downloadQueue) {
	if (download.status !== 'Downloading') {
		return;
	}

	downloadQueue.cancelDownload(download);
}

/**
 * Handle a user's action on a download
 *
 * @param  {Object} data          An object with details about the download to act on
 * @param  {Array} downloads      The list of all downloads
 * @param  {Array} downloadQueue  The download queue
 */
function handle(data, downloads, downloadQueue) {
	const result = findDownload(data.downloadId, downloads);

	if (!result) {
		return;
	}

	switch (data.selectedOptionId) {
		case 'open':
			ipcRenderer.send('open-item', result.download.filepath);
			break;
		case 'open-in-browser':
			ipcRenderer.send('open-external', result.download.url);
			break;
		case 'show-in-folder':
			ipcRenderer.send('show-item-in-folder', result.download.filepath);
			break;
		case 'remove-from-list':
			downloads.splice(result.index, 1);
			break;
		case 'cancel':
			handleCancel(result.download, downloadQueue);
			break;
	}
}

module.exports = {
	handle
};
<commit_msg>Also remove the item from the queue when removing from list<commit_after>const { ipcRenderer } = require('electron');

/**
 * Find a download by its uid from the list of downloads.
 * Returns null if not found.
 *
 * @param  {String} uid
 * @param  {Array} downloads
 * @return {Object|null}
 */
function findDownload(uid, downloads) {
	for (let i = 0; i < downloads.length; i++) {
		if (uid == downloads[i].uid) {
			return {
				download: downloads[i],
				index: i
			};
		}
	}

	return null;
}

/**
 * Handle the user's request to cancel a download
 *
 * @param  {Object} download
 * @param  {DownloadQueue} downloadQueue
 */
function handleCancel(download, downloadQueue) {
	if (download.status !== 'Downloading') {
		return;
	}

	downloadQueue.cancelDownload(download);
}

/**
 * Handle a user's action on a download
 *
 * @param  {Object} data          An object with details about the download to act on
 * @param  {Array} downloads      The list of all downloads
 * @param  {Array} downloadQueue  The download queue
 */
function handle(data, downloads, downloadQueue) {
	const item = findDownload(data.downloadId, downloads);
	const itemOnQueue = findDownload(data.downloadId, downloadQueue.items);

	if (!item) {
		return;
	}

	switch (data.selectedOptionId) {
		case 'open':
			ipcRenderer.send('open-item', item.download.filepath);
			break;
		case 'open-in-browser':
			ipcRenderer.send('open-external', item.download.url);
			break;
		case 'show-in-folder':
			ipcRenderer.send('show-item-in-folder', item.download.filepath);
			break;
		case 'remove-from-list':
			downloads.splice(item.index, 1);

			if (itemOnQueue) {
				downloadQueue.items.splice(itemOnQueue.index, 1);
			}

			break;
		case 'cancel':
			handleCancel(item.download, downloadQueue);
			break;
	}
}

module.exports = {
	handle
};
<|endoftext|>


<commit_before>/*
    ***** BEGIN LICENSE BLOCK *****

    Copyright © 2011 Center for History and New Media
                     George Mason University, Fairfax, Virginia, USA
                     http://zotero.org

    This file is part of Zotero.

    Zotero is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    Zotero is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with Zotero.  If not, see <http://www.gnu.org/licenses/>.

    ***** END LICENSE BLOCK *****
*/

Zotero.Server = new function() {
	var _onlineObserverRegistered;
	var responseCodes = {
		200:"OK",
		201:"Created",
		300:"Multiple Choices",
		400:"Bad Request",
		404:"Not Found",
		412:"Precondition Failed",
		500:"Internal Server Error",
		501:"Method Not Implemented"
	};

	/**
	 * initializes a very rudimentary web server
	 */
	this.init = function(port, bindAllAddr, maxConcurrentConnections) {
		if (Zotero.HTTP.browserIsOffline()) {
			Zotero.debug('Browser is offline -- not initializing HTTP server');
			_registerOnlineObserver();
			return;
		}

		// start listening on socket
		var serv = Components.classes["@mozilla.org/network/server-socket;1"]
					.createInstance(Components.interfaces.nsIServerSocket);
		try {
			// bind to a random port on loopback only
			serv.init(port ? port : Zotero.Prefs.get('httpServer.port'), !bindAllAddr, -1);
			serv.asyncListen(Zotero.Server.SocketListener);

			Zotero.debug("HTTP server listening on "+(bindAllAddr ? "*": " 127.0.0.1")+":"+serv.port);
		} catch(e) {
			Zotero.debug("Not initializing HTTP server");
		}

		_registerOnlineObserver()
	}

	/**
	 * generates the response to an HTTP request
	 */
	this.generateResponse = function (status, contentType, body) {
		var response = "HTTP/1.0 "+status+" "+responseCodes[status]+"\r\n";
		response += "Access-Control-Allow-Origin: org.zotero.zoteroconnectorforsafari-69x6c999f9\r\n";
		response += "Access-Control-Allow-Methods: POST, GET, OPTIONS, HEAD\r\n";

		if(body) {
			if(contentType) {
				response += "Content-Type: "+contentType+"\r\n";
			}
			response += "\r\n"+body;
		} else {
			response += "Content-Length: 0\r\n\r\n";
		}

		return response;
	}

	/**
	 * Parses a query string into a key => value object
	 * @param {String} queryString Query string
	 */
	this.decodeQueryString = function(queryString) {
		var splitData = queryString.split("&");
		var decodedData = {};
		for each(var variable in splitData) {
			var splitIndex = variable.indexOf("=");
			decodedData[decodeURIComponent(variable.substr(0, splitIndex))] = decodeURIComponent(variable.substr(splitIndex+1));
		}
		return decodedData;
	}

	function _registerOnlineObserver() {
		if (_onlineObserverRegistered) {
			return;
		}

		// Observer to enable the integration when we go online
		var observer = {
			observe: function(subject, topic, data) {
				if (data == 'online') {
					Zotero.Server.init();
				}
			}
		};

		var observerService =
			Components.classes["@mozilla.org/observer-service;1"]
				.getService(Components.interfaces.nsIObserverService);
		observerService.addObserver(observer, "network:offline-status-changed", false);

		_onlineObserverRegistered = true;
	}
}

Zotero.Server.SocketListener = new function() {
	this.onSocketAccepted = onSocketAccepted;
	this.onStopListening = onStopListening;

	/*
	 * called when a socket is opened
	 */
	function onSocketAccepted(socket, transport) {
		// get an input stream
		var iStream = transport.openInputStream(0, 0, 0);
		var oStream = transport.openOutputStream(Components.interfaces.nsITransport.OPEN_BLOCKING, 0, 0);

		var dataListener = new Zotero.Server.DataListener(iStream, oStream);
		var pump = Components.classes["@mozilla.org/network/input-stream-pump;1"]
							 .createInstance(Components.interfaces.nsIInputStreamPump);
		pump.init(iStream, -1, -1, 0, 0, false);
		pump.asyncRead(dataListener, null);
	}

	function onStopListening(serverSocket, status) {
		Zotero.debug("HTTP server going offline");
	}
}

/*
 * handles the actual acquisition of data
 */
Zotero.Server.DataListener = function(iStream, oStream) {
	this.header = "";
	this.headerFinished = false;

	this.body = "";
	this.bodyLength = 0;

	this.iStream = iStream;
	this.oStream = oStream;
	this.sStream = Components.classes["@mozilla.org/scriptableinputstream;1"]
	                         .createInstance(Components.interfaces.nsIScriptableInputStream);
	this.sStream.init(iStream);

	this.foundReturn = false;
}

/*
 * called when a request begins (although the request should have begun before
 * the DataListener was generated)
 */
Zotero.Server.DataListener.prototype.onStartRequest = function(request, context) {}

/*
 * called when a request stops
 */
Zotero.Server.DataListener.prototype.onStopRequest = function(request, context, status) {
	this.iStream.close();
	this.oStream.close();
}

/*
 * called when new data is available
 */
Zotero.Server.DataListener.prototype.onDataAvailable = function(request, context,
                                                             inputStream, offset, count) {
	var readData = this.sStream.read(count);

	if(this.headerFinished) {	// reading body
		this.body += readData;
		// check to see if data is done
		this._bodyData();
	} else {					// reading header
		// see if there's a magic double return
		var lineBreakIndex = readData.indexOf("\r\n\r\n");
		if(lineBreakIndex != -1) {
			if(lineBreakIndex != 0) {
				this.header += readData.substr(0, lineBreakIndex+4);
				this.body = readData.substr(lineBreakIndex+4);
			}

			this._headerFinished();
			return;
		}
		var lineBreakIndex = readData.indexOf("\n\n");
		if(lineBreakIndex != -1) {
			if(lineBreakIndex != 0) {
				this.header += readData.substr(0, lineBreakIndex+2);
				this.body = readData.substr(lineBreakIndex+2);
			}

			this._headerFinished();
			return;
		}
		if(this.header && this.header[this.header.length-1] == "\n" &&
		   (readData[0] == "\n" || readData[0] == "\r")) {
			if(readData.length > 1 && readData[1] == "\n") {
				this.header += readData.substr(0, 2);
				this.body = readData.substr(2);
			} else {
				this.header += readData[0];
				this.body = readData.substr(1);
			}

			this._headerFinished();
			return;
		}
		this.header += readData;
	}
}

/*
 * processes an HTTP header and decides what to do
 */
Zotero.Server.DataListener.prototype._headerFinished = function() {
	this.headerFinished = true;

	Zotero.debug(this.header, 5);

	const methodRe = /^([A-Z]+) ([^ \r\n?]+)(\?[^ \r\n]+)?/;
	const contentTypeRe = /[\r\n]Content-Type: +([^ \r\n]+)/i;

	// get first line of request
	var method = methodRe.exec(this.header);
	// get content-type
	var contentType = contentTypeRe.exec(this.header);
	if(contentType) {
		var splitContentType = contentType[1].split(/\s*;/);
		this.contentType = splitContentType[0];
	}

	if(!method) {
		this._requestFinished(Zotero.Server.generateResponse(400, "text/plain", "Invalid method specified\n"));
		return;
	}
	if(!Zotero.Server.Endpoints[method[2]]) {
		this._requestFinished(Zotero.Server.generateResponse(404, "text/plain", "No endpoint found\n"));
		return;
	}
	this.pathname = method[2];
	this.endpoint = Zotero.Server.Endpoints[method[2]];
	this.query = method[3];

	if(method[1] == "HEAD" || method[1] == "OPTIONS") {
		this._requestFinished(Zotero.Server.generateResponse(200));
	} else if(method[1] == "GET") {
		this._processEndpoint("GET", null);
	} else if(method[1] == "POST") {
		const contentLengthRe = /[\r\n]Content-Length: +([0-9]+)/i;

		// parse content length
		var m = contentLengthRe.exec(this.header);
		if(!m) {
			this._requestFinished(Zotero.Server.generateResponse(400, "text/plain", "Content-length not provided\n"));
			return;
		}

		this.bodyLength = parseInt(m[1]);
		this._bodyData();
	} else {
		this._requestFinished(Zotero.Server.generateResponse(501, "text/plain", "Method not implemented\n"));
		return;
	}
}

/*
 * checks to see if Content-Length bytes of body have been read and, if so, processes the body
 */
Zotero.Server.DataListener.prototype._bodyData = function() {
	if(this.body.length >= this.bodyLength) {
		// convert to UTF-8
		var dataStream = Components.classes["@mozilla.org/io/string-input-stream;1"]
		                           .createInstance(Components.interfaces.nsIStringInputStream);
		dataStream.setData(this.body, this.bodyLength);

		var utf8Stream = Components.classes["@mozilla.org/intl/converter-input-stream;1"]
		                           .createInstance(Components.interfaces.nsIConverterInputStream);
		utf8Stream.init(dataStream, "UTF-8", 4096, "?");

		this.body = "";
		var string = {};
		while(utf8Stream.readString(this.bodyLength, string)) {
			this.body += string.value;
		}

		// handle envelope
		this._processEndpoint("POST", this.body);
	}
}

/**
 * Generates a response based on calling the function associated with the endpoint
 */
Zotero.Server.DataListener.prototype._processEndpoint = function(method, postData) {
	try {
		var endpoint = new this.endpoint;

		// check that endpoint supports method
		if(endpoint.supportedMethods && endpoint.supportedMethods.indexOf(method) === -1) {
			this._requestFinished(Zotero.Server.generateResponse(400, "text/plain", "Endpoint does not support method\n"));
			return;
		}

		var decodedData = null;
		if(postData && this.contentType) {
			// check that endpoint supports contentType
			var supportedDataTypes = endpoint.supportedDataTypes;
			if(supportedDataTypes && supportedDataTypes.indexOf(this.contentType) === -1) {
				this._requestFinished(Zotero.Server.generateResponse(400, "text/plain", "Endpoint does not support content-type\n"));
				return;
			}

			// decode JSON or urlencoded post data, and pass through anything else
			if(supportedDataTypes && this.contentType === "application/json") {
				try {
					decodedData = JSON.parse(postData);
				} catch(e) {
					this._requestFinished(Zotero.Server.generateResponse(400, "text/plain", "Invalid JSON provided\n"));
					return;
				}
			} else if(supportedDataTypes && this.contentType === "application/x-www-urlencoded") {
				decodedData = Zotero.Server.decodeQueryString(postData);
			} else {
				decodedData = postData;
			}
		}

		// set up response callback
		var me = this;
		var sendResponseCallback = function(code, contentType, arg) {
			me._requestFinished(Zotero.Server.generateResponse(code, contentType, arg));
		}

		// pass to endpoint
		if((endpoint.init.length ? endpoint.init.length : endpoint.init.arity) === 3) {
			var url = {
				"pathname":this.pathname,
				"query":this.query ? Zotero.Server.decodeQueryString(this.query.substr(1)) : {}
			};

			endpoint.init(url, decodedData, sendResponseCallback);
		} else {
			endpoint.init(decodedData, sendResponseCallback);
		}
	} catch(e) {
		Zotero.debug(e);
		this._requestFinished(Zotero.Server.generateResponse(500), "text/plain", "An error occurred\n");
		throw e;
	}
}

/*
 * returns HTTP data from a request
 */
Zotero.Server.DataListener.prototype._requestFinished = function(response) {
	// close input stream
	this.iStream.close();

	// open UTF-8 converter for output stream
	var intlStream = Components.classes["@mozilla.org/intl/converter-output-stream;1"]
							   .createInstance(Components.interfaces.nsIConverterOutputStream);

	// write
	try {
		intlStream.init(this.oStream, "UTF-8", 1024, "?".charCodeAt(0));

		// write response
		Zotero.debug(response, 5);
		intlStream.writeString(response);
	} finally {
		intlStream.close();
	}
}

/**
 * Endpoints for the HTTP server
 *
 * Each endpoint should take the form of an object. The init() method of this object will be passed:
 *     method - the method of the request ("GET" or "POST")
 *     data - the query string (for a "GET" request) or POST data (for a "POST" request)
 *     sendResponseCallback - a function to send a response to the HTTP request. This can be passed
 *                            a response code alone (e.g., sendResponseCallback(404)) or a response
 *                            code, MIME type, and response body
 *                            (e.g., sendResponseCallback(200, "text/plain", "Hello World!"))
 *
 * See connector/server_connector.js for examples
 */
Zotero.Server.Endpoints = {}<commit_msg>Don't add Access-Control-Allow-Origin headers to Safari<commit_after>/*
    ***** BEGIN LICENSE BLOCK *****

    Copyright © 2011 Center for History and New Media
                     George Mason University, Fairfax, Virginia, USA
                     http://zotero.org

    This file is part of Zotero.

    Zotero is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    Zotero is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with Zotero.  If not, see <http://www.gnu.org/licenses/>.

    ***** END LICENSE BLOCK *****
*/

Zotero.Server = new function() {
	var _onlineObserverRegistered;
	var responseCodes = {
		200:"OK",
		201:"Created",
		300:"Multiple Choices",
		400:"Bad Request",
		404:"Not Found",
		412:"Precondition Failed",
		500:"Internal Server Error",
		501:"Method Not Implemented"
	};

	/**
	 * initializes a very rudimentary web server
	 */
	this.init = function(port, bindAllAddr, maxConcurrentConnections) {
		if (Zotero.HTTP.browserIsOffline()) {
			Zotero.debug('Browser is offline -- not initializing HTTP server');
			_registerOnlineObserver();
			return;
		}

		// start listening on socket
		var serv = Components.classes["@mozilla.org/network/server-socket;1"]
					.createInstance(Components.interfaces.nsIServerSocket);
		try {
			// bind to a random port on loopback only
			serv.init(port ? port : Zotero.Prefs.get('httpServer.port'), !bindAllAddr, -1);
			serv.asyncListen(Zotero.Server.SocketListener);

			Zotero.debug("HTTP server listening on "+(bindAllAddr ? "*": " 127.0.0.1")+":"+serv.port);
		} catch(e) {
			Zotero.debug("Not initializing HTTP server");
		}

		_registerOnlineObserver()
	}

	/**
	 * generates the response to an HTTP request
	 */
	this.generateResponse = function (status, contentType, body) {
		var response = "HTTP/1.0 "+status+" "+responseCodes[status]+"\r\n";

		if(body) {
			if(contentType) {
				response += "Content-Type: "+contentType+"\r\n";
			}
			response += "\r\n"+body;
		} else {
			response += "Content-Length: 0\r\n\r\n";
		}

		return response;
	}

	/**
	 * Parses a query string into a key => value object
	 * @param {String} queryString Query string
	 */
	this.decodeQueryString = function(queryString) {
		var splitData = queryString.split("&");
		var decodedData = {};
		for each(var variable in splitData) {
			var splitIndex = variable.indexOf("=");
			decodedData[decodeURIComponent(variable.substr(0, splitIndex))] = decodeURIComponent(variable.substr(splitIndex+1));
		}
		return decodedData;
	}

	function _registerOnlineObserver() {
		if (_onlineObserverRegistered) {
			return;
		}

		// Observer to enable the integration when we go online
		var observer = {
			observe: function(subject, topic, data) {
				if (data == 'online') {
					Zotero.Server.init();
				}
			}
		};

		var observerService =
			Components.classes["@mozilla.org/observer-service;1"]
				.getService(Components.interfaces.nsIObserverService);
		observerService.addObserver(observer, "network:offline-status-changed", false);

		_onlineObserverRegistered = true;
	}
}

Zotero.Server.SocketListener = new function() {
	this.onSocketAccepted = onSocketAccepted;
	this.onStopListening = onStopListening;

	/*
	 * called when a socket is opened
	 */
	function onSocketAccepted(socket, transport) {
		// get an input stream
		var iStream = transport.openInputStream(0, 0, 0);
		var oStream = transport.openOutputStream(Components.interfaces.nsITransport.OPEN_BLOCKING, 0, 0);

		var dataListener = new Zotero.Server.DataListener(iStream, oStream);
		var pump = Components.classes["@mozilla.org/network/input-stream-pump;1"]
							 .createInstance(Components.interfaces.nsIInputStreamPump);
		pump.init(iStream, -1, -1, 0, 0, false);
		pump.asyncRead(dataListener, null);
	}

	function onStopListening(serverSocket, status) {
		Zotero.debug("HTTP server going offline");
	}
}

/*
 * handles the actual acquisition of data
 */
Zotero.Server.DataListener = function(iStream, oStream) {
	this.header = "";
	this.headerFinished = false;

	this.body = "";
	this.bodyLength = 0;

	this.iStream = iStream;
	this.oStream = oStream;
	this.sStream = Components.classes["@mozilla.org/scriptableinputstream;1"]
	                         .createInstance(Components.interfaces.nsIScriptableInputStream);
	this.sStream.init(iStream);

	this.foundReturn = false;
}

/*
 * called when a request begins (although the request should have begun before
 * the DataListener was generated)
 */
Zotero.Server.DataListener.prototype.onStartRequest = function(request, context) {}

/*
 * called when a request stops
 */
Zotero.Server.DataListener.prototype.onStopRequest = function(request, context, status) {
	this.iStream.close();
	this.oStream.close();
}

/*
 * called when new data is available
 */
Zotero.Server.DataListener.prototype.onDataAvailable = function(request, context,
                                                             inputStream, offset, count) {
	var readData = this.sStream.read(count);

	if(this.headerFinished) {	// reading body
		this.body += readData;
		// check to see if data is done
		this._bodyData();
	} else {					// reading header
		// see if there's a magic double return
		var lineBreakIndex = readData.indexOf("\r\n\r\n");
		if(lineBreakIndex != -1) {
			if(lineBreakIndex != 0) {
				this.header += readData.substr(0, lineBreakIndex+4);
				this.body = readData.substr(lineBreakIndex+4);
			}

			this._headerFinished();
			return;
		}
		var lineBreakIndex = readData.indexOf("\n\n");
		if(lineBreakIndex != -1) {
			if(lineBreakIndex != 0) {
				this.header += readData.substr(0, lineBreakIndex+2);
				this.body = readData.substr(lineBreakIndex+2);
			}

			this._headerFinished();
			return;
		}
		if(this.header && this.header[this.header.length-1] == "\n" &&
		   (readData[0] == "\n" || readData[0] == "\r")) {
			if(readData.length > 1 && readData[1] == "\n") {
				this.header += readData.substr(0, 2);
				this.body = readData.substr(2);
			} else {
				this.header += readData[0];
				this.body = readData.substr(1);
			}

			this._headerFinished();
			return;
		}
		this.header += readData;
	}
}

/*
 * processes an HTTP header and decides what to do
 */
Zotero.Server.DataListener.prototype._headerFinished = function() {
	this.headerFinished = true;

	Zotero.debug(this.header, 5);

	const methodRe = /^([A-Z]+) ([^ \r\n?]+)(\?[^ \r\n]+)?/;
	const contentTypeRe = /[\r\n]Content-Type: +([^ \r\n]+)/i;

	// get first line of request
	var method = methodRe.exec(this.header);
	// get content-type
	var contentType = contentTypeRe.exec(this.header);
	if(contentType) {
		var splitContentType = contentType[1].split(/\s*;/);
		this.contentType = splitContentType[0];
	}

	if(!method) {
		this._requestFinished(Zotero.Server.generateResponse(400, "text/plain", "Invalid method specified\n"));
		return;
	}
	if(!Zotero.Server.Endpoints[method[2]]) {
		this._requestFinished(Zotero.Server.generateResponse(404, "text/plain", "No endpoint found\n"));
		return;
	}
	this.pathname = method[2];
	this.endpoint = Zotero.Server.Endpoints[method[2]];
	this.query = method[3];

	if(method[1] == "HEAD" || method[1] == "OPTIONS") {
		this._requestFinished(Zotero.Server.generateResponse(200));
	} else if(method[1] == "GET") {
		this._processEndpoint("GET", null);
	} else if(method[1] == "POST") {
		const contentLengthRe = /[\r\n]Content-Length: +([0-9]+)/i;

		// parse content length
		var m = contentLengthRe.exec(this.header);
		if(!m) {
			this._requestFinished(Zotero.Server.generateResponse(400, "text/plain", "Content-length not provided\n"));
			return;
		}

		this.bodyLength = parseInt(m[1]);
		this._bodyData();
	} else {
		this._requestFinished(Zotero.Server.generateResponse(501, "text/plain", "Method not implemented\n"));
		return;
	}
}

/*
 * checks to see if Content-Length bytes of body have been read and, if so, processes the body
 */
Zotero.Server.DataListener.prototype._bodyData = function() {
	if(this.body.length >= this.bodyLength) {
		// convert to UTF-8
		var dataStream = Components.classes["@mozilla.org/io/string-input-stream;1"]
		                           .createInstance(Components.interfaces.nsIStringInputStream);
		dataStream.setData(this.body, this.bodyLength);

		var utf8Stream = Components.classes["@mozilla.org/intl/converter-input-stream;1"]
		                           .createInstance(Components.interfaces.nsIConverterInputStream);
		utf8Stream.init(dataStream, "UTF-8", 4096, "?");

		this.body = "";
		var string = {};
		while(utf8Stream.readString(this.bodyLength, string)) {
			this.body += string.value;
		}

		// handle envelope
		this._processEndpoint("POST", this.body);
	}
}

/**
 * Generates a response based on calling the function associated with the endpoint
 */
Zotero.Server.DataListener.prototype._processEndpoint = function(method, postData) {
	try {
		var endpoint = new this.endpoint;

		// check that endpoint supports method
		if(endpoint.supportedMethods && endpoint.supportedMethods.indexOf(method) === -1) {
			this._requestFinished(Zotero.Server.generateResponse(400, "text/plain", "Endpoint does not support method\n"));
			return;
		}

		var decodedData = null;
		if(postData && this.contentType) {
			// check that endpoint supports contentType
			var supportedDataTypes = endpoint.supportedDataTypes;
			if(supportedDataTypes && supportedDataTypes.indexOf(this.contentType) === -1) {
				this._requestFinished(Zotero.Server.generateResponse(400, "text/plain", "Endpoint does not support content-type\n"));
				return;
			}

			// decode JSON or urlencoded post data, and pass through anything else
			if(supportedDataTypes && this.contentType === "application/json") {
				try {
					decodedData = JSON.parse(postData);
				} catch(e) {
					this._requestFinished(Zotero.Server.generateResponse(400, "text/plain", "Invalid JSON provided\n"));
					return;
				}
			} else if(supportedDataTypes && this.contentType === "application/x-www-urlencoded") {
				decodedData = Zotero.Server.decodeQueryString(postData);
			} else {
				decodedData = postData;
			}
		}

		// set up response callback
		var me = this;
		var sendResponseCallback = function(code, contentType, arg) {
			me._requestFinished(Zotero.Server.generateResponse(code, contentType, arg));
		}

		// pass to endpoint
		if((endpoint.init.length ? endpoint.init.length : endpoint.init.arity) === 3) {
			var url = {
				"pathname":this.pathname,
				"query":this.query ? Zotero.Server.decodeQueryString(this.query.substr(1)) : {}
			};

			endpoint.init(url, decodedData, sendResponseCallback);
		} else {
			endpoint.init(decodedData, sendResponseCallback);
		}
	} catch(e) {
		Zotero.debug(e);
		this._requestFinished(Zotero.Server.generateResponse(500), "text/plain", "An error occurred\n");
		throw e;
	}
}

/*
 * returns HTTP data from a request
 */
Zotero.Server.DataListener.prototype._requestFinished = function(response) {
	// close input stream
	this.iStream.close();

	// open UTF-8 converter for output stream
	var intlStream = Components.classes["@mozilla.org/intl/converter-output-stream;1"]
							   .createInstance(Components.interfaces.nsIConverterOutputStream);

	// write
	try {
		intlStream.init(this.oStream, "UTF-8", 1024, "?".charCodeAt(0));

		// write response
		Zotero.debug(response, 5);
		intlStream.writeString(response);
	} finally {
		intlStream.close();
	}
}

/**
 * Endpoints for the HTTP server
 *
 * Each endpoint should take the form of an object. The init() method of this object will be passed:
 *     method - the method of the request ("GET" or "POST")
 *     data - the query string (for a "GET" request) or POST data (for a "POST" request)
 *     sendResponseCallback - a function to send a response to the HTTP request. This can be passed
 *                            a response code alone (e.g., sendResponseCallback(404)) or a response
 *                            code, MIME type, and response body
 *                            (e.g., sendResponseCallback(200, "text/plain", "Hello World!"))
 *
 * See connector/server_connector.js for examples
 */
Zotero.Server.Endpoints = {}<|endoftext|>


<commit_before>// Generic forms user page (Part A)

'use strict';

var generateSections = require('eregistrations/view/components/generate-form-sections')
, _                 = require('mano').i18n.bind('Registration');

exports._parent = require('./business-process-data-forms-tabbed');

exports['forms-sections-content'] = function () {
	insert(
		_if(this.section._legend, div({ class: 'info-main' },
			md(this.section._legend))),
		this.section.applicableSections ?
				generateSections(this.section.applicableSections, { viewContext: this }) :
				this.section.toDOMForm(document),
		p({ class: 'user-next-step-button' },
			a({ href: this.section._nextSection.map(function (nextSection) {
				if (!nextSection) return '/documents/';
				return '/forms/' + nextSection.pageUrl + '/';
			})
				}, _('Continue to next step')))
	);

};

exports._match = 'section';
<commit_msg>#1260 Add progress rules display to tabs<commit_after>// Generic forms user page (Part A)

'use strict';

var generateSections = require('eregistrations/view/components/generate-form-sections')
  , progressRules    = require('./components/progress-rules')
  , _                = require('mano').i18n.bind('Registration');

exports._parent = require('./business-process-data-forms-tabbed');

exports['forms-sections-content'] = function () {
	insert(
		_if(this.section._legend, div({ class: 'info-main' },
			md(this.section._legend))),
		this.section.applicableSections ?
				[progressRules(this.section),
					generateSections(this.section.applicableSections, { viewContext: this })] :
				this.section.toDOMForm(document),
		p({ class: 'user-next-step-button' },
			a({ href: this.section._nextSection.map(function (nextSection) {
				if (!nextSection) return '/documents/';
				return '/forms/' + nextSection.pageUrl + '/';
			})
				}, _('Continue to next step')))
	);

};

exports._match = 'section';
<|endoftext|>


<commit_before>var myApp = angular.module('YouTubeApp', ['auth']);

myApp.constant('YT_event', {
  STOP: 0,
  PLAY: 1,
  PAUSE: 2,
  STATUS_CHANGE: 3
});

myApp.controller('YouTubeCtrl', function ($scope, $rootScope, YT_event, authSrv, socket) {
  //initial settings
  $scope.yt = {
    width: 600,
    height: 480,
    //videoid: "KRaWnd3LJfs", default video for testing purpose.
    playerStatus: "NOT PLAYING"
  };

  $scope.YT_event = YT_event;

  $scope.sendControlEvent = function (ctrlEvent) {
    this.$broadcast(ctrlEvent);
  }

  $scope.$on(YT_event.STATUS_CHANGE, function (event, data) {
    $scope.yt.playerStatus = data;
  });

  authSrv.getCurrentUser(function(currentUser){
    // Changed polling rate to 500, since we're not expecting much load for the MVP
    if (currentUser) {
      setInterval(function () {
        if ($scope.isPlayerReady) {
          socket.emit('broadcast_player_status', {
            radioId: currentUser.username,
            videoId: $rootScope.player.getVideoUrl().match(/[?&]v=([^&]+)/)[1],
            videoUrl: $rootScope.player.getVideoUrl(),
            currentTime: $rootScope.player.getCurrentTime(),
            playerState: $rootScope.player.getPlayerState()
          });
        }
      }, 500);
    }
  });

  socket.on('update_player_status', function (data) {
    if ($rootScope.player) {
      if ($rootScope.player.getVideoUrl() !== data.videoUrl) {
        if (data.playerState === YT_event.PLAY) {
          $rootScope.player.loadVideoById(data.videoId, data.currentTime);
        } else {
          $rootScope.player.cueVideoById(data.videoId, data.currentTime);
        }
      } else {
        if (Math.abs(data.currentTime - $rootScope.player.getCurrentTime()) > 10) {
          $rootScope.player.seekTo(data.currentTime);
        }
        if (data.playerState === YT_event.PLAY) {
          $rootScope.player.playVideo();
        }
        if (data.playerState === YT_event.PAUSE) {
          $rootScope.player.pauseVideo();
        }
      }
    }
  });

  socket.on('update_broadcaster_status', function (data){
    if ($rootScope.player){
      if (!data.isBroadcasterConnected){
        $rootScope.player.loadVideoById(null); // hacky way to stop the radio.
      } else {
        $rootScope.player.playVideo();
      }
    }
  });

});

myApp.directive('youtube', function ($window, YT_event, $rootScope, $http) {
  return {
    restrict: "E",

    template: '

',

    link: function (scope, element, attrs) {
      var tag = document.createElement('script');
      tag.src = "https://www.youtube.com/iframe_api";
      var firstScriptTag = document.getElementsByTagName('script')[0];
      firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
      scope.isPlayerReady = false;
      $window.onYouTubeIframeAPIReady = function () {
        var autoplay = 1;
        if (!$rootScope.isBroadcasterOnline){
          autoplay = 0;
        }
        $rootScope.player = new YT.Player(element.children()[0], {
          playerVars: {
            autoplay: autoplay,
            html5: 1,
            theme: "light",
            modesbranding: 0,
            color: "white",
            iv_load_policy: 3,
            showinfo: 1,
            controls: 1
          },

          height: scope.yt.height,
          width: scope.yt.width,
          videoId: scope.yt.videoid,

          events: {
            'onStateChange': function (event) {

              var message = {
                event: YT_event.STATUS_CHANGE,
                data: ""
              };

              switch (event.data) {
                case YT.PlayerState.PLAYING:
                  message.data = "PLAYING";
                  break;
                case YT.PlayerState.ENDED:
                  message.data = "ENDED";
                  break;
                case YT.PlayerState.UNSTARTED:
                  message.data = "NOT PLAYING";
                  break;
                case YT.PlayerState.PAUSED:
                  message.data = "PAUSED";
                  break;
              }

              scope.$apply(function () {
                scope.$emit(message.event, message.data);
              });
            },

            'onReady': function (event) {
              scope.isPlayerReady = true;
            }
          }
        });
      };

      scope.$watch('yt.videoid', function (newValue, oldValue) {
        if (newValue == oldValue) {
          return;
        }

        $rootScope.player.cueVideoById(scope.yt.videoid);
      });

      scope.$on(YT_event.STOP, function () {
        $rootScope.player.seekTo(0);
        $rootScope.player.stopVideo();
      });

      scope.$on(YT_event.PLAY, function () {
        $rootScope.player.playVideo();
      });

      scope.$on(YT_event.PAUSE, function () {
        $rootScope.player.pauseVideo();
      });

      scope.$on(YT_event.STATUS_CHANGE, function (event, message) {
        if (message === "ENDED") {
          $http.get('recommendation-engine/next').success(function (response) {
            if (response && response.videoId) {
              $rootScope.player.loadVideoById(response.videoId);
            }
          })
        }
      })

    }
  };
});
<commit_msg>set default autoplay to 0<commit_after>var myApp = angular.module('YouTubeApp', ['auth']);

myApp.constant('YT_event', {
  STOP: 0,
  PLAY: 1,
  PAUSE: 2,
  STATUS_CHANGE: 3
});

myApp.controller('YouTubeCtrl', function ($scope, $rootScope, YT_event, authSrv, socket) {
  //initial settings
  $scope.yt = {
    width: 600,
    height: 480,
    //videoid: "KRaWnd3LJfs", default video for testing purpose.
    playerStatus: "NOT PLAYING"
  };

  $scope.YT_event = YT_event;

  $scope.sendControlEvent = function (ctrlEvent) {
    this.$broadcast(ctrlEvent);
  }

  $scope.$on(YT_event.STATUS_CHANGE, function (event, data) {
    $scope.yt.playerStatus = data;
  });

  authSrv.getCurrentUser(function(currentUser){
    // Changed polling rate to 500, since we're not expecting much load for the MVP
    if (currentUser) {
      setInterval(function () {
        if ($scope.isPlayerReady) {
          socket.emit('broadcast_player_status', {
            radioId: currentUser.username,
            videoId: $rootScope.player.getVideoUrl().match(/[?&]v=([^&]+)/)[1],
            videoUrl: $rootScope.player.getVideoUrl(),
            currentTime: $rootScope.player.getCurrentTime(),
            playerState: $rootScope.player.getPlayerState()
          });
        }
      }, 500);
    }
  });

  socket.on('update_player_status', function (data) {
    if ($rootScope.player) {
      if ($rootScope.player.getVideoUrl() !== data.videoUrl) {
        if (data.playerState === YT_event.PLAY) {
          $rootScope.player.loadVideoById(data.videoId, data.currentTime);
        } else {
          $rootScope.player.cueVideoById(data.videoId, data.currentTime);
        }
      } else {
        if (Math.abs(data.currentTime - $rootScope.player.getCurrentTime()) > 10) {
          $rootScope.player.seekTo(data.currentTime);
        }
        if (data.playerState === YT_event.PLAY) {
          $rootScope.player.playVideo();
        }
        if (data.playerState === YT_event.PAUSE) {
          $rootScope.player.pauseVideo();
        }
      }
    }
  });

  socket.on('update_broadcaster_status', function (data){
    if ($rootScope.player){
      if (!data.isBroadcasterConnected){
        $rootScope.player.loadVideoById(null); // hacky way to stop the radio.
      } else {
        $rootScope.player.playVideo();
      }
    }
  });

});

myApp.directive('youtube', function ($window, YT_event, $rootScope, $http) {
  return {
    restrict: "E",

    template: '

',

    link: function (scope, element, attrs) {
      var tag = document.createElement('script');
      tag.src = "https://www.youtube.com/iframe_api";
      var firstScriptTag = document.getElementsByTagName('script')[0];
      firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
      scope.isPlayerReady = false;
      $window.onYouTubeIframeAPIReady = function () {
        $rootScope.player = new YT.Player(element.children()[0], {
          playerVars: {
            autoplay: 0,
            html5: 1,
            theme: "light",
            modesbranding: 0,
            color: "white",
            iv_load_policy: 3,
            showinfo: 1,
            controls: 1
          },

          height: scope.yt.height,
          width: scope.yt.width,
          videoId: scope.yt.videoid,

          events: {
            'onStateChange': function (event) {

              var message = {
                event: YT_event.STATUS_CHANGE,
                data: ""
              };

              switch (event.data) {
                case YT.PlayerState.PLAYING:
                  message.data = "PLAYING";
                  break;
                case YT.PlayerState.ENDED:
                  message.data = "ENDED";
                  break;
                case YT.PlayerState.UNSTARTED:
                  message.data = "NOT PLAYING";
                  break;
                case YT.PlayerState.PAUSED:
                  message.data = "PAUSED";
                  break;
              }

              scope.$apply(function () {
                scope.$emit(message.event, message.data);
              });
            },

            'onReady': function (event) {
              scope.isPlayerReady = true;
            }
          }
        });
      };

      scope.$watch('yt.videoid', function (newValue, oldValue) {
        if (newValue == oldValue) {
          return;
        }

        $rootScope.player.cueVideoById(scope.yt.videoid);
      });

      scope.$on(YT_event.STOP, function () {
        $rootScope.player.seekTo(0);
        $rootScope.player.stopVideo();
      });

      scope.$on(YT_event.PLAY, function () {
        $rootScope.player.playVideo();
      });

      scope.$on(YT_event.PAUSE, function () {
        $rootScope.player.pauseVideo();
      });

      scope.$on(YT_event.STATUS_CHANGE, function (event, message) {
        if (message === "ENDED") {
          $http.get('recommendation-engine/next').success(function (response) {
            if (response && response.videoId) {
              $rootScope.player.loadVideoById(response.videoId);
            }
          })
        }
      })

    }
  };
});
<|endoftext|>


<commit_before>(function () {
  'use strict';

  angular.module('arkclient')
    .service('pluginLoader', ['accountService', PluginLoader]);

  /**
   * pluginLoader
   * @constructor
   */
  function PluginLoader(accountService) {

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

    const confFilename = "plugin.conf";
    const pluginFilename = "plugin.js";

    var pluginsDirectory = path.resolve(__dirname, "plugins");
    var plugins = getDirectories(pluginsDirectory);

    self.plugincontent = {};

    //load all plugin configurations
    plugins.forEach(function (element) {
      var conf = JSON.parse(readFile(path.join(pluginsDirectory, element, confFilename)));
      self.plugincontent[element] = conf;
    }, this);

    //on event
    self.triggerEvent = function (eventname) {
      //parse arguments and prepare for sending
      var sendargs = "";
      for (var arg = 1; arg < arguments.length; ++arg) {
        if (sendargs != "") {
          sendargs = sendargs + "," + JSON.stringify(arguments[arg]);
        } else {
          sendargs = JSON.stringify(arguments[arg]);
        }
      }
      //make callback function available for plugin
      function triggerEvent(eventname) {
        if (eventname == "getLocalAccount") {
          return accountService.getAccount(arguments[1]);
        }
      }
      //iterate over all plugins
      plugins.forEach(function (element) {
        //if plugin has event defined in configuration
        if (self.plugincontent[element]["events"][eventname]) {
          //read its javascript file
          var js = readFile(path.join(pluginsDirectory, element, pluginFilename));
          //run the js file and call the function with the prepared parameters
          eval(js + ";" + self.plugincontent[element]["events"][eventname] + "(" + sendargs + ")");
        }
      }, this);

    }

    //helper functions for os operations
    function getDirectories(dir) {
      if (!fs.statSync(dir).isDirectory()) return [];

      return fs.readdirSync(dir).filter(function (file) {
        var stat = fs.statSync(dir + '/' + file);
        return stat.isDirectory();
      });
    }

    function readFile(filename) {
      var data = fs.readFileSync(filename, 'utf8');
      return data;
    }
  }
})();<commit_msg>Fixed wrong path issue<commit_after>(function () {
  'use strict';

  angular.module('arkclient')
    .service('pluginLoader', ['accountService', PluginLoader]);

  /**
   * pluginLoader
   * @constructor
   */
  function PluginLoader(accountService) {

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

    const confFilename = "plugin.conf";
    const pluginFilename = "plugin.js";

    var pluginsDirectory = path.resolve(__dirname, "plugins");
    var plugins = getDirectories(pluginsDirectory);

    self.plugincontent = {};

    //load all plugin configurations
    plugins.forEach(function (element) {
      var conf = JSON.parse(readFile(path.join(pluginsDirectory, element, confFilename)));
      self.plugincontent[element] = conf;
    }, this);

    //on event
    self.triggerEvent = function (eventname) {
      //parse arguments and prepare for sending
      var sendargs = "";
      for (var arg = 1; arg < arguments.length; ++arg) {
        if (sendargs != "") {
          sendargs = sendargs + "," + JSON.stringify(arguments[arg]);
        } else {
          sendargs = JSON.stringify(arguments[arg]);
        }
      }
      //make callback function available for plugin
      function triggerEvent(eventname) {
        if (eventname == "getLocalAccount") {
          return accountService.getAccount(arguments[1]);
        }
      }
      //iterate over all plugins
      plugins.forEach(function (element) {
        //if plugin has event defined in configuration
        if (self.plugincontent[element]["events"][eventname]) {
          //read its javascript file
          var js = readFile(path.join(pluginsDirectory, element, pluginFilename));
          //run the js file and call the function with the prepared parameters
          eval(js + ";" + self.plugincontent[element]["events"][eventname] + "(" + sendargs + ")");
        }
      }, this);

    }

    //helper functions for os operations
    function getDirectories(dir) {
      try {
        return fs.readdirSync(dir).filter(function (file) {
          var stat = fs.statSync(dir + '/' + file);
          return stat.isDirectory();
        });
      } catch (e) {
        return [];
      }
    }

    function readFile(filename) {
      var data = fs.readFileSync(filename, 'utf8');
      return data;
    }
  }
})();<|endoftext|>


<commit_before>import React, { PureComponent, PropTypes } from 'react'
import { Link } from 'react-router'
import { Menu, Button, Header, Icon } from 'semantic-ui-react'

class Navbar extends PureComponent {
  static propTypes = {
    currentUser: PropTypes.object,
    signedIn: PropTypes.bool.isRequired,
    signOut: PropTypes.func.isRequired
  }

  render() {
    const { signedIn, signOut } = this.props
    return(
      <Menu stackable borderless>
        <Menu.Item as={Link} to='/'>SlimPoll</Menu.Item>
        { signedIn &&
          <Menu.Item>
            <Button basic animated='vertical' color='orange' as={Link} to='/create-poll'>
              <Button.Content visible>Create a poll</Button.Content>
              <Button.Content hidden>Easy as 1. 2. 3.</Button.Content>
            </Button>
          </Menu.Item>
        }
        <Menu.Menu position='right'>
          { signedIn &&
            <Menu.Item onClick={signOut}>Sign out</Menu.Item>
          }
          { !signedIn &&
            <Menu.Item as={Link} to='sign-in'>Sign in</Menu.Item>
          }
        </Menu.Menu>
      </Menu>
    )
  }
}

export default Navbar
<commit_msg>Add dropdown menu to navbar<commit_after>import React, { PureComponent, PropTypes } from 'react'
import { Link } from 'react-router'
import { Menu, Button, Header, Dropdown } from 'semantic-ui-react'

class Navbar extends PureComponent {
  static propTypes = {
    currentUser: PropTypes.object,
    signedIn: PropTypes.bool.isRequired,
    signOut: PropTypes.func.isRequired
  }

  render() {
    const { signedIn, signOut, currentUser } = this.props
    return(
      <Menu stackable borderless>
        <Menu.Item as={Link} to='/'>SlimPoll</Menu.Item>
        { signedIn &&
          <Menu.Item>
            <Button basic animated='vertical' color='orange' as={Link} to='/create-poll'>
              <Button.Content visible>Create a poll</Button.Content>
              <Button.Content hidden>Easy as 1. 2. 3.</Button.Content>
            </Button>
          </Menu.Item>
        }
        <Menu.Menu position='right'>
          { signedIn &&
            <Dropdown item closeOnBlur text={ currentUser.name }>
              <Dropdown.Menu>
                <Dropdown.Item as={Link} to='/create-poll'>Create a poll</Dropdown.Item>
                <Dropdown.Item>Your polls</Dropdown.Item>
                <Dropdown.Item>Polls you voted on</Dropdown.Item>
                <Dropdown.Divider />
                <Dropdown.Item onClick={signOut}>Sign out</Dropdown.Item>
              </Dropdown.Menu>
            </Dropdown>
          }
          { !signedIn &&
            <Menu.Item as={Link} to='sign-in'>Sign in</Menu.Item>
          }
        </Menu.Menu>
      </Menu>
    )
  }
}

export default Navbar
<|endoftext|>


<commit_before>var meshCommon = function() {
	var meshCommon = {};
	//IMPORTANT! Before you call any of these functions, run this function on your blocks array!
	//This makes it so all empty blocks become AIR, I found this makes the
	//greedy mesher about 20% faster (even with the preprocessing).
	meshCommon.preprocessBlocks = function (blocks) {
		for (var index = 0; index < blocks.length; index++) {
			if (Block.isInvisible(blocks[index])) {
				blocks[index] = Block.AIR;
			}
		}
	}

	//The order is important here! It makes sure the normals line up with the 'face numbers' given
	//by Block.getColors.
	meshCommon.LOOP_CUBEFACES_DATA = [
		{faceDirection: 1,  compX: 1, compY: 2, compZ: 0},
		{faceDirection: -1, compX: 1, compY: 2, compZ: 0},
		{faceDirection: 1,  compX: 2, compY: 0, compZ: 1},
		{faceDirection: -1, compX: 2, compY: 0, compZ: 1},
		{faceDirection: 1,  compX: 0, compY: 1, compZ: 2},
		{faceDirection: -1, compX: 0, compY: 1, compZ: 2}
	];

	function noiseFunc(bcX, bcY, bcZ, voxelization) {
		function n(q) {
			return perlinNoise(Math.abs(bcX) / q, Math.abs(bcY) / q, Math.abs(bcZ) / q);
		}
		var val = n(8) + n(32);
		if (abs(voxelization - 4) > 0.001) val += n(4);
		if (abs(voxelization - 2) > 0.001) val += n(2);
		return clamp(val / 2 + 0.5, 0.0, 1.0);
	}

	meshCommon.getVoxelatedBlockType = function(ocXStart, ocYStart, ocZStart, voxelization, blocks) {
		if(voxelization == 1) {
			return blocks[
						ocXStart * CHUNK_WIDTH * CHUNK_HEIGHT +
						ocYStart * CHUNK_WIDTH +
						ocZStart
					];
		}

		//Ugh... have to sample to find the block
		var blockCounts = [];

		//If we wanted to allow for say, voxelization of 3 (meaning the edges
		//are different size) this would be where we would do part of it... it would
		//make the chunk boundaries look bad though.

		var ocXEnd = ocXStart + voxelization;
		var ocYEnd = ocYStart + voxelization;
		var ocZEnd = ocZStart + voxelization;

		for (var ocX = ocXStart; ocX < ocXEnd; ocX++) {
			for (var ocY = ocYStart; ocY < ocYEnd; ocY++) {
				for (var ocZ = ocZStart; ocZ < ocZEnd; ocZ++) {
					var sampleBlockType = blocks[
							ocX * CHUNK_WIDTH * CHUNK_HEIGHT +
							ocY * CHUNK_WIDTH +
							ocZ
						];
					if (!blockCounts[sampleBlockType]) {
						blockCounts[sampleBlockType] = 0;
					}
					blockCounts[sampleBlockType]++;
				}
			}
		}

		//We make our block the most common block, excluding non-solid blocks.
		//However if we are all air, then we do become air (really we just don't
		//render).
		var maxCount = 0;
		var planeBlock = Block.AIR; //Eh... slightly different than simpleMesher, but will result in the same thing.

		for(var blockType = 0; blockType < blockCounts.length; blockType++) {
			var blockCount = blockCounts[blockType];
			if (blockCount > maxCount && blockType != Block.AIR) {
				maxCount = blockCount;
				planeBlock = blockType;
			}
		}

		return planeBlock;
	}

	meshCommon.getNeighbourBlockType = function(ocXStart, ocYStart, ocZStart, blocks, neighbourComp, voxelization) {
		var sampleSizeArr = [voxelization, voxelization, voxelization];
		sampleSizeArr[neighbourComp] = 1;
		for (var ocX = ocXStart; ocX < sampleSizeArr[0] + ocXStart; ocX++) {
			for (var ocY = ocYStart; ocY < sampleSizeArr[1] + ocYStart; ocY++) {
				for (var ocZ = ocZStart; ocZ < sampleSizeArr[2] + ocZStart; ocZ++) {
					var adjBlock = blocks[
							ocX * CHUNK_WIDTH * CHUNK_HEIGHT +
							ocY * CHUNK_WIDTH +
							ocZ];
					if (adjBlock == Block.AIR) {
						return Block.AIR;
					}
				}
			}
		}
		return Block.DIRT; //Any solid would do
	}

	//ocArr is an in/out, if it is beyond the bounds of the cc chunk, a neighbour chunk's data
	//  is returned and oc is modified to be relative to that.
	//compZ is the dimension in ocArr we should check.
	//Assumes ocArr is only 1 off, which makes it easier for greedyMesher.
	//Returns null if the chunk does not exist
	var chunkDims = [CHUNK_WIDTH, CHUNK_HEIGHT, CHUNK_DEPTH];
	meshCommon.getBlockData = function(manager, blocks, ccArr, ocArr, compZ) {
		var ccDirection = 0;
		if (ocArr[compZ] < 0) {
			ocArr[compZ] = chunkDims[compZ] - 1;
			ccDirection = -1;
		}
		else if (ocArr[compZ] >= chunkDims[compZ]) {
			ocArr[compZ] = 0;
			ccDirection = +1;
		} else {
			return blocks;
		}

		ccArr[compZ] += ccDirection;
		var neighbourChunk = manager.chunkAt(ccArr[0], ccArr[1], ccArr[2]);
		ccArr[compZ] -= ccDirection;

		if(!neighbourChunk) return null;

		return neighbourChunk.blocks;
	}

	meshCommon.addQuad = function(bcX, bcY, bcZ, quadWidth, quadHeight, compZ, faceDirection, voxelization, verts) {
		//The face direction is always right-hand rule, so we place the vertices accordingly
		var counterClockwise = [
				[0, 0],
				[1, 0],
				[1, 1],
				[0, 1],
				[0.5, 0.5]
			];
		var clockwise = [
				[0, 0],
				[0, 1],
				[1, 1],
				[1, 0],
				[0.5, 0.5]
			];
		var offsetArray = faceDirection == 1 ? counterClockwise : clockwise;

		var bcVerts = [bcX, bcY, bcZ];

		if (faceDirection == 1) {
			bcVerts[compZ] += voxelization;
		}

		for (var iVertex = 0; iVertex < offsetArray.length; iVertex++) {
			var offsets = offsetArray[iVertex];

			bcVerts[(compZ + 1) % 3] += quadWidth * offsets[0];
			bcVerts[(compZ + 2) % 3] += quadHeight * offsets[1];

			verts.push(bcVerts[0], bcVerts[1], bcVerts[2]);

			bcVerts[(compZ + 1) % 3] -= quadWidth * offsets[0];
			bcVerts[(compZ + 2) % 3] -= quadHeight * offsets[1];
		}
	}

	//This is the function which most meshers should call to create their return structure.

	//Takes:
	//var verts = []; //Each vertice is made of 3 integers (3D point)
	//var blockTypes = []; //1 per face, which is has 5 points, so 15 verts
	//var faceNumbers = []; //same a blockTypes in size
	//var indexes = []; //indexes for triangles of points in verts
	meshCommon.generateGeometry = function(verts, blockTypes, faceNumbers, indexes, voxelization) {
		function copy(src, dst) {
			for (var i = 0; i < src.length; i++) {
				dst[i] = src[i];
			}
		}

		var vertsa = new Float32Array(verts.length);
		copy(verts, vertsa);

		//Bunch of squares, point in each corner and one in the middle
		//Each point is made up of 3 numbers
		for(var iVert = 0; iVert < verts.length / 3; iVert += 5) {
			indexes.push(iVert, iVert + 1, iVert + 4);
			indexes.push(iVert + 1, iVert + 2, iVert + 4);
			indexes.push(iVert + 2, iVert + 3, iVert + 4);
			indexes.push(iVert + 3, iVert, iVert + 4);
		}

		var color = [];

		for(var iFace = 0; iFace < verts.length / 15; iFace++) {
			var iVertexStart = iFace * 15;
			var blockType = blockTypes[iFace];
			var faceNumber = faceNumbers[iFace];

			var colours = Block.getColours(blockType, faceNumber);
			var c = colours.light;
			var c2 = colours.dark;

			for(var iVertex = 0; iVertex < 5; iVertex++) {
				var iVS = iVertexStart;
				var noise = noiseFunc(verts[iVS], verts[iVS + 1], verts[iVS + 2], voxelization);

				var r = c.r*noise + c2.r*(1 - noise);
				var g = c.g*noise + c2.g*(1 - noise);
				var b = c.b*noise + c2.b*(1 - noise);
				color.push(r/255, g/255, b/255);

				iVertexStart += 3;
			}
		}

		var indexa = new Uint16Array(indexes.length);
		copy(indexes, indexa);

		var colora = new Float32Array(color.length);
		copy(color, colora);

		var attributes = {
			position: { //x, y, z (triangles)
				itemSize: 3,
				array: vertsa,
				numItems: verts.length,
			},
			index: {    //triangle indices inside position (so *3 for real index), every 3 make up a triangle.
				itemSize: 1,
				array: indexa,
				numItems: indexes.length,
			},
			color: {    //colors of positions (vertices), we use a vertex shader
						//(possibly a built in one) to color the faces based on these.
				itemSize: 3,
				array: colora,
				numItems: color.length,
			},
		};
		var offsets = [{ //Just states the indexes used, you could theoretically have multiple of these?
			start: 0,
			count: indexes.length,
			index: 0,
		}];

		return {
			attributes: attributes,
			offsets: offsets,
			transferables: [vertsa.buffer, indexa.buffer, colora.buffer],
		};
	}

	return meshCommon;
}();<commit_msg>Mostly fixed the white pixel problem, you may still see some white pixels in the distance though.<commit_after>var meshCommon = function() {
	var meshCommon = {};
	//IMPORTANT! Before you call any of these functions, run this function on your blocks array!
	//This makes it so all empty blocks become AIR, I found this makes the
	//greedy mesher about 20% faster (even with the preprocessing).
	meshCommon.preprocessBlocks = function (blocks) {
		for (var index = 0; index < blocks.length; index++) {
			if (Block.isInvisible(blocks[index])) {
				blocks[index] = Block.AIR;
			}
		}
	}

	//The order is important here! It makes sure the normals line up with the 'face numbers' given
	//by Block.getColors.
	meshCommon.LOOP_CUBEFACES_DATA = [
		{faceDirection: 1,  compX: 1, compY: 2, compZ: 0},
		{faceDirection: -1, compX: 1, compY: 2, compZ: 0},
		{faceDirection: 1,  compX: 2, compY: 0, compZ: 1},
		{faceDirection: -1, compX: 2, compY: 0, compZ: 1},
		{faceDirection: 1,  compX: 0, compY: 1, compZ: 2},
		{faceDirection: -1, compX: 0, compY: 1, compZ: 2}
	];

	function noiseFunc(bcX, bcY, bcZ, voxelization) {
		function n(q) {
			return perlinNoise(Math.abs(bcX) / q, Math.abs(bcY) / q, Math.abs(bcZ) / q);
		}
		var val = n(8) + n(32);
		if (abs(voxelization - 4) > 0.001) val += n(4);
		if (abs(voxelization - 2) > 0.001) val += n(2);
		return clamp(val / 2 + 0.5, 0.0, 1.0);
	}

	meshCommon.getVoxelatedBlockType = function(ocXStart, ocYStart, ocZStart, voxelization, blocks) {
		if(voxelization == 1) {
			return blocks[
						ocXStart * CHUNK_WIDTH * CHUNK_HEIGHT +
						ocYStart * CHUNK_WIDTH +
						ocZStart
					];
		}

		//Ugh... have to sample to find the block
		var blockCounts = [];

		//If we wanted to allow for say, voxelization of 3 (meaning the edges
		//are different size) this would be where we would do part of it... it would
		//make the chunk boundaries look bad though.

		var ocXEnd = ocXStart + voxelization;
		var ocYEnd = ocYStart + voxelization;
		var ocZEnd = ocZStart + voxelization;

		for (var ocX = ocXStart; ocX < ocXEnd; ocX++) {
			for (var ocY = ocYStart; ocY < ocYEnd; ocY++) {
				for (var ocZ = ocZStart; ocZ < ocZEnd; ocZ++) {
					var sampleBlockType = blocks[
							ocX * CHUNK_WIDTH * CHUNK_HEIGHT +
							ocY * CHUNK_WIDTH +
							ocZ
						];
					if (!blockCounts[sampleBlockType]) {
						blockCounts[sampleBlockType] = 0;
					}
					blockCounts[sampleBlockType]++;
				}
			}
		}

		//We make our block the most common block, excluding non-solid blocks.
		//However if we are all air, then we do become air (really we just don't
		//render).
		var maxCount = 0;
		var planeBlock = Block.AIR; //Eh... slightly different than simpleMesher, but will result in the same thing.

		for(var blockType = 0; blockType < blockCounts.length; blockType++) {
			var blockCount = blockCounts[blockType];
			if (blockCount > maxCount && blockType != Block.AIR) {
				maxCount = blockCount;
				planeBlock = blockType;
			}
		}

		return planeBlock;
	}

	meshCommon.getNeighbourBlockType = function(ocXStart, ocYStart, ocZStart, blocks, neighbourComp, voxelization) {
		var sampleSizeArr = [voxelization, voxelization, voxelization];
		sampleSizeArr[neighbourComp] = 1;
		for (var ocX = ocXStart; ocX < sampleSizeArr[0] + ocXStart; ocX++) {
			for (var ocY = ocYStart; ocY < sampleSizeArr[1] + ocYStart; ocY++) {
				for (var ocZ = ocZStart; ocZ < sampleSizeArr[2] + ocZStart; ocZ++) {
					var adjBlock = blocks[
							ocX * CHUNK_WIDTH * CHUNK_HEIGHT +
							ocY * CHUNK_WIDTH +
							ocZ];
					if (adjBlock == Block.AIR) {
						return Block.AIR;
					}
				}
			}
		}
		return Block.DIRT; //Any solid would do
	}

	//ocArr is an in/out, if it is beyond the bounds of the cc chunk, a neighbour chunk's data
	//  is returned and oc is modified to be relative to that.
	//compZ is the dimension in ocArr we should check.
	//Assumes ocArr is only 1 off, which makes it easier for greedyMesher.
	//Returns null if the chunk does not exist
	var chunkDims = [CHUNK_WIDTH, CHUNK_HEIGHT, CHUNK_DEPTH];
	meshCommon.getBlockData = function(manager, blocks, ccArr, ocArr, compZ) {
		var ccDirection = 0;
		if (ocArr[compZ] < 0) {
			ocArr[compZ] = chunkDims[compZ] - 1;
			ccDirection = -1;
		}
		else if (ocArr[compZ] >= chunkDims[compZ]) {
			ocArr[compZ] = 0;
			ccDirection = +1;
		} else {
			return blocks;
		}

		ccArr[compZ] += ccDirection;
		var neighbourChunk = manager.chunkAt(ccArr[0], ccArr[1], ccArr[2]);
		ccArr[compZ] -= ccDirection;

		if(!neighbourChunk) return null;

		return neighbourChunk.blocks;
	}

	var VERTS_PER_FACE = 4;
	meshCommon.addQuad = function(bcX, bcY, bcZ, quadWidth, quadHeight, compZ, faceDirection, voxelization, verts) {
		//The face direction is always right-hand rule, so we place the vertices accordingly
		var counterClockwise = [
				[0, 0],
				[1, 0],
				[1, 1],
				[0, 1]
			];
		var clockwise = [
				[0, 0],
				[0, 1],
				[1, 1],
				[1, 0]
			];
		if(counterClockwise.length != VERTS_PER_FACE || clockwise.length != VERTS_PER_FACE) {
			throw "Update VERTS_PER_FACE...";
		}
		var offsetArray = faceDirection == 1 ? counterClockwise : clockwise;

		var bcVerts = [bcX, bcY, bcZ];

		if (faceDirection == 1) {
			bcVerts[compZ] += voxelization;
		}

		//The amount we make vertices overlap to prevent rounding problems.
		var quadBufferPercent = 0.0005;

		for (var iVertex = 0; iVertex < offsetArray.length; iVertex++) {
			var offsets = offsetArray[iVertex];

			var curWidth;
			var curHeight;

			if(offsets[0]) {
				curWidth = quadWidth * offsets[0] * (1 + quadBufferPercent);
			} else {
				curWidth = quadWidth * -quadBufferPercent;
			}
			if(offsets[1]) {
				curHeight = quadHeight * offsets[1] * (1 + quadBufferPercent);
			} else {
				curHeight = quadHeight * -quadBufferPercent;
			}

			bcVerts[(compZ + 1) % 3] += curWidth;
			bcVerts[(compZ + 2) % 3] += curHeight;

			verts.push(bcVerts[0], bcVerts[1], bcVerts[2]);

			bcVerts[(compZ + 1) % 3] -= curWidth;
			bcVerts[(compZ + 2) % 3] -= curHeight;
		}
	}

	//This is the function which most meshers should call to create their return structure.

	//Takes:
	//var verts = []; //Each vertice is made of 3 integers (3D point)
	//var blockTypes = []; //1 per face, which is has VERTS_PER_FACE points, so 3 * VERTS_PER_FACE verts
	//var faceNumbers = []; //same a blockTypes in size
	//var indexes = []; //indexes for triangles of points in verts
	meshCommon.generateGeometry = function(verts, blockTypes, faceNumbers, indexes, voxelization) {
		function copy(src, dst) {
			for (var i = 0; i < src.length; i++) {
				dst[i] = src[i];
			}
		}

		var vertsa = new Float32Array(verts.length);
		copy(verts, vertsa);

		//Bunch of squares, point in each corner and one in the middle
		//Each point is made up of 3 numbers
		for(var iVert = 0; iVert < verts.length / 3; iVert += VERTS_PER_FACE) {
			//2 triangle faces
			indexes.push(iVert, iVert + 1, iVert + 2);
			indexes.push(iVert + 2, iVert + 3, iVert);
			//Code for 4 triangle faces:
			//indexes.push(iVert, iVert + 1, iVert + 4);
			//indexes.push(iVert + 1, iVert + 2, iVert + 4);
			//indexes.push(iVert + 2, iVert + 3, iVert + 4);
			//indexes.push(iVert + 3, iVert, iVert + 4);
		}

		var color = [];

		for(var iFace = 0; iFace < verts.length / (3 * VERTS_PER_FACE); iFace++) {
			var iVertexStart = iFace * 3 * VERTS_PER_FACE;
			var blockType = blockTypes[iFace];
			var faceNumber = faceNumbers[iFace];

			var colours = Block.getColours(blockType, faceNumber);
			var c = colours.light;
			var c2 = colours.dark;

			for(var iVertex = 0; iVertex < VERTS_PER_FACE; iVertex++) {
				var iVS = iVertexStart + iVertex * 3;
				var noise = noiseFunc(verts[iVS], verts[iVS + 1], verts[iVS + 2], voxelization);

				var r = c.r*noise + c2.r*(1 - noise);
				var g = c.g*noise + c2.g*(1 - noise);
				var b = c.b*noise + c2.b*(1 - noise);
				color.push(r/255, g/255, b/255);
			}
		}

		var indexa = new Uint16Array(indexes.length);
		copy(indexes, indexa);

		var colora = new Float32Array(color.length);
		copy(color, colora);

		var attributes = {
			position: { //x, y, z (triangles)
				itemSize: 3,
				array: vertsa,
				numItems: verts.length,
			},
			index: {    //triangle indices inside position (so *3 for real index), every 3 make up a triangle.
				itemSize: 1,
				array: indexa,
				numItems: indexes.length,
			},
			color: {    //colors of positions (vertices), we use a vertex shader
						//(possibly a built in one) to color the faces based on these.
				itemSize: 3,
				array: colora,
				numItems: color.length,
			},
		};
		var offsets = [{ //Just states the indexes used, you could theoretically have multiple of these?
			start: 0,
			count: indexes.length,
			index: 0,
		}];

		return {
			attributes: attributes,
			offsets: offsets,
			transferables: [vertsa.buffer, indexa.buffer, colora.buffer],
		};
	}

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


<commit_before>import React, { Component } from 'react';

import withStyles from '@material-ui/core/styles/withStyles';
import Button from '@material-ui/core/Button';
import Card from '@material-ui/core/Card';
import CardContent from '@material-ui/core/CardContent';
import CardActions from '@material-ui/core/CardActions';
import Typography from '@material-ui/core/Typography';

import ClearIcon from '@material-ui/icons/Clear';
import SearchIcon from '@material-ui/icons/Search';
import { TextField } from 'redux-form-material-ui';

import { reduxForm, Field, reset} from 'redux-form';

import { connect } from 'react-redux'
import { push } from 'connected-react-router'
import PropTypes from 'prop-types';
import _ from 'lodash'

import './index.css'
import { searchDatasetsByFields } from '../../redux/ducks/datasets'
import selectors from  '../../redux/selectors'

import PageHead from '../page-head';
import PageBody from '../page-body';

const style = (theme) => (
  {
    field: {
      width: '100%',
      display: 'flex',
      marginBottom: 2 * theme.spacing.unit,
    },
    textField: {
      flexGrow: 1
    },
    actions: {
      display: 'flex',
      justifyContent: 'flex-end'
    }
  }
)

class Search extends Component {

  search = () => {

    let authors = null;
    let atomicSpecies = null;

    if (_.isString(this.props.authors)) {
      authors = this.props.authors.split(/\s/);
    }
    if (_.isString(this.props.atomicSpecies)) {
      atomicSpecies = this.props.atomicSpecies.split(/\s/);
    }

    this.props.dispatch(
        searchDatasetsByFields(
            this.props.title,
            authors,
            atomicSpecies,
            this.props.slug
        )
    );

    this.props.dispatch(push('/results'));
  }

  reset = () => {
    this.props.dispatch(reset('search'));
  }

  onKeyDown = e => {
    if(e.keyCode === 13 && e.shiftKey === false) {
      this.search();
    }
  }

  render = () => {
    const { pristine, submitting, invalid, classes } = this.props;
    return (

        <PageHead>
          <Typography  color="inherit" gutterBottom variant="display1">
            Search for structures
          </Typography>
          <Typography variant="subheading" paragraph color="inherit">
            Filter by publication title, authors, or atomic species.
          </Typography>
        </PageHead>
        <PageBody>
          <Card>
            <form onKeyDown={this.onKeyDown}>
              <CardContent>
                <Field
                  fullWidth
                  name="title"
                  className={classes.field}
                  component={TextField}
                  label="Title"
                />
                <Field
                  fullWidth
                  name="authors"
                  className={classes.field}
                  component={TextField}
                  label="Author"
                />
                <Field
                  fullWidth
                  name="atomicSpecies"
                  className={classes.field}
                  component={TextField}
                  label="Atomic Species"
                />
                <Field
                  fullWidth
                  name="slug"
                  className={classes.field}
                  component={TextField}
                  label="MDB ID"
                />
              </CardContent>
              <CardActions className={classes.actions}>
                <Button
                  variant="raised"
                  disabled={ submitting || invalid}
                  color="primary"
                  onClick={() => this.search()}
                >
                  <SearchIcon />
                  Search
                </Button>
                <Button
                  variant="raised"
                  disabled={pristine || submitting}
                  color="primary"
                  onClick={() => this.reset()}
                >
                  <ClearIcon/>
                  Clear
                </Button>
              </CardActions>
            </form>
          </Card>
        </PageBody>

    );
  }
}

Search.propTypes = {
  title: PropTypes.string,
  authors: PropTypes.string,
  atomicSpecies:  PropTypes.string
}

Search.defaultProps = {
  title: null,
  authors: null,
  atomicSpecies: null
}

function mapStateToProps(state, ownProps) {

  let search = selectors.search.getSearch(state);
  let props = {}
  if (!_.isNil(search)) {
    props = {...search.values}
  }

  return props;
}

Search = withStyles(style)(Search);
Search = connect(mapStateToProps)(Search)

export default reduxForm({
  form: 'search',
  destroyOnUnmount: false
})(Search)

<commit_msg>Reorganize search fields<commit_after>import React, { Component } from 'react';

import withStyles from '@material-ui/core/styles/withStyles';
import Button from '@material-ui/core/Button';
import Card from '@material-ui/core/Card';
import CardContent from '@material-ui/core/CardContent';
import CardActions from '@material-ui/core/CardActions';
import Typography from '@material-ui/core/Typography';

import ClearIcon from '@material-ui/icons/Clear';
import SearchIcon from '@material-ui/icons/Search';
import { TextField } from 'redux-form-material-ui';

import { reduxForm, Field, reset} from 'redux-form';

import { connect } from 'react-redux'
import { push } from 'connected-react-router'
import PropTypes from 'prop-types';
import _ from 'lodash'

import './index.css'
import { searchDatasetsByFields } from '../../redux/ducks/datasets'
import selectors from  '../../redux/selectors'

import PageHead from '../page-head';
import PageBody from '../page-body';

const style = (theme) => (
  {
    field: {
      width: '100%',
      display: 'flex',
      marginBottom: 2 * theme.spacing.unit,
    },
    textField: {
      flexGrow: 1
    },
    actions: {
      display: 'flex',
      justifyContent: 'flex-end'
    }
  }
)

class Search extends Component {

  search = () => {

    let authors = null;
    let atomicSpecies = null;

    if (_.isString(this.props.authors)) {
      authors = this.props.authors.split(/\s/);
    }
    if (_.isString(this.props.atomicSpecies)) {
      atomicSpecies = this.props.atomicSpecies.split(/\s/);
    }

    this.props.dispatch(
        searchDatasetsByFields(
            this.props.title,
            authors,
            atomicSpecies,
            this.props.slug
        )
    );

    this.props.dispatch(push('/results'));
  }

  reset = () => {
    this.props.dispatch(reset('search'));
  }

  onKeyDown = e => {
    if(e.keyCode === 13 && e.shiftKey === false) {
      this.search();
    }
  }

  render = () => {
    const { pristine, submitting, invalid, classes } = this.props;
    return (

        <PageHead>
          <Typography  color="inherit" gutterBottom variant="display1">
            Search for structures
          </Typography>
        </PageHead>
        <PageBody>
          <Card>
            <form onKeyDown={this.onKeyDown}>
              <CardContent>
                <Field
                  fullWidth
                  name="slug"
                  className={classes.field}
                  component={TextField}
                  label="MDB ID"
                />
                <Field
                  fullWidth
                  name="title"
                  className={classes.field}
                  component={TextField}
                  label="Name of the structure"
                />
                <Field
                  fullWidth
                  name="authors"
                  className={classes.field}
                  component={TextField}
                  label="Author"
                />
                <Field
                  fullWidth
                  name="atomicSpecies"
                  className={classes.field}
                  component={TextField}
                  label="Atomic species"
                />

              </CardContent>
              <CardActions className={classes.actions}>
                <Button
                  variant="raised"
                  disabled={ submitting || invalid}
                  color="primary"
                  onClick={() => this.search()}
                >
                  <SearchIcon />
                  Search
                </Button>
                <Button
                  variant="raised"
                  disabled={pristine || submitting}
                  color="primary"
                  onClick={() => this.reset()}
                >
                  <ClearIcon/>
                  Clear
                </Button>
              </CardActions>
            </form>
          </Card>
        </PageBody>

    );
  }
}

Search.propTypes = {
  title: PropTypes.string,
  authors: PropTypes.string,
  atomicSpecies:  PropTypes.string
}

Search.defaultProps = {
  title: null,
  authors: null,
  atomicSpecies: null
}

function mapStateToProps(state, ownProps) {

  let search = selectors.search.getSearch(state);
  let props = {}
  if (!_.isNil(search)) {
    props = {...search.values}
  }

  return props;
}

Search = withStyles(style)(Search);
Search = connect(mapStateToProps)(Search)

export default reduxForm({
  form: 'search',
  destroyOnUnmount: false
})(Search)

<|endoftext|>


<commit_before>import { Meteor } from 'meteor/meteor';
import { Template } from 'meteor/templating';
import { FlowRouter } from 'meteor/kadira:flow-router';
import { FlashMessage } from '/client/helpers/flashMessage';
import $ from 'jquery';

function loginCallback(error) {
    if (error) {
        console.error('An error occurred while trying to log in:', error);
        (new FlashMessage('Login error', error.message)).show();
    }

    let routeName = FlowRouter.current().route.getName();
    if (routeName === 'login' || routeName === 'signup' || routeName === 'home') {
        FlowRouter.go('home');
    }
}

Template.loginLdap.events({
    'submit #ldapLoginForm'(event) {
        event.preventDefault();

        let username = $('#id_ldapUsername').val();
        let password = $('#id_ldapPassword').val();

        Meteor.loginWithLdap(username, password, loginCallback);
    }
});<commit_msg>Fix: LDAP login produced client side exception. Fixes #338<commit_after>import { Meteor } from 'meteor/meteor';
import { Template } from 'meteor/templating';
import { FlowRouter } from 'meteor/kadira:flow-router';
import { FlashMessage } from '/client/helpers/flashMessage';
import $ from 'jquery';

function loginCallback(error) {
    if (error) {
        console.error('An error occurred while trying to log in:', error);
        (new FlashMessage('Login error', error.message)).show();
    }

    let routeName = FlowRouter.current().route.name;
    if (routeName === 'login' || routeName === 'signup' || routeName === 'home') {
        FlowRouter.go('home');
    }
}

Template.loginLdap.events({
    'submit #ldapLoginForm'(event) {
        event.preventDefault();

        let username = $('#id_ldapUsername').val();
        let password = $('#id_ldapPassword').val();

        Meteor.loginWithLdap(username, password, loginCallback);
    }
});<|endoftext|>