<commit_before>/** @jsx element */

import VirtualList from '../components/VirtualList'
import LinkModal from '../components/LinkModal'
import SortHeader from '../components/SortHeader'
import ChallengeLoader from './ChallengeLoader'
import createAction from '@f/create-action'
import handleActions from '@f/handle-actions'
import {Block, Flex, Menu} from 'vdux-ui'
import Button from '../components/Button'
import Empty from '../components/Empty'
import flatten from 'lodash/flatten'
import {refMethod} from 'vdux-fire'
import objEqual from '@f/equal-obj'
import element from 'vdux/element'
import sort from 'lodash/orderBy'
import Window from 'vdux/window'
import chunk from 'lodash/chunk'
import map from '@f/map'

const setModal = createAction('<ChallengeFeed/>: SET_MODAL')
const setOrderType = createAction('<ChallengeFeed/>: SET_ORDER_TYPE')
const setOrder = createAction('<ChallengeFeed/>: SET_ORDER')
const setItems = createAction('<ChallengeFeed:> SET_ITEMS')

const caseInsensitiveName = (game) => game.name
  ? game.name.toLowerCase()
  : game.title.toLowerCase()

const initialState = ({local}) => ({
  modal: '',
  orderBy: 'lastEdited',
  order: 'desc',
  items: [],
  actions: {
    setOrder: local(setOrder),
    setModal: local(setModal),
    setOrderType: local(setOrderType),
    setItems: local(setItems)
  }
})

function * onCreate ({props, local, state}) {
  const sortedGames = sort(map((game, key) => ({...game, key}), props.games), state.orderBy, state.order)
  yield state.actions.setItems(sortedGames)
}

function * onUpdate (prev, {props, local, state}) {
  if (prev.state.orderBy !== state.orderBy || prev.state.order !== state.order || !objEqual(prev.props.games, props.games) && props.games) {
    const sortedGames = sort(map((game, key) => ({...game, key}), props.games), state.orderBy, state.order)
    yield state.actions.setItems(sortedGames)
  }
}

function render ({props, state}) {
  const {games, selected = [], mine, uid, toggleSelected} = props
  const {modal, order, actions, orderBy, items} = state

  const modalFooter = (
    <Block>
      <Button ml='m' onClick={() => actions.setModal('')}>Done</Button>
    </Block>
  )

  if (items.length < 1) {
    return <Empty />
  }

  const Challenges = ({
    props
  }) => (
    <Block h={props.virtual.style.height} {...props.virtual.style} boxSizing='border-box'>
      {props.virtual.items.map((game, i) => (
        <ChallengeLoader
          checkbox
          lastEdited={game.lastEdited}
          setModal={actions.setModal}
          userRef={game.key}
          h={props.itemHeight}
          remove={remove}
          key={game.key}
          draggable={false}
          isSelected={selected.indexOf(game.ref) > -1}
          selectMode={selected.length > 0}
          handleClick={toggleSelected}
          uid={uid}
          mine={mine}
          ref={game.ref} />
      ))}
    </Block>
  )

  const CVL = VirtualList(Challenges, items, {height: 71, buffer: 20, container: document.getElementById('action-bar-holder')})

  return (
    <Flex flexWrap='wrap' wide margin='0 auto'>
      <Menu
        column
        wide
        border='1px solid #e0e0e0'
        borderTopWidth='0'
        bgColor='white'
        minWidth='820px'>
        <Block borderBottom='1px solid #E0E0E0' pl='5%' pr='16px' py='8px' color='#999' fontWeight='800' align='start center' bgColor='transparent'>
          <SortHeader
            onClick={() => handleClick(caseInsensitiveName)}
            minWidth='250px'
            flex
            dir={orderBy === caseInsensitiveName && order}
            label='NAME' />
          <SortHeader
            onClick={() => handleClick('animal')}
            dir={orderBy === 'animal' && order}
            minWidth='180px'
            w='180px'
            label='ANIMAL' />
          <SortHeader
            onClick={() => handleClick('inputType')}
            dir={orderBy === 'inputType' && order}
            minWidth='180px'
            w='180px'
            label='CODE TYPE' />
          <SortHeader
            onClick={() => handleClick('lastEdited')}
            dir={orderBy === 'lastEdited' && order}
            minWidth='180px'
            w='180px'
            label='LAST EDITED' />
        </Block>
        <Block>
          <CVL />
        </Block>
      </Menu>
      {
        modal && <LinkModal
          code={modal}
          footer={modalFooter} />
      }
    </Flex>
  )

  function * handleClick (cat) {
    if (orderBy === cat) {
      const newOrder = order === 'asc' ? 'desc' : 'asc'
      yield actions.setOrder(newOrder)
    } else {
      yield actions.setOrderType(cat)
    }
  }
}

function * remove (uid, ref) {
  yield refMethod({
    ref: `/users/${uid}/games/${ref}`,
    updates: {
      method: 'remove'
    }
  })
}

const reducer = handleActions({
  [setModal.type]: (state, payload) => ({...state, modal: payload}),
  [setOrderType.type]: (state, payload) => ({...state, orderBy: payload}),
  [setOrder.type]: (state, payload) => ({...state, order: payload}),
  [setItems.type]: (state, payload) => ({...state, items: payload})
})

export default {
  initialState,
  onUpdate,
  onCreate,
  reducer,
  render
}
<commit_msg>add more buffer to virtual list<commit_after>/** @jsx element */

import VirtualList from '../components/VirtualList'
import LinkModal from '../components/LinkModal'
import SortHeader from '../components/SortHeader'
import ChallengeLoader from './ChallengeLoader'
import createAction from '@f/create-action'
import handleActions from '@f/handle-actions'
import {Block, Flex, Menu} from 'vdux-ui'
import Button from '../components/Button'
import Empty from '../components/Empty'
import flatten from 'lodash/flatten'
import {refMethod} from 'vdux-fire'
import objEqual from '@f/equal-obj'
import element from 'vdux/element'
import sort from 'lodash/orderBy'
import Window from 'vdux/window'
import chunk from 'lodash/chunk'
import map from '@f/map'

const setModal = createAction('<ChallengeFeed/>: SET_MODAL')
const setOrderType = createAction('<ChallengeFeed/>: SET_ORDER_TYPE')
const setOrder = createAction('<ChallengeFeed/>: SET_ORDER')
const setItems = createAction('<ChallengeFeed:> SET_ITEMS')

const caseInsensitiveName = (game) => game.name
  ? game.name.toLowerCase()
  : game.title.toLowerCase()

const initialState = ({local}) => ({
  modal: '',
  orderBy: 'lastEdited',
  order: 'desc',
  items: [],
  actions: {
    setOrder: local(setOrder),
    setModal: local(setModal),
    setOrderType: local(setOrderType),
    setItems: local(setItems)
  }
})

function * onCreate ({props, local, state}) {
  const sortedGames = sort(map((game, key) => ({...game, key}), props.games), state.orderBy, state.order)
  yield state.actions.setItems(sortedGames)
}

function * onUpdate (prev, {props, local, state}) {
  if (prev.state.orderBy !== state.orderBy || prev.state.order !== state.order || !objEqual(prev.props.games, props.games) && props.games) {
    const sortedGames = sort(map((game, key) => ({...game, key}), props.games), state.orderBy, state.order)
    yield state.actions.setItems(sortedGames)
  }
}

function render ({props, state}) {
  const {games, selected = [], mine, uid, toggleSelected} = props
  const {modal, order, actions, orderBy, items} = state

  const modalFooter = (
    <Block>
      <Button ml='m' onClick={() => actions.setModal('')}>Done</Button>
    </Block>
  )

  if (items.length < 1) {
    return <Empty />
  }

  const Challenges = ({
    props
  }) => (
    <Block h={props.virtual.style.height} {...props.virtual.style} boxSizing='border-box'>
      {props.virtual.items.map((game, i) => (
        <ChallengeLoader
          checkbox
          lastEdited={game.lastEdited}
          setModal={actions.setModal}
          userRef={game.key}
          h={props.itemHeight}
          remove={remove}
          key={game.key}
          draggable={false}
          isSelected={selected.indexOf(game.ref) > -1}
          selectMode={selected.length > 0}
          handleClick={toggleSelected}
          uid={uid}
          mine={mine}
          ref={game.ref} />
      ))}
    </Block>
  )

  const CVL = VirtualList(Challenges, items, {height: 71, buffer: 25, container: document.getElementById('action-bar-holder')})

  return (
    <Flex flexWrap='wrap' wide margin='0 auto'>
      <Menu
        column
        wide
        border='1px solid #e0e0e0'
        borderTopWidth='0'
        bgColor='white'
        minWidth='820px'>
        <Block borderBottom='1px solid #E0E0E0' pl='5%' pr='16px' py='8px' color='#999' fontWeight='800' align='start center' bgColor='transparent'>
          <SortHeader
            onClick={() => handleClick(caseInsensitiveName)}
            minWidth='250px'
            flex
            dir={orderBy === caseInsensitiveName && order}
            label='NAME' />
          <SortHeader
            onClick={() => handleClick('animal')}
            dir={orderBy === 'animal' && order}
            minWidth='180px'
            w='180px'
            label='ANIMAL' />
          <SortHeader
            onClick={() => handleClick('inputType')}
            dir={orderBy === 'inputType' && order}
            minWidth='180px'
            w='180px'
            label='CODE TYPE' />
          <SortHeader
            onClick={() => handleClick('lastEdited')}
            dir={orderBy === 'lastEdited' && order}
            minWidth='180px'
            w='180px'
            label='LAST EDITED' />
        </Block>
        <Block>
          <CVL />
        </Block>
      </Menu>
      {
        modal && <LinkModal
          code={modal}
          footer={modalFooter} />
      }
    </Flex>
  )

  function * handleClick (cat) {
    if (orderBy === cat) {
      const newOrder = order === 'asc' ? 'desc' : 'asc'
      yield actions.setOrder(newOrder)
    } else {
      yield actions.setOrderType(cat)
    }
  }
}

function * remove (uid, ref) {
  yield refMethod({
    ref: `/users/${uid}/games/${ref}`,
    updates: {
      method: 'remove'
    }
  })
}

const reducer = handleActions({
  [setModal.type]: (state, payload) => ({...state, modal: payload}),
  [setOrderType.type]: (state, payload) => ({...state, orderBy: payload}),
  [setOrder.type]: (state, payload) => ({...state, order: payload}),
  [setItems.type]: (state, payload) => ({...state, items: payload})
})

export default {
  initialState,
  onUpdate,
  onCreate,
  reducer,
  render
}
<|endoftext|>


<commit_before>'use strict';

angular.module('confRegistrationWebApp')
  .controller('AngularUiTreeConfig', function ($scope, modalMessage) {
    $scope.toolbarTreeConfig = {
      accept: function(sourceNodeScope, destNodesScope) {
        return sourceNodeScope.$treeScope === destNodesScope.$treeScope;
      },
      beforeDrop: function(event) {
        //cancel regular drop action
        event.source.nodeScope.$$apply = false;
        //insert block
        if(event.dest.nodesScope.$nodeScope){ //prevents error from dropping on source tree
          var block = event.source.nodeScope.$modelValue;
          var pageId = event.dest.nodesScope.$nodeScope.$modelValue.id;
          $scope.insertBlock(block.id, pageId, event.dest.index, block.defaultTitle);
        }
      }
    };
    $scope.pageTreeConfig = {
      accept: function (sourceNodeScope, destNodesScope) {
        var sourceType = sourceNodeScope.$modelValue.pageId || sourceNodeScope.$modelValue.defaultTitle ? 'block' : 'page';
        var destType = destNodesScope.$element.attr('drop-type');
        return (sourceType === destType); // only accept the same type
      },
      beforeDrop: function(event){
        var block = event.source.nodeScope.block;
        if(angular.isUndefined(block)){ //must be a block to continue
          return;
        }

        var conference = $scope.$parent.$parent.conference;
        var positionArray = [];
        conference.registrationPages.forEach(function (page, pageIndex) {
          page.blocks.forEach(function (block, blockIndex) {
            positionArray[block.id] = {page: pageIndex, block: blockIndex, title: block.title};
          });
        });

        var sourcePageId = event.source.nodesScope.$nodeScope.$modelValue.id;
        var sourcePageIndex = _.findIndex(conference.registrationPages, {'id':  sourcePageId});

        var destPageId = event.dest.nodesScope.$nodeScope.$modelValue.id;
        var destPageIndex = _.findIndex(conference.registrationPages, {'id':  destPageId});

        var rulesViolated = [];
        //check if any of current blocks rules will be violated
        angular.forEach(block.rules, function(rule){
          var parentBlockLocation = positionArray[rule.parentBlockId];
          if(parentBlockLocation.page > destPageIndex || (parentBlockLocation.page === destPageIndex && parentBlockLocation.block >= event.dest.index)){
            rulesViolated.push('"' + block.title + '" must be below "' + parentBlockLocation.title + '".');
            event.source.nodeScope.$$apply = false;
          }
        });

        //check if any other blocks rules will be violated
        var allRules = _.flatten(_.flatten(conference.registrationPages, 'blocks'), 'rules');
        var rulesLinkedToBlock = _.where(allRules, {parentBlockId: block.id});
        angular.forEach(rulesLinkedToBlock, function(rule){
          var childBlockLocation = positionArray[rule.blockId];
          if(
              childBlockLocation.page < destPageIndex ||
              (childBlockLocation.page === destPageIndex && childBlockLocation.block < event.dest.index) ||
              (childBlockLocation.page === destPageIndex && sourcePageIndex === destPageIndex && childBlockLocation.block === event.dest.index)
          ){
            rulesViolated.push('"' + childBlockLocation.title + '" must be below "' + block.title + '".');
          }
        });

        if(rulesViolated.length){
          event.source.nodeScope.$$apply = false;
          modalMessage.error({
            'title': 'Error Moving Question',
            'message': '
Rule violations:

' + rulesViolated.join('

') + '

'
          });
        }
      }
    };
  });
<commit_msg>Remove extra 'event.source.nodeScope.$$apply = false;'<commit_after>'use strict';

angular.module('confRegistrationWebApp')
  .controller('AngularUiTreeConfig', function ($scope, modalMessage) {
    $scope.toolbarTreeConfig = {
      accept: function(sourceNodeScope, destNodesScope) {
        return sourceNodeScope.$treeScope === destNodesScope.$treeScope;
      },
      beforeDrop: function(event) {
        //cancel regular drop action
        event.source.nodeScope.$$apply = false;
        //insert block
        if(event.dest.nodesScope.$nodeScope){ //prevents error from dropping on source tree
          var block = event.source.nodeScope.$modelValue;
          var pageId = event.dest.nodesScope.$nodeScope.$modelValue.id;
          $scope.insertBlock(block.id, pageId, event.dest.index, block.defaultTitle);
        }
      }
    };
    $scope.pageTreeConfig = {
      accept: function (sourceNodeScope, destNodesScope) {
        var sourceType = sourceNodeScope.$modelValue.pageId || sourceNodeScope.$modelValue.defaultTitle ? 'block' : 'page';
        var destType = destNodesScope.$element.attr('drop-type');
        return (sourceType === destType); // only accept the same type
      },
      beforeDrop: function(event){
        var block = event.source.nodeScope.block;
        if(angular.isUndefined(block)){ //must be a block to continue
          return;
        }

        var conference = $scope.$parent.$parent.conference;
        var positionArray = [];
        conference.registrationPages.forEach(function (page, pageIndex) {
          page.blocks.forEach(function (block, blockIndex) {
            positionArray[block.id] = {page: pageIndex, block: blockIndex, title: block.title};
          });
        });

        var sourcePageId = event.source.nodesScope.$nodeScope.$modelValue.id;
        var sourcePageIndex = _.findIndex(conference.registrationPages, {'id':  sourcePageId});

        var destPageId = event.dest.nodesScope.$nodeScope.$modelValue.id;
        var destPageIndex = _.findIndex(conference.registrationPages, {'id':  destPageId});

        var rulesViolated = [];
        //check if any of current blocks rules will be violated
        angular.forEach(block.rules, function(rule){
          var parentBlockLocation = positionArray[rule.parentBlockId];
          if(parentBlockLocation.page > destPageIndex || (parentBlockLocation.page === destPageIndex && parentBlockLocation.block >= event.dest.index)){
            rulesViolated.push('"' + block.title + '" must be below "' + parentBlockLocation.title + '".');
          }
        });

        //check if any other blocks rules will be violated
        var allRules = _.flatten(_.flatten(conference.registrationPages, 'blocks'), 'rules');
        var rulesLinkedToBlock = _.where(allRules, {parentBlockId: block.id});
        angular.forEach(rulesLinkedToBlock, function(rule){
          var childBlockLocation = positionArray[rule.blockId];
          if(
              childBlockLocation.page < destPageIndex ||
              (childBlockLocation.page === destPageIndex && childBlockLocation.block < event.dest.index) ||
              (childBlockLocation.page === destPageIndex && sourcePageIndex === destPageIndex && childBlockLocation.block === event.dest.index)
          ){
            rulesViolated.push('"' + childBlockLocation.title + '" must be below "' + block.title + '".');
          }
        });

        if(rulesViolated.length){
          event.source.nodeScope.$$apply = false;
          modalMessage.error({
            'title': 'Error Moving Question',
            'message': '
Rule violations:

' + rulesViolated.join('

') + '

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


<commit_before>/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 *
 * @author: Dejan Dežman <dejan.dezman@cosylab.com>
 */

// Array of different search types
var searchTypes = [];

// Autocomplete object that can contain different arrays for autocompletion
var autocomplete = searchTypes;

$(document).ready(function(){

	// Listen to include history chenckbox change
	$('#search-query-clean').unbind('click');
	$('#search-query-clean').on('click', function(e) {
		deleteFilterData();
		window.location.reload();
	});

	// Create
	$.each(keyMap, function(key, value){
		searchTypes.push(key);
	});

	// Wait for dataselect on logbooks filter
	$('#load_logbooks').on('dataselected', function(e, data){
		generateSearchQuery(data);
	});

	// Wait for dataselect on tags filter
	$('#load_tags').on('dataselected', function(e, data){
		generateSearchQuery(data);
	});

	// Wait for dataselect on owner filter
	$('#load_owners').on('dataselected', function(e, data){
		generateSearchQuery(data);
	});

	// Wait for dataselect on time from filter
	$('#load_time_from').on('dataselected', function(e, data){
		generateSearchQuery(data);
	});

	// Wait for dataselect on time to filter
	$('#load_time_to').on('dataselected', function(e, data){
		generateSearchQuery(data);
	});

	// Wait for dataselect on from-to filter
	$('#load_time_from_to').on('dataselected', function(e, data){
		generateSearchQuery(data);
	});

	// Activate search autocomplete functionality
	searchAutocomplete();
});

/**
 * When search event happens, parse input and search for Logs.
 */
function activateSearch(){
	// Simple search
	var searchQuery = buildSearchQuery();
	l("search query " + searchQuery);

	// If search query is empty just load first page of Logs
	if(searchQuery === ""){
		page = 1;
		loadLogs(page, true);

	// If search query is not empty, send this query to the server
	} else {
		searchForLogs(searchQuery, true);
	}
}

/*
 * Start listening for Search multiple events.
 */
function startListeningForSearchEvents() {
	// Listen to a click of a button
	$("#search-button").click(function(e){
		activateSearch();
	});

	// Listen to press of an Enter key
	$("#search-query").keypress(function(e){

		if(e.which === 13) {
			activateSearch();
		}
	});
}

/**
 * Input search query and request data from the server
 * @param {type} searchQuery prebuilt search query
 * @param {type} resetPageCouner on some occasions we want page counter to be reset
 */
function searchForLogs(searchQuery, resetPageCouner) {

	// Reset pagecouner if new filters are selected
	if(resetPageCouner === true) {
		page = 1;
	}

	searchQuery = serviceurl + 'logs?' + searchQuery + 'page=' + page + '&limit=' + numberOfLogsPerLoad + '&';

	// Append include history parameter
	if(ologSettings.includeHistory && !("history" in $.url(searchQuery).param())) {
		searchQuery += historyParameter + "=&";
	}

	searchURL = searchQuery;

	l(searchURL);

	// Load logs
	$.getJSON(searchQuery, function(logs) {
		$(".log-last").remove();
		$(".log").remove();
		repeatLogs(logs, false);
		appendAddMoreLog("load_logs");
		startListeningForLogClicks();
		scrollToLastLog();

		$('.log img').last().load(function(){
			scrollToLastLog();
		});
	});
}

/**
 * Start parsing search input value from left to right and return type and search
 * value.
 * @param {type} value unparsed string part
 * @returns {Array} array that consists of search type, search value and remainder
 */
function parseSearchLanguage(value){

	var searchString = "";
	var filterType = "";
	var remainder = "";

	var re = new RegExp("(owner:|tag:|logbook:|from:|to:|^)(.*?)(owner:.*|tag:.*|logbook:.*|from:.*|to:.*|$)", "i");
	var searchParts = re.exec(value);

	if(searchParts === null) {
		searchString = value;

	} else {
		filterType = searchParts[1];
		searchString = searchParts[2];
		remainder = searchParts[3];
	}

	return [filterType, searchString, remainder];
}

/**
 * Build search query from search input
 * @returns {String} prepared search query
 */
function buildSearchQuery(){

	var value = $("#search-query").val();
	var query = "";

	var parsedStringParts = parseSearchLanguage(value);

	// Get custom part of search value
	if(parsedStringParts[0] === "") {

		// Prepend and append a star
		if(trim(parsedStringParts[1]) !== "") {
			query += keyMap['search:'] + "*" + trim(parsedStringParts[1]) + "*&";
		}

	} else {
		if(keyMap[parsedStringParts[0]] !== undefined) {

			if(parsedStringParts[0] === "from:" || parsedStringParts[0] === "start:") {
				query += keyMap[parsedStringParts[0]] + returnTimeFilterTimestamp(trim(parsedStringParts[1]), undefined)[0] + "&";

			} else if(parsedStringParts[0] === "to:" || parsedStringParts[0] === "end:") {
				query += keyMap[parsedStringParts[0]] + returnTimeFilterTimestamp(undefined, trim(parsedStringParts[1]))[1] + "&";

			} else {
				query += keyMap[parsedStringParts[0]] + trim(parsedStringParts[1]) + "&";
			}
		}
	}

	// Get tags, logbooks and time constraints that are present in search input
	while (parsedStringParts[2] !== "") {
		parsedStringParts = parseSearchLanguage(parsedStringParts[2]);

		if(keyMap[parsedStringParts[0]] !== undefined) {

			if(parsedStringParts[0] === "from:" || parsedStringParts[0] === "start:") {
				query += keyMap[parsedStringParts[0]] + returnTimeFilterTimestamp(trim(parsedStringParts[1]), undefined)[0] + "&";

			} else if(parsedStringParts[0] === "to:" || parsedStringParts[0] === "end:") {
				query += keyMap[parsedStringParts[0]] + returnTimeFilterTimestamp(undefined, trim(parsedStringParts[1]))[1] + "&";

			} else {
				query += keyMap[parsedStringParts[0]] + trim(parsedStringParts[1]) + "&";
			}
		}
	}
	return query;
}

/**
 * Generate search input string and search query when user clicks on filters
 * in the left pane.
 * @param {type} dataArray currently selected logbooks and tags
 * @returns {undefined}
 */
function generateSearchQuery(dataArray) {

	// If there is no data, try to get data from global variable
	if(dataArray === null) {
		dataArray = selectedElements;
	}

	var value = $("#search-query").val();
	var queryString = "";

	var parsedStringParts = parseSearchLanguage(value);

	var newValue = "";

	// Append general search to a search query
	if(parsedStringParts[0] === "" && value !== ""){
		newValue = parsedStringParts[1];
		queryString += "search=" + newValue + '&';

		if(newValue[newValue.length-1] !== " ") {
			newValue += " ";
		}
	}

	// If at least one logbook is selected, append logbook part to a search query
	if(dataArray['list'] !== undefined && dataArray['list'].length !== 0) {
		newValue += "logbook: " + dataArray['list'] + ' ';
		queryString += "logbook=" + dataArray['list'] + '&';
	}

	// If at least one tag is selected, append tag part to a search query
	if(dataArray['list2'] !== undefined && dataArray['list2'].length !== 0) {
		newValue += "tag: " + dataArray['list2'] + ' ';
		queryString += "tag=" + dataArray['list2'] + '&';
	}

	// If at least one owner is selected, append owner part to a search query
	if(dataArray['owner'] !== undefined && dataArray['owner'].length !== 0) {
		newValue += "owner: " + dataArray['owner'] + ' ';
		queryString += "owner=" + dataArray['owner'] + '&';
	}

	// From time filter is set, append time part to a search query
	if(dataArray['from'] !== undefined && dataArray['from'].length !== 0) {
		newValue += "from: " + dataArray['from'] + ' ';
		queryString += "start=" + returnTimeFilterTimestamp(dataArray['from'], undefined)[0] + '&';
	}

	// If to time filter is set, append time part to a search query
	if(dataArray['to'] !== undefined && dataArray['to'].length !== 0) {
		newValue += "to: " + dataArray['to'] + ' ';
		queryString += "end=" + returnTimeFilterTimestamp(undefined, dataArray['to'])[1] + '&';
	}

	// Append include history parameter
	if(ologSettings.includeHistory && !("history" in $.url(queryString).param())) {
		queryString += historyParameter + "=&";
	}

	// Set new search query to its place
	$("#search-query").val(newValue);
	searchForLogs(queryString, true);
}

/**
 * Split search input value by space
 * @param val current input value
 */
function split(val) {
	return val.split(/ \s*/);
}

/**
 * Extract last item when input is split by custom devider
 * @param {type} term devider for spliting input value
 * @returns last item that is result of slpit
 */
function extractLast(term) {
	return split(term).pop();
}

/**
 * Initialize autocomplete functionality on search input
 */
function searchAutocomplete() {

	// don't navigate away from the field on tab when selecting an item
	$("#search-query").bind("keydown", function(event) {
			if (event.keyCode === $.ui.keyCode.TAB &&
				$(this).data("ui-autocomplete").menu.active) {
				event.preventDefault();
			}

		}).autocomplete({
			minLength: 0,
			source: function(request, response) {
				// delegate back to autocomplete, but extract the last term
				response($.ui.autocomplete.filter(
				autocomplete, extractLast(request.term)));
			},
			focus: function() {
				// prevent value inserted on focus
				return false;
			},
			select: function(event, ui) {
				var terms = split(this.value);
				// remove the current input
				terms.pop();

				var selectedItem = ui.item.value;

				// add the selected item
				terms.push(ui.item.value);

				if(selectedItem === "tag:") {
					autocomplete = savedTags.concat(searchTypes);

				} else if(selectedItem === "logbook:") {
					autocomplete = savedLogbooks.concat(searchTypes);
				} else if(selectedItem === "owner:") {
					autocomplete = savedOwners.concat(searchTypes);
				}

				// add placeholder to get the comma-and-space at the end
				terms.push("");
				this.value = terms.join(" ");
				return false;
			}
		});
}
<commit_msg>remove owner autocomplete list of owners<commit_after>/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 *
 * @author: Dejan Dežman <dejan.dezman@cosylab.com>
 */

// Array of different search types
var searchTypes = [];

// Autocomplete object that can contain different arrays for autocompletion
var autocomplete = searchTypes;

$(document).ready(function(){

	// Listen to include history chenckbox change
	$('#search-query-clean').unbind('click');
	$('#search-query-clean').on('click', function(e) {
		deleteFilterData();
		window.location.reload();
	});

	// Create
	$.each(keyMap, function(key, value){
		searchTypes.push(key);
	});

	// Wait for dataselect on logbooks filter
	$('#load_logbooks').on('dataselected', function(e, data){
		generateSearchQuery(data);
	});

	// Wait for dataselect on tags filter
	$('#load_tags').on('dataselected', function(e, data){
		generateSearchQuery(data);
	});

	// Wait for dataselect on owner filter
	$('#load_owners').on('dataselected', function(e, data){
		generateSearchQuery(data);
	});

	// Wait for dataselect on time from filter
	$('#load_time_from').on('dataselected', function(e, data){
		generateSearchQuery(data);
	});

	// Wait for dataselect on time to filter
	$('#load_time_to').on('dataselected', function(e, data){
		generateSearchQuery(data);
	});

	// Wait for dataselect on from-to filter
	$('#load_time_from_to').on('dataselected', function(e, data){
		generateSearchQuery(data);
	});

	// Activate search autocomplete functionality
	searchAutocomplete();
});

/**
 * When search event happens, parse input and search for Logs.
 */
function activateSearch(){
	// Simple search
	var searchQuery = buildSearchQuery();
	l("search query " + searchQuery);

	// If search query is empty just load first page of Logs
	if(searchQuery === ""){
		page = 1;
		loadLogs(page, true);

	// If search query is not empty, send this query to the server
	} else {
		searchForLogs(searchQuery, true);
	}
}

/*
 * Start listening for Search multiple events.
 */
function startListeningForSearchEvents() {
	// Listen to a click of a button
	$("#search-button").click(function(e){
		activateSearch();
	});

	// Listen to press of an Enter key
	$("#search-query").keypress(function(e){

		if(e.which === 13) {
			activateSearch();
		}
	});
}

/**
 * Input search query and request data from the server
 * @param {type} searchQuery prebuilt search query
 * @param {type} resetPageCouner on some occasions we want page counter to be reset
 */
function searchForLogs(searchQuery, resetPageCouner) {

	// Reset pagecouner if new filters are selected
	if(resetPageCouner === true) {
		page = 1;
	}

	searchQuery = serviceurl + 'logs?' + searchQuery + 'page=' + page + '&limit=' + numberOfLogsPerLoad + '&';

	// Append include history parameter
	if(ologSettings.includeHistory && !("history" in $.url(searchQuery).param())) {
		searchQuery += historyParameter + "=&";
	}

	searchURL = searchQuery;

	l(searchURL);

	// Load logs
	$.getJSON(searchQuery, function(logs) {
		$(".log-last").remove();
		$(".log").remove();
		repeatLogs(logs, false);
		appendAddMoreLog("load_logs");
		startListeningForLogClicks();
		scrollToLastLog();

		$('.log img').last().load(function(){
			scrollToLastLog();
		});
	});
}

/**
 * Start parsing search input value from left to right and return type and search
 * value.
 * @param {type} value unparsed string part
 * @returns {Array} array that consists of search type, search value and remainder
 */
function parseSearchLanguage(value){

	var searchString = "";
	var filterType = "";
	var remainder = "";

	var re = new RegExp("(owner:|tag:|logbook:|from:|to:|^)(.*?)(owner:.*|tag:.*|logbook:.*|from:.*|to:.*|$)", "i");
	var searchParts = re.exec(value);

	if(searchParts === null) {
		searchString = value;

	} else {
		filterType = searchParts[1];
		searchString = searchParts[2];
		remainder = searchParts[3];
	}

	return [filterType, searchString, remainder];
}

/**
 * Build search query from search input
 * @returns {String} prepared search query
 */
function buildSearchQuery(){

	var value = $("#search-query").val();
	var query = "";

	var parsedStringParts = parseSearchLanguage(value);

	// Get custom part of search value
	if(parsedStringParts[0] === "") {

		// Prepend and append a star
		if(trim(parsedStringParts[1]) !== "") {
			query += keyMap['search:'] + "*" + trim(parsedStringParts[1]) + "*&";
		}

	} else {
		if(keyMap[parsedStringParts[0]] !== undefined) {

			if(parsedStringParts[0] === "from:" || parsedStringParts[0] === "start:") {
				query += keyMap[parsedStringParts[0]] + returnTimeFilterTimestamp(trim(parsedStringParts[1]), undefined)[0] + "&";

			} else if(parsedStringParts[0] === "to:" || parsedStringParts[0] === "end:") {
				query += keyMap[parsedStringParts[0]] + returnTimeFilterTimestamp(undefined, trim(parsedStringParts[1]))[1] + "&";

			} else {
				query += keyMap[parsedStringParts[0]] + trim(parsedStringParts[1]) + "&";
			}
		}
	}

	// Get tags, logbooks and time constraints that are present in search input
	while (parsedStringParts[2] !== "") {
		parsedStringParts = parseSearchLanguage(parsedStringParts[2]);

		if(keyMap[parsedStringParts[0]] !== undefined) {

			if(parsedStringParts[0] === "from:" || parsedStringParts[0] === "start:") {
				query += keyMap[parsedStringParts[0]] + returnTimeFilterTimestamp(trim(parsedStringParts[1]), undefined)[0] + "&";

			} else if(parsedStringParts[0] === "to:" || parsedStringParts[0] === "end:") {
				query += keyMap[parsedStringParts[0]] + returnTimeFilterTimestamp(undefined, trim(parsedStringParts[1]))[1] + "&";

			} else {
				query += keyMap[parsedStringParts[0]] + trim(parsedStringParts[1]) + "&";
			}
		}
	}
	return query;
}

/**
 * Generate search input string and search query when user clicks on filters
 * in the left pane.
 * @param {type} dataArray currently selected logbooks and tags
 * @returns {undefined}
 */
function generateSearchQuery(dataArray) {

	// If there is no data, try to get data from global variable
	if(dataArray === null) {
		dataArray = selectedElements;
	}

	var value = $("#search-query").val();
	var queryString = "";

	var parsedStringParts = parseSearchLanguage(value);

	var newValue = "";

	// Append general search to a search query
	if(parsedStringParts[0] === "" && value !== ""){
		newValue = parsedStringParts[1];
		queryString += "search=" + newValue + '&';

		if(newValue[newValue.length-1] !== " ") {
			newValue += " ";
		}
	}

	// If at least one logbook is selected, append logbook part to a search query
	if(dataArray['list'] !== undefined && dataArray['list'].length !== 0) {
		newValue += "logbook: " + dataArray['list'] + ' ';
		queryString += "logbook=" + dataArray['list'] + '&';
	}

	// If at least one tag is selected, append tag part to a search query
	if(dataArray['list2'] !== undefined && dataArray['list2'].length !== 0) {
		newValue += "tag: " + dataArray['list2'] + ' ';
		queryString += "tag=" + dataArray['list2'] + '&';
	}

	// If at least one owner is selected, append owner part to a search query
	if(dataArray['owner'] !== undefined && dataArray['owner'].length !== 0) {
		newValue += "owner: " + dataArray['owner'] + ' ';
		queryString += "owner=" + dataArray['owner'] + '&';
	}

	// From time filter is set, append time part to a search query
	if(dataArray['from'] !== undefined && dataArray['from'].length !== 0) {
		newValue += "from: " + dataArray['from'] + ' ';
		queryString += "start=" + returnTimeFilterTimestamp(dataArray['from'], undefined)[0] + '&';
	}

	// If to time filter is set, append time part to a search query
	if(dataArray['to'] !== undefined && dataArray['to'].length !== 0) {
		newValue += "to: " + dataArray['to'] + ' ';
		queryString += "end=" + returnTimeFilterTimestamp(undefined, dataArray['to'])[1] + '&';
	}

	// Append include history parameter
	if(ologSettings.includeHistory && !("history" in $.url(queryString).param())) {
		queryString += historyParameter + "=&";
	}

	// Set new search query to its place
	$("#search-query").val(newValue);
	searchForLogs(queryString, true);
}

/**
 * Split search input value by space
 * @param val current input value
 */
function split(val) {
	return val.split(/ \s*/);
}

/**
 * Extract last item when input is split by custom devider
 * @param {type} term devider for spliting input value
 * @returns last item that is result of slpit
 */
function extractLast(term) {
	return split(term).pop();
}

/**
 * Initialize autocomplete functionality on search input
 */
function searchAutocomplete() {

	// don't navigate away from the field on tab when selecting an item
	$("#search-query").bind("keydown", function(event) {
			if (event.keyCode === $.ui.keyCode.TAB &&
				$(this).data("ui-autocomplete").menu.active) {
				event.preventDefault();
			}

		}).autocomplete({
			minLength: 0,
			source: function(request, response) {
				// delegate back to autocomplete, but extract the last term
				response($.ui.autocomplete.filter(
				autocomplete, extractLast(request.term)));
			},
			focus: function() {
				// prevent value inserted on focus
				return false;
			},
			select: function(event, ui) {
				var terms = split(this.value);
				// remove the current input
				terms.pop();

				var selectedItem = ui.item.value;

				// add the selected item
				terms.push(ui.item.value);

				if(selectedItem === "tag:") {
					autocomplete = savedTags.concat(searchTypes);

				} else if(selectedItem === "logbook:") {
					autocomplete = savedLogbooks.concat(searchTypes);
				}

				// add placeholder to get the comma-and-space at the end
				terms.push("");
				this.value = terms.join(" ");
				return false;
			}
		});
}
<|endoftext|>


<commit_before>const Context = require('./context')

class Composer {
  constructor (...fns) {
    this.handler = Composer.compose(fns)
  }

  use (...fns) {
    this.handler = Composer.compose([this.handler, ...fns])
    return this
  }

  on (updateTypes, ...fns) {
    return this.use(Composer.mount(updateTypes, ...fns))
  }

  hears (triggers, ...fns) {
    return this.use(Composer.hears(triggers, ...fns))
  }

  command (commands, ...fns) {
    return this.use(Composer.command(commands, ...fns))
  }

  action (triggers, ...fns) {
    return this.use(Composer.action(triggers, ...fns))
  }

  inlineQuery (triggers, ...fns) {
    return this.use(Composer.inlineQuery(triggers, ...fns))
  }

  gameQuery (...fns) {
    return this.use(Composer.gameQuery(...fns))
  }

  drop (predicate) {
    return this.use(Composer.drop(predicate))
  }

  filter (predicate) {
    return this.use(Composer.filter(predicate))
  }

  entity (...args) {
    return this.use(Composer.entity(...args))
  }

  email (...args) {
    return this.use(Composer.email(...args))
  }

  url (...args) {
    return this.use(Composer.url(...args))
  }

  textLink (...args) {
    return this.use(Composer.textLink(...args))
  }

  textMention (...args) {
    return this.use(Composer.textMention(...args))
  }

  mention (...args) {
    return this.use(Composer.mention(...args))
  }

  phone (...args) {
    return this.use(Composer.phone(...args))
  }

  hashtag (...args) {
    return this.use(Composer.hashtag(...args))
  }

  cashtag (...args) {
    return this.use(Composer.cashtag(...args))
  }

  start (...fns) {
    return this.command('start', Composer.tap((ctx) => {
      ctx.startPayload = ctx.message.text.substring(7)
    }), ...fns)
  }

  help (...fns) {
    return this.command('help', ...fns)
  }

  settings (...fns) {
    return this.command('settings', ...fns)
  }

  middleware () {
    return this.handler
  }

  static reply (...args) {
    return (ctx) => ctx.reply(...args)
  }

  static fork (middleware) {
    const handler = Composer.unwrap(middleware)
    return (ctx, next) => {
      setImmediate(handler, ctx, Composer.safePassThru())
      return next(ctx)
    }
  }

  static tap (middleware) {
    const handler = Composer.unwrap(middleware)
    return (ctx, next) => Promise.resolve(handler(ctx, Composer.safePassThru())).then(() => next(ctx))
  }

  static passThru () {
    return (ctx, next) => next(ctx)
  }

  static safePassThru () {
    return (ctx, next) => typeof next === 'function' ? next(ctx) : Promise.resolve()
  }

  static lazy (factoryFn) {
    if (typeof factoryFn !== 'function') {
      throw new Error('Argument must be a function')
    }
    return (ctx, next) => Promise.resolve(factoryFn(ctx))
      .then((middleware) => Composer.unwrap(middleware)(ctx, next))
  }

  static log (logFn = console.log) {
    return Composer.fork((ctx) => logFn(JSON.stringify(ctx.update, null, 2)))
  }

  static branch (predicate, trueMiddleware, falseMiddleware) {
    if (typeof predicate !== 'function') {
      return predicate ? trueMiddleware : falseMiddleware
    }
    return Composer.lazy((ctx) => Promise.resolve(predicate(ctx))
      .then((value) => value ? trueMiddleware : falseMiddleware))
  }

  static optional (predicate, ...fns) {
    return Composer.branch(predicate, Composer.compose(fns), Composer.safePassThru())
  }

  static filter (predicate) {
    return Composer.branch(predicate, Composer.safePassThru(), () => { })
  }

  static drop (predicate) {
    return Composer.branch(predicate, () => { }, Composer.safePassThru())
  }

  static dispatch (routeFn, handlers) {
    return typeof routeFn === 'function'
      ? Composer.lazy((ctx) => Promise.resolve(routeFn(ctx)).then((value) => handlers[value]))
      : handlers[routeFn]
  }

  static mount (updateType, ...fns) {
    const updateTypes = normalizeTextArguments(updateType)
    const predicate = (ctx) => updateTypes.includes(ctx.updateType) || updateTypes.some((type) => ctx.updateSubTypes.includes(type))
    return Composer.optional(predicate, ...fns)
  }

  static entity (predicate, ...fns) {
    if (typeof predicate !== 'function') {
      const entityTypes = normalizeTextArguments(predicate)
      return Composer.entity(({ type }) => entityTypes.includes(type), ...fns)
    }
    return Composer.optional((ctx) => {
      const message = ctx.message || ctx.channelPost
      const entities = message && (message.entities || message.caption_entities)
      const text = message && (message.text || message.caption)
      return entities && entities.some((entity) =>
        predicate(entity, text.substring(entity.offset, entity.offset + entity.length), ctx)
      )
    }, ...fns)
  }

  static entityText (entityType, predicate, ...fns) {
    if (fns.length === 0) {
      return Array.isArray(predicate)
        ? Composer.entity(entityType, ...predicate)
        : Composer.entity(entityType, predicate)
    }
    const triggers = normalizeTriggers(predicate)
    return Composer.entity(({ type }, value, ctx) => {
      if (type !== entityType) {
        return false
      }
      for (const trigger of triggers) {
        ctx.match = trigger(value, ctx)
        if (ctx.match) {
          return true
        }
      }
    }, ...fns)
  }

  static email (email, ...fns) {
    return Composer.entityText('email', email, ...fns)
  }

  static phone (number, ...fns) {
    return Composer.entityText('phone_number', number, ...fns)
  }

  static url (url, ...fns) {
    return Composer.entityText('url', url, ...fns)
  }

  static textLink (link, ...fns) {
    return Composer.entityText('text_link', link, ...fns)
  }

  static textMention (mention, ...fns) {
    return Composer.entityText('text_mention', mention, ...fns)
  }

  static mention (mention, ...fns) {
    return Composer.entityText('mention', normalizeTextArguments(mention, '@'), ...fns)
  }

  static hashtag (hashtag, ...fns) {
    return Composer.entityText('hashtag', normalizeTextArguments(hashtag, '#'), ...fns)
  }

  static cashtag (cashtag, ...fns) {
    return Composer.entityText('cashtag', normalizeTextArguments(cashtag, '$'), ...fns)
  }

  static match (triggers, ...fns) {
    return Composer.optional((ctx) => {
      const text = (
        (ctx.message && (ctx.message.caption || ctx.message.text)) ||
        (ctx.callbackQuery && ctx.callbackQuery.data) ||
        (ctx.inlineQuery && ctx.inlineQuery.query)
      )
      for (const trigger of triggers) {
        ctx.match = trigger(text, ctx)
        if (ctx.match) {
          return true
        }
      }
    }, ...fns)
  }

  static hears (triggers, ...fns) {
    return Composer.mount('text', Composer.match(normalizeTriggers(triggers), ...fns))
  }

  static command (command, ...fns) {
    if (fns.length === 0) {
      return Composer.entity(['bot_command'], command)
    }
    const commands = normalizeTextArguments(command, '/')
    return Composer.mount('text', Composer.lazy((ctx) => {
      const groupCommands = ctx.me && (ctx.chat.type === 'group' || ctx.chat.type === 'supergroup')
        ? commands.map((command) => `${command}@${ctx.me}`)
        : []
      return Composer.entity(({ offset, type }, value) =>
        offset === 0 &&
        type === 'bot_command' &&
        (commands.includes(value) || groupCommands.includes(value))
      , ...fns)
    }))
  }

  static action (triggers, ...fns) {
    return Composer.mount('callback_query', Composer.match(normalizeTriggers(triggers), ...fns))
  }

  static inlineQuery (triggers, ...fns) {
    return Composer.mount('inline_query', Composer.match(normalizeTriggers(triggers), ...fns))
  }

  static acl (userId, ...fns) {
    if (typeof userId === 'function') {
      return Composer.optional(userId, ...fns)
    }
    const allowed = Array.isArray(userId) ? userId : [userId]
    return Composer.optional((ctx) => !ctx.from || allowed.includes(ctx.from.id), ...fns)
  }

  static memberStatus (status, ...fns) {
    const statuses = Array.isArray(status) ? status : [status]
    return Composer.optional((ctx) => ctx.message && ctx.getChatMember(ctx.message.from.id)
      .then(member => member && statuses.includes(member.status))
    , ...fns)
  }

  static admin (...fns) {
    return Composer.memberStatus(['administrator', 'creator'], ...fns)
  }

  static creator (...fns) {
    return Composer.memberStatus('creator', ...fns)
  }

  static chatType (type, ...fns) {
    const types = Array.isArray(type) ? type : [type]
    return Composer.optional((ctx) => ctx.chat && types.includes(ctx.chat.type), ...fns)
  }

  static privateChat (...fns) {
    return Composer.chatType('private', ...fns)
  }

  static groupChat (...fns) {
    return Composer.chatType(['group', 'supergroup'], ...fns)
  }

  static gameQuery (...fns) {
    return Composer.mount('callback_query', Composer.optional((ctx) => ctx.callbackQuery.game_short_name, ...fns))
  }

  static unwrap (handler) {
    if (!handler) {
      throw new Error('Handler is undefined')
    }
    return typeof handler.middleware === 'function'
      ? handler.middleware()
      : handler
  }

  static compose (middlewares) {
    if (!Array.isArray(middlewares)) {
      throw new Error('Middlewares must be an array')
    }
    if (middlewares.length === 0) {
      return Composer.safePassThru()
    }
    if (middlewares.length === 1) {
      return Composer.unwrap(middlewares[0])
    }
    return (ctx, next) => {
      let index = -1
      return execute(0, ctx)
      function execute (i, context) {
        if (!(context instanceof Context)) {
          return Promise.reject(new Error('next(ctx) called with invalid context'))
        }
        if (i <= index) {
          return Promise.reject(new Error('next() called multiple times'))
        }
        index = i
        const handler = middlewares[i] ? Composer.unwrap(middlewares[i]) : next
        if (!handler) {
          return Promise.resolve()
        }
        try {
          return Promise.resolve(
            handler(context, (ctx = context) => execute(i + 1, ctx))
          )
        } catch (err) {
          return Promise.reject(err)
        }
      }
    }
  }
}

function normalizeTriggers (triggers) {
  if (!Array.isArray(triggers)) {
    triggers = [triggers]
  }
  return triggers.map((trigger) => {
    if (!trigger) {
      throw new Error('Invalid trigger')
    }
    if (typeof trigger === 'function') {
      return trigger
    }
    if (trigger instanceof RegExp) {
      return (value) => {
        trigger.lastIndex = 0
        return trigger.exec(value || '')
      }
    }
    return (value) => trigger === value ? value : null
  })
}

function normalizeTextArguments (argument, prefix) {
  const args = Array.isArray(argument) ? argument : [argument]
  return args
    .filter(Boolean)
    .map((arg) => prefix && typeof arg === 'string' && !arg.startsWith(prefix) ? `${prefix}${arg}` : arg)
}

module.exports = Composer
<commit_msg>error handling middlewares<commit_after>const Context = require('./context')

class Composer {
  constructor (...fns) {
    this.handler = Composer.compose(fns)
  }

  use (...fns) {
    this.handler = Composer.compose([this.handler, ...fns])
    return this
  }

  on (updateTypes, ...fns) {
    return this.use(Composer.mount(updateTypes, ...fns))
  }

  hears (triggers, ...fns) {
    return this.use(Composer.hears(triggers, ...fns))
  }

  command (commands, ...fns) {
    return this.use(Composer.command(commands, ...fns))
  }

  action (triggers, ...fns) {
    return this.use(Composer.action(triggers, ...fns))
  }

  inlineQuery (triggers, ...fns) {
    return this.use(Composer.inlineQuery(triggers, ...fns))
  }

  gameQuery (...fns) {
    return this.use(Composer.gameQuery(...fns))
  }

  drop (predicate) {
    return this.use(Composer.drop(predicate))
  }

  filter (predicate) {
    return this.use(Composer.filter(predicate))
  }

  entity (...args) {
    return this.use(Composer.entity(...args))
  }

  email (...args) {
    return this.use(Composer.email(...args))
  }

  url (...args) {
    return this.use(Composer.url(...args))
  }

  textLink (...args) {
    return this.use(Composer.textLink(...args))
  }

  textMention (...args) {
    return this.use(Composer.textMention(...args))
  }

  mention (...args) {
    return this.use(Composer.mention(...args))
  }

  phone (...args) {
    return this.use(Composer.phone(...args))
  }

  hashtag (...args) {
    return this.use(Composer.hashtag(...args))
  }

  cashtag (...args) {
    return this.use(Composer.cashtag(...args))
  }

  start (...fns) {
    return this.command('start', Composer.tap((ctx) => {
      ctx.startPayload = ctx.message.text.substring(7)
    }), ...fns)
  }

  help (...fns) {
    return this.command('help', ...fns)
  }

  settings (...fns) {
    return this.command('settings', ...fns)
  }

  middleware () {
    return this.handler
  }

  static reply (...args) {
    return (ctx) => ctx.reply(...args)
  }

  static catchAll (...fns) {
    return Composer.hanleErrors((err) => {
      console.error()
      console.error((err.stack || err.toString()).replace(/^/gm, '  '))
      console.error()
    }, ...fns)
  }

  static catch (errorHandler, ...fns) {
    const handler = Composer.compose(fns)
    return (ctx, next) => Promise.resolve(handler(ctx, next))
      .catch((err) => errorHandler(err, ctx))
  }

  static fork (middleware) {
    const handler = Composer.unwrap(middleware)
    return (ctx, next) => {
      setImmediate(handler, ctx, Composer.safePassThru())
      return next(ctx)
    }
  }

  static tap (middleware) {
    const handler = Composer.unwrap(middleware)
    return (ctx, next) => Promise.resolve(handler(ctx, Composer.safePassThru())).then(() => next(ctx))
  }

  static passThru () {
    return (ctx, next) => next(ctx)
  }

  static safePassThru () {
    return (ctx, next) => typeof next === 'function' ? next(ctx) : Promise.resolve()
  }

  static lazy (factoryFn) {
    if (typeof factoryFn !== 'function') {
      throw new Error('Argument must be a function')
    }
    return (ctx, next) => Promise.resolve(factoryFn(ctx))
      .then((middleware) => Composer.unwrap(middleware)(ctx, next))
  }

  static log (logFn = console.log) {
    return Composer.fork((ctx) => logFn(JSON.stringify(ctx.update, null, 2)))
  }

  static branch (predicate, trueMiddleware, falseMiddleware) {
    if (typeof predicate !== 'function') {
      return predicate ? trueMiddleware : falseMiddleware
    }
    return Composer.lazy((ctx) => Promise.resolve(predicate(ctx))
      .then((value) => value ? trueMiddleware : falseMiddleware))
  }

  static optional (predicate, ...fns) {
    return Composer.branch(predicate, Composer.compose(fns), Composer.safePassThru())
  }

  static filter (predicate) {
    return Composer.branch(predicate, Composer.safePassThru(), () => { })
  }

  static drop (predicate) {
    return Composer.branch(predicate, () => { }, Composer.safePassThru())
  }

  static dispatch (routeFn, handlers) {
    return typeof routeFn === 'function'
      ? Composer.lazy((ctx) => Promise.resolve(routeFn(ctx)).then((value) => handlers[value]))
      : handlers[routeFn]
  }

  static mount (updateType, ...fns) {
    const updateTypes = normalizeTextArguments(updateType)
    const predicate = (ctx) => updateTypes.includes(ctx.updateType) || updateTypes.some((type) => ctx.updateSubTypes.includes(type))
    return Composer.optional(predicate, ...fns)
  }

  static entity (predicate, ...fns) {
    if (typeof predicate !== 'function') {
      const entityTypes = normalizeTextArguments(predicate)
      return Composer.entity(({ type }) => entityTypes.includes(type), ...fns)
    }
    return Composer.optional((ctx) => {
      const message = ctx.message || ctx.channelPost
      const entities = message && (message.entities || message.caption_entities)
      const text = message && (message.text || message.caption)
      return entities && entities.some((entity) =>
        predicate(entity, text.substring(entity.offset, entity.offset + entity.length), ctx)
      )
    }, ...fns)
  }

  static entityText (entityType, predicate, ...fns) {
    if (fns.length === 0) {
      return Array.isArray(predicate)
        ? Composer.entity(entityType, ...predicate)
        : Composer.entity(entityType, predicate)
    }
    const triggers = normalizeTriggers(predicate)
    return Composer.entity(({ type }, value, ctx) => {
      if (type !== entityType) {
        return false
      }
      for (const trigger of triggers) {
        ctx.match = trigger(value, ctx)
        if (ctx.match) {
          return true
        }
      }
    }, ...fns)
  }

  static email (email, ...fns) {
    return Composer.entityText('email', email, ...fns)
  }

  static phone (number, ...fns) {
    return Composer.entityText('phone_number', number, ...fns)
  }

  static url (url, ...fns) {
    return Composer.entityText('url', url, ...fns)
  }

  static textLink (link, ...fns) {
    return Composer.entityText('text_link', link, ...fns)
  }

  static textMention (mention, ...fns) {
    return Composer.entityText('text_mention', mention, ...fns)
  }

  static mention (mention, ...fns) {
    return Composer.entityText('mention', normalizeTextArguments(mention, '@'), ...fns)
  }

  static hashtag (hashtag, ...fns) {
    return Composer.entityText('hashtag', normalizeTextArguments(hashtag, '#'), ...fns)
  }

  static cashtag (cashtag, ...fns) {
    return Composer.entityText('cashtag', normalizeTextArguments(cashtag, '$'), ...fns)
  }

  static match (triggers, ...fns) {
    return Composer.optional((ctx) => {
      const text = (
        (ctx.message && (ctx.message.caption || ctx.message.text)) ||
        (ctx.callbackQuery && ctx.callbackQuery.data) ||
        (ctx.inlineQuery && ctx.inlineQuery.query)
      )
      for (const trigger of triggers) {
        ctx.match = trigger(text, ctx)
        if (ctx.match) {
          return true
        }
      }
    }, ...fns)
  }

  static hears (triggers, ...fns) {
    return Composer.mount('text', Composer.match(normalizeTriggers(triggers), ...fns))
  }

  static command (command, ...fns) {
    if (fns.length === 0) {
      return Composer.entity(['bot_command'], command)
    }
    const commands = normalizeTextArguments(command, '/')
    return Composer.mount('text', Composer.lazy((ctx) => {
      const groupCommands = ctx.me && (ctx.chat.type === 'group' || ctx.chat.type === 'supergroup')
        ? commands.map((command) => `${command}@${ctx.me}`)
        : []
      return Composer.entity(({ offset, type }, value) =>
        offset === 0 &&
        type === 'bot_command' &&
        (commands.includes(value) || groupCommands.includes(value))
      , ...fns)
    }))
  }

  static action (triggers, ...fns) {
    return Composer.mount('callback_query', Composer.match(normalizeTriggers(triggers), ...fns))
  }

  static inlineQuery (triggers, ...fns) {
    return Composer.mount('inline_query', Composer.match(normalizeTriggers(triggers), ...fns))
  }

  static acl (userId, ...fns) {
    if (typeof userId === 'function') {
      return Composer.optional(userId, ...fns)
    }
    const allowed = Array.isArray(userId) ? userId : [userId]
    return Composer.optional((ctx) => !ctx.from || allowed.includes(ctx.from.id), ...fns)
  }

  static memberStatus (status, ...fns) {
    const statuses = Array.isArray(status) ? status : [status]
    return Composer.optional((ctx) => ctx.message && ctx.getChatMember(ctx.message.from.id)
      .then(member => member && statuses.includes(member.status))
    , ...fns)
  }

  static admin (...fns) {
    return Composer.memberStatus(['administrator', 'creator'], ...fns)
  }

  static creator (...fns) {
    return Composer.memberStatus('creator', ...fns)
  }

  static chatType (type, ...fns) {
    const types = Array.isArray(type) ? type : [type]
    return Composer.optional((ctx) => ctx.chat && types.includes(ctx.chat.type), ...fns)
  }

  static privateChat (...fns) {
    return Composer.chatType('private', ...fns)
  }

  static groupChat (...fns) {
    return Composer.chatType(['group', 'supergroup'], ...fns)
  }

  static gameQuery (...fns) {
    return Composer.mount('callback_query', Composer.optional((ctx) => ctx.callbackQuery.game_short_name, ...fns))
  }

  static unwrap (handler) {
    if (!handler) {
      throw new Error('Handler is undefined')
    }
    return typeof handler.middleware === 'function'
      ? handler.middleware()
      : handler
  }

  static compose (middlewares) {
    if (!Array.isArray(middlewares)) {
      throw new Error('Middlewares must be an array')
    }
    if (middlewares.length === 0) {
      return Composer.safePassThru()
    }
    if (middlewares.length === 1) {
      return Composer.unwrap(middlewares[0])
    }
    return (ctx, next) => {
      let index = -1
      return execute(0, ctx)
      function execute (i, context) {
        if (!(context instanceof Context)) {
          return Promise.reject(new Error('next(ctx) called with invalid context'))
        }
        if (i <= index) {
          return Promise.reject(new Error('next() called multiple times'))
        }
        index = i
        const handler = middlewares[i] ? Composer.unwrap(middlewares[i]) : next
        if (!handler) {
          return Promise.resolve()
        }
        try {
          return Promise.resolve(
            handler(context, (ctx = context) => execute(i + 1, ctx))
          )
        } catch (err) {
          return Promise.reject(err)
        }
      }
    }
  }
}

function normalizeTriggers (triggers) {
  if (!Array.isArray(triggers)) {
    triggers = [triggers]
  }
  return triggers.map((trigger) => {
    if (!trigger) {
      throw new Error('Invalid trigger')
    }
    if (typeof trigger === 'function') {
      return trigger
    }
    if (trigger instanceof RegExp) {
      return (value) => {
        trigger.lastIndex = 0
        return trigger.exec(value || '')
      }
    }
    return (value) => trigger === value ? value : null
  })
}

function normalizeTextArguments (argument, prefix) {
  const args = Array.isArray(argument) ? argument : [argument]
  return args
    .filter(Boolean)
    .map((arg) => prefix && typeof arg === 'string' && !arg.startsWith(prefix) ? `${prefix}${arg}` : arg)
}

module.exports = Composer
<|endoftext|>


<commit_before>const Main = imports.ui.main;
const GLib = imports.gi.GLib;

const ExtensionUtils = imports.misc.extensionUtils;
const Me = ExtensionUtils.getCurrentExtension();
const Convenience = Me.imports.convenience;

var settings;

function init() {
    settings = Convenience.getSettings();
}

var fromCodePoint = function() {
    var MAX_SIZE = 0x4000;
    var codeUnits = [];
    var highSurrogate;
    var lowSurrogate;
    var index = -1;
    var length = arguments.length;
    if (!length) { return ''; }
    var result = '';
    while (++index < length) {
        var codePoint = Number(arguments[index]);
        if (
            !isFinite(codePoint) ||       // `NaN`, `+Infinity`, or `-Infinity`
            codePoint < 0 ||              // not a valid Unicode code point
            codePoint > 0x10FFFF ||       // not a valid Unicode code point
            Math.floor(codePoint) != codePoint // not an integer
        ) {
            throw RangeError('Invalid code point: ' + codePoint);
        }
        if (codePoint <= 0xFFFF) { // BMP code point
            codeUnits.push(codePoint);
        } else { // Astral code point; split in surrogate halves
            // http://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae
            codePoint -= 0x10000;
            highSurrogate = (codePoint >> 10) + 0xD800;
            lowSurrogate = (codePoint % 0x400) + 0xDC00;
            codeUnits.push(highSurrogate, lowSurrogate);
        }
        if (index + 1 == length || codeUnits.length > MAX_SIZE) {
            result += String.fromCharCode.apply(null, codeUnits);
            codeUnits.length = 0;
        }
    }
    return result;
};

function overrider(lbl) {
    var t = lbl.get_text();
    var FORMAT = settings.get_string("override-string");
    var desired = FORMAT;
    if (FORMAT.indexOf("%") > -1) {
        var now = GLib.DateTime.new_now_local();
        if (FORMAT.indexOf("%f") > -1) {
            var hour = now.get_hour();
            // convert from 0-23 to 1-12
            if (hour > 12) {
                hour -= 12;
            }
            if (hour == 0) {
                hour = 12;
            }
            var clockFaceCodePoint = 0x1f550 + (hour - 1);
            var minute = now.get_minute();
            if (minute >= 30) {
                clockFaceCodePoint += 12;
            }
            var repl;
            if (String.fromCodePoint) {
                repl = String.fromCodePoint(clockFaceCodePoint)
            } else {
                repl = fromCodePoint(clockFaceCodePoint);
            }
            desired = desired.replace("%f", repl);
        }
        desired = now.format(desired);
    }
    if (t != desired) {
        last = t;
        lbl.set_text(desired);
    }
}

var lbl, signalHandlerID, last = "";

function enable() {
    var sA = Main.panel._statusArea;
    if (!sA) { sA = Main.panel.statusArea; }

    if (!sA || !sA.dateMenu || !sA.dateMenu.actor) {
        print("Looks like Shell has changed where things live again; aborting.");
        return;
    }

    sA.dateMenu.actor.first_child.get_children().forEach(function(w) {
        // assume that the text label is the first StLabel we find.
        // This is dodgy behaviour but there's no reliable way to
        // work out which it is.
        if (w.get_text && !lbl) { lbl = w; }
    });
    if (!lbl) {
        print("Looks like Shell has changed where things live again; aborting.");
        return;
    }
    signalHandlerID = lbl.connect("notify::text", overrider);
    last = lbl.get_text();
    overrider(lbl);
}

function disable() {
    if (lbl && signalHandlerID) {
        lbl.disconnect(signalHandlerID);
        lbl.set_text(last);
    }
}
<commit_msg>Allow multiple `%f` instances in override format<commit_after>const Main = imports.ui.main;
const GLib = imports.gi.GLib;

const ExtensionUtils = imports.misc.extensionUtils;
const Me = ExtensionUtils.getCurrentExtension();
const Convenience = Me.imports.convenience;

var settings;

function init() {
    settings = Convenience.getSettings();
}

var fromCodePoint = function() {
    var MAX_SIZE = 0x4000;
    var codeUnits = [];
    var highSurrogate;
    var lowSurrogate;
    var index = -1;
    var length = arguments.length;
    if (!length) { return ''; }
    var result = '';
    while (++index < length) {
        var codePoint = Number(arguments[index]);
        if (
            !isFinite(codePoint) ||       // `NaN`, `+Infinity`, or `-Infinity`
            codePoint < 0 ||              // not a valid Unicode code point
            codePoint > 0x10FFFF ||       // not a valid Unicode code point
            Math.floor(codePoint) != codePoint // not an integer
        ) {
            throw RangeError('Invalid code point: ' + codePoint);
        }
        if (codePoint <= 0xFFFF) { // BMP code point
            codeUnits.push(codePoint);
        } else { // Astral code point; split in surrogate halves
            // http://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae
            codePoint -= 0x10000;
            highSurrogate = (codePoint >> 10) + 0xD800;
            lowSurrogate = (codePoint % 0x400) + 0xDC00;
            codeUnits.push(highSurrogate, lowSurrogate);
        }
        if (index + 1 == length || codeUnits.length > MAX_SIZE) {
            result += String.fromCharCode.apply(null, codeUnits);
            codeUnits.length = 0;
        }
    }
    return result;
};

function overrider(lbl) {
    var t = lbl.get_text();
    var FORMAT = settings.get_string("override-string");
    var desired = FORMAT;
    if (FORMAT.indexOf("%") > -1) {
        var now = GLib.DateTime.new_now_local();
        if (FORMAT.indexOf("%f") > -1) {
            var hour = now.get_hour();
            // convert from 0-23 to 1-12
            if (hour > 12) {
                hour -= 12;
            }
            if (hour == 0) {
                hour = 12;
            }
            var clockFaceCodePoint = 0x1f550 + (hour - 1);
            var minute = now.get_minute();
            if (minute >= 30) {
                clockFaceCodePoint += 12;
            }
            var repl;
            if (String.fromCodePoint) {
                repl = String.fromCodePoint(clockFaceCodePoint)
            } else {
                repl = fromCodePoint(clockFaceCodePoint);
            }
            desired = desired.replace(/%f/g, repl);
        }
        desired = now.format(desired);
    }
    if (t != desired) {
        last = t;
        lbl.set_text(desired);
    }
}

var lbl, signalHandlerID, last = "";

function enable() {
    var sA = Main.panel._statusArea;
    if (!sA) { sA = Main.panel.statusArea; }

    if (!sA || !sA.dateMenu || !sA.dateMenu.actor) {
        print("Looks like Shell has changed where things live again; aborting.");
        return;
    }

    sA.dateMenu.actor.first_child.get_children().forEach(function(w) {
        // assume that the text label is the first StLabel we find.
        // This is dodgy behaviour but there's no reliable way to
        // work out which it is.
        if (w.get_text && !lbl) { lbl = w; }
    });
    if (!lbl) {
        print("Looks like Shell has changed where things live again; aborting.");
        return;
    }
    signalHandlerID = lbl.connect("notify::text", overrider);
    last = lbl.get_text();
    overrider(lbl);
}

function disable() {
    if (lbl && signalHandlerID) {
        lbl.disconnect(signalHandlerID);
        lbl.set_text(last);
    }
}
<|endoftext|>


<commit_before>/*
 * GridGallery is a plugin that will generate a grid depending on
 * the elements width and height. it is not required to have the same height
 * also you can enable/disable show lightbox effect to load content
 *
 * license: under MIT
 *          https://github.com/pencilpix/grid-gallery/blob/master/LICENSE
 * Author: Mohamed Hassan
 * Author Url: mohamedhassan@me
 */
(function($doc, $win){
  'use strict';

  /*=========================
   * @private helper functions
   *=========================*/

   /*
    * _extend Private method that extends an object using
    * properties of another object.
    * @param { Object } obj1 The object that will be extended
    * @param { Object } obj2 The object that will be used to extend another.
    *
    * @return { Object } obj1 The first object after being extended.
    */
  function _extend(obj1, obj2) {
    for ( var prop in obj2 ) {
      if ( obj2.hasOwnProperty(prop) ) {
        obj1[prop] = obj2[prop];
      }
    }

    return obj1;
  }

  /*
   * _checkItems Private method that check if GridGallery enabled on items
   * @param { Array-like } items The grid elements.
   * @return { Array } notEnabled The grid items that not set yet.
   */
  function _checkItems(items) {
    var notEnabled = Array.prototype.map.call(items, function(item) {
      var pattern = new RegExp('grid-gallery__item');
      if(!item.dataset.grid && pattern.test(item.className)){
        return item;
      }
    });

    if (notEnabled.length){
      return notEnabled;
    }
  }

  /*
   * _enableItems Private method to handle un-enabled grid items
   * and give each element position and data-grid attribute.
   * @param { Array } items un-enabled elements.
   * @retrun { Array } enabled elements after update position
   * and data-grid.
   */
  function _enableItems(items) {
    var parentPos = items[0].parentNode.getBoundingClientRect();
    var rowItemsNo = Math.floor(parentPos.width / items[0].offsetWidth);
    var nextPos = [];
    var prevItem, parentHeight;
    var whiteSpace = parentPos.width - items[0].offsetWidth * 4;

    // enable elements and set each item position
    var enabled = items.map(function(item, index, ar) {
     if(index === 0) {
       item.style.top = 0;
       item.style.left = whiteSpace / 2 + 'px';

       nextPos[0] = {
         top: item.offsetHeight,
         left: 0
       }
     } else if(index < rowItemsNo) {
       prevItem = ar[index - 1];

       item.style.top = 0;
       item.style.left = prevItem.offsetLeft + prevItem.offsetWidth + 'px';

       nextPos[index] = {
         top: item.offsetHeight,
         left: item.offsetLeft
       };
     } else if(index % rowItemsNo <= nextPos.length){
       var posIndex = index % rowItemsNo;

       item.style.top = nextPos[posIndex].top + 'px';
       item.style.left = (posIndex) ? nextPos[posIndex].left + 'px' : (whiteSpace / 2) + 'px';

       nextPos[posIndex] = {
         top: item.offsetTop + item.offsetHeight,
         left: item.offsetLeft
       };
     }

     item.dataset.grid = true;
    });

    // set container height
    parentHeight = nextPos.reduce(function(prevPos, pos) {
      if(prevPos > pos.top) {
        return prevPos;
      } else {
        prevPos = pos.top;
        return prevPos;
      }
    }, nextPos[0].top);

    items[0].parentNode.style.height = parentHeight + 'px';
    return enabled;
  }

  /*
   * class constructor of GridGallery component
   * @param { HTMLElement } element The element that will be the container.
   * @param { Object } options The options to initialize the component.
   */
  function GridGallery(element, options) {
    var _defaultOptions = { lightBox: false };

    this.element = element;

    this.options = _extend({}, _defaultOptions); // extend options to default
    this.options = _extend(this.options, options); // extend default to custom

    this.init();
  }

  /*
   * init this method that do the rest of initialization
   * work and updates the dom.
   */
  GridGallery.prototype.init = function() {
    _enableItems(_checkItems(this.element.children))
  };

  $win.GridGallery = GridGallery; // makes the component globally exist.
})(document, window);

<commit_msg>fix the calculation of the whitespace<commit_after>/*
 * GridGallery is a plugin that will generate a grid depending on
 * the elements width and height. it is not required to have the same height
 * also you can enable/disable show lightbox effect to load content
 *
 * license: under MIT
 *          https://github.com/pencilpix/grid-gallery/blob/master/LICENSE
 * Author: Mohamed Hassan
 * Author Url: mohamedhassan@me
 */
(function($doc, $win){
  'use strict';

  /*=========================
   * @private helper functions
   *=========================*/

   /*
    * _extend Private method that extends an object using
    * properties of another object.
    * @param { Object } obj1 The object that will be extended
    * @param { Object } obj2 The object that will be used to extend another.
    *
    * @return { Object } obj1 The first object after being extended.
    */
  function _extend(obj1, obj2) {
    for ( var prop in obj2 ) {
      if ( obj2.hasOwnProperty(prop) ) {
        obj1[prop] = obj2[prop];
      }
    }

    return obj1;
  }

  /*
   * _checkItems Private method that check if GridGallery enabled on items
   * @param { Array-like } items The grid elements.
   * @return { Array } notEnabled The grid items that not set yet.
   */
  function _checkItems(items) {
    var notEnabled = Array.prototype.map.call(items, function(item) {
      var pattern = new RegExp('grid-gallery__item');
      if(!item.dataset.grid && pattern.test(item.className)){
        return item;
      }
    });

    if (notEnabled.length){
      return notEnabled;
    }
  }

  /*
   * _enableItems Private method to handle un-enabled grid items
   * and give each element position and data-grid attribute.
   * @param { Array } items un-enabled elements.
   * @retrun { Array } enabled elements after update position
   * and data-grid.
   */
  function _enableItems(items) {
    var parentPos = items[0].parentNode.getBoundingClientRect();
    var rowItemsNo = Math.floor(parentPos.width / items[0].offsetWidth);
    var nextPos = [];
    var prevItem, parentHeight;
    var whiteSpace = parentPos.width - items[0].offsetWidth * (Math.floor(parentPos.width / items[0].offsetWidth));

    // enable elements and set each item position
    var enabled = items.map(function(item, index, ar) {
     if(index === 0) {
       item.style.top = 0;
       item.style.left = whiteSpace / 2 + 'px';

       nextPos[0] = {
         top: item.offsetHeight,
         left: 0
       }
     } else if(index < rowItemsNo) {
       prevItem = ar[index - 1];

       item.style.top = 0;
       item.style.left = prevItem.offsetLeft + prevItem.offsetWidth + 'px';

       nextPos[index] = {
         top: item.offsetHeight,
         left: item.offsetLeft
       };
     } else if(index % rowItemsNo <= nextPos.length){
       var posIndex = index % rowItemsNo;

       item.style.top = nextPos[posIndex].top + 'px';
       item.style.left = (posIndex) ? nextPos[posIndex].left + 'px' : (whiteSpace / 2) + 'px';

       nextPos[posIndex] = {
         top: item.offsetTop + item.offsetHeight,
         left: item.offsetLeft
       };
     }

     item.dataset.grid = true;
    });

    // set container height
    parentHeight = nextPos.reduce(function(prevPos, pos) {
      if(prevPos > pos.top) {
        return prevPos;
      } else {
        prevPos = pos.top;
        return prevPos;
      }
    }, nextPos[0].top);

    items[0].parentNode.style.height = parentHeight + 'px';
    return enabled;
  }

  /*
   * class constructor of GridGallery component
   * @param { HTMLElement } element The element that will be the container.
   * @param { Object } options The options to initialize the component.
   */
  function GridGallery(element, options) {
    var _defaultOptions = { lightBox: false };

    this.element = element;

    this.options = _extend({}, _defaultOptions); // extend options to default
    this.options = _extend(this.options, options); // extend default to custom

    this.init();
  }

  /*
   * init this method that do the rest of initialization
   * work and updates the dom.
   */
  GridGallery.prototype.init = function() {
    _enableItems(_checkItems(this.element.children))
  };

  $win.GridGallery = GridGallery; // makes the component globally exist.
})(document, window);

<|endoftext|>


<commit_before>/**
 **  Copyright (C) 2014 3D Repo Ltd
 **
 **  This program 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.
 **
 **  This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
 **/

var UnityUtil;

(function() {
	"use strict";

    angular.module("3drepo")
        .factory("UnityUtil", UnityUtil);

	UnityUtil = function() {};

	var LoadingState = {
		VIEWER_READY : 1,  //Viewer has been loaded
		MODEL_LOADING : 2, //model information has been fetched, world offset determined, model starts loading
		MODEL_LOADED : 3 //Models
	};

	var readyPromise;
	var readyResolve;
	var loadedPromise;
	var loadedResolve;
	var loadingPromise;
	var loadingResolve;
	var screenshotPromises = [];
	var vpPromise = null;
	var objectStatusPromise = null;
	var loaded = false;
	var UNITY_GAME_OBJECT = "WebGLInterface";

	var SendMessage_vss, SendMessage_vssn, SendMessage_vsss;
	UnityUtil.prototype._SendMessage = function(gameObject, func, param)
	{
    	if (param === undefined) {
	      if (!SendMessage_vss)
    	    SendMessage_vss = Module.cwrap('SendMessage', 'void', ['string', 'string']);
	      SendMessage_vss(gameObject, func);
    	} else if (typeof param === "string") {
	      if (!SendMessage_vsss)
    	    SendMessage_vsss = Module.cwrap('SendMessageString', 'void', ['string', 'string', 'string']);
	      SendMessage_vsss(gameObject, func, param);
	    } else if (typeof param === "number") {
    	  if (!SendMessage_vssn)
	        SendMessage_vssn = Module.cwrap('SendMessageFloat', 'void', ['string', 'string', 'number']);
    	 SendMessage_vssn(gameObject, func, param);
	    } else
    	    throw "" + param + " is does not have a type which is supported by SendMessage.";
	}

	UnityUtil.prototype.onError = function(err, url, line){
		if(confirm("Your browser has failed to load 3D Repo. \nThis may due to insufficient memory.\nPlease ensure you are using a 64bit web browser (Chrome or FireFox for best results), reduce your memory usage and try again.\n\nIf you are unable to resolve this problem, please contact support@3drepo.org referencing the following:\n\n\"Error " + err + " occured at line " + line + "\"\n\n\nClick ok to refresh this page.\n"))
		{
			window.location.reload();
		}
		return true;
	}

	UnityUtil.prototype.onLoaded = function()
	{
		if(!loadedPromise)
		{
		   loadedPromise	= new Promise(function(resolve, reject)
			{
				loadedResolve = {resolve: resolve, reject: reject};
			}
			);
		}
		return loadedPromise;

	}

	UnityUtil.prototype.onLoading = function()
	{
		if(!loadingPromise)
		{
		   loadingPromise	= new Promise(function(resolve, reject)
			{
				loadingResolve = {resolve: resolve, reject: reject};
			}
			);
		}
		return loadingPromise;

	}

	UnityUtil.prototype.onReady = function()
	{
		if(!readyPromise)
		{
		   readyPromise	= new Promise(function(resolve, reject)
			{
				readyResolve = {resolve: resolve, reject: reject};
			}
			);
		}
		return readyPromise;

	}

	function toUnity(methodName, requireStatus, params)
	{
		if(requireStatus == LoadingState.MODEL_LOADED)
		{
			//Requires model to be loaded
			UnityUtil.onLoaded().then(function()
			{
				SendMessage(UNITY_GAME_OBJECT, methodName, params);

			});
		}
		else if(requireStatus == LoadingState.MODEL_LOADING)
		{
			//Requires model to be loading
			UnityUtil.onLoading().then(function()
			{
				SendMessage(UNITY_GAME_OBJECT, methodName, params);

			});
		}
		else
		{
			UnityUtil.onReady().then(function()
			{
				SendMessage(UNITY_GAME_OBJECT, methodName, params);

			});
		}

	}

	/*
	 * =============== FROM UNITY ====================
	 */

	UnityUtil.prototype.clipBroadcast = function(clipInfo)
	{
		if(UnityUtil.clipBroadcastCallback)
		{
			UnityUtil.clipBroadcastCallback(JSON.parse(clipInfo));
		}
	}

	UnityUtil.prototype.currentPointInfo = function(pointInfo)
	{
		var point = JSON.parse(pointInfo);
		if(UnityUtil.objectSelectedCallback)
			UnityUtil.objectSelectedCallback(point);
	}

	UnityUtil.prototype.doubleClicked = function(meshInfo)
	{
		var point = JSON.parse(meshInfo);
		UnityUtil.centreToPoint(point.model, point.meshID);
	}

	UnityUtil.prototype.loaded = function(bboxStr)
	{
		var res = {};
		res.bbox = JSON.parse(bboxStr);
		loadedResolve.resolve(res);
		loaded = true;
	}

	UnityUtil.prototype.loading = function(bboxStr)
	{

		loadingResolve.resolve();
	}

	UnityUtil.prototype.objectStatusBroadcast = function(nodeInfo)
	{
		console.log("nodeInfo: " + nodeInfo);
		objectStatusPromise.resolve(JSON.parse(nodeInfo));
		objectStatusPromise = null;

	}

	UnityUtil.prototype.pickPointAlert = function(pointInfo)
	{
		var point = JSON.parse(pointInfo);
		if(UnityUtil.pickPointCallback)
			UnityUtil.pickPointCallback(point);
	}

	UnityUtil.prototype.ready = function()
	{
		//Overwrite the Send Message function to make it run quicker
		//This shouldn't need to be done in the future when the
		//optimisation in added into unity.
		SendMessage = UnityUtil._SendMessage;
		readyResolve.resolve();
	}

	UnityUtil.prototype.screenshotReady = function(screenshot)
	{
		var ssJSON = JSON.parse(screenshot);

		screenshotPromises.forEach(function(promise)
				{
					promise.resolve(ssJSON.ssBytes);
				}
		);
		screenshotPromises = [];
	}

	UnityUtil.prototype.viewpointReturned = function(vpInfo)
	{
		if(vpPromise != null)
		{
			var viewpoint = JSON.parse(vpInfo);
			vpPromise.resolve(viewpoint);
			vpPromise = null;
		}

	}

	/*
	 * =============== TO UNITY ====================
	 */

	UnityUtil.prototype.centreToPoint = function(model, id)
	{
		var params = {};
		params.model = model;
		params.meshID = id
		toUnity("CentreToObject", LoadingState.MODEL_LOADING, JSON.stringify(params));
	}

	UnityUtil.prototype.changePinColour = function(id, colour)
	{
		var params =  {};
		params.color = colour;
		params.pinName = id;
		toUnity("ChangePinColor", LoadingState.MODEL_LOADING, JSON.stringify(params));
	}

	UnityUtil.prototype.clearHighlights = function()
	{
		toUnity("ClearHighlighting", LoadingState.MODEL_LOADED);
	}

	UnityUtil.prototype.disableClippingPlanes = function()
	{
		toUnity("DisableClip");
	}

	UnityUtil.prototype.dropPin = function(id, position, normal, colour)
	{
		var params = {};
		params.id = id;
		params.position = position;
		params.normal = normal;
		params.color = colour;
		toUnity("DropPin", LoadingState.MODEL_LOADING, JSON.stringify(params));

	}

	UnityUtil.prototype.getObjectsStatus = function(account, model, promise)
	{
		var nameSpace = "";
		if(account && model)
		{
			nameSpace = account + "."  + model;
		}
		if(objectStatusPromise)
		{
			objectStatusPromise.then(function(blah){
					_getObjectsStatus(nameSpace, promise);
				}
			);
		}
		else
			_getObjectsStatus(nameSpace, promise)

	}

	function _getObjectsStatus(nameSpace, promise)
	{
		objectStatusPromise = promise;
		toUnity("GetObjectsStatus", LoadingState.MODEL_LOADED, nameSpace);
	}

	UnityUtil.prototype.getPointInfo = function()
	{
		toUnity("GetPointInfo", false, 0);
	}

	UnityUtil.prototype.highlightObjects = function(account, model, idArr, color, toggleMode)
	{
		var params = {};
		params.database = account;
		params.model = model;
		params.ids = idArr;
		params.toggle = toggleMode;
		if(color)
			params.color = color;

		toUnity("HighlightObjects", LoadingState.MODEL_LOADED, JSON.stringify(params));
	}

	UnityUtil.prototype.loadModel  = function(account, model, branch, revision)
	{

		UnityUtil.reset();
		if(!loaded && loadedResolve)
		{
			//If the previous model is being loaded but hasn't finished yet
			loadedResolve.reject();
			loadingResolve.reject();
		}

		loadedPromise = null;
		loadedResolve = null;
		loadingPromise = null;
		loadingResolve = null;
		loaded  = false;
		var params = {};
		params.database = account;
		params.model = model;
		if(revision != "head")
			params.revID = revision;

		UnityUtil.onLoading(); //Initialise the promise
		toUnity("LoadModel", LoadingState.VIEWER_READY, JSON.stringify(params));

		return UnityUtil.onLoaded();
	}

	UnityUtil.prototype.removePin = function(id)
	{
		toUnity("RemovePin", LoadingState.MODEL_LOADING, id);
	}

	UnityUtil.prototype.reset = function()
	{
		toUnity("ClearCanvas", LoadingState.VIEWER_READY);
	}

	UnityUtil.prototype.resetCamera = function()
	{
		toUnity("ResetCamera", LoadingState.VIEWER_READY);
	}

	UnityUtil.prototype.requestScreenShot = function(promise)
	{
		screenshotPromises.push(promise);
		toUnity("RequestScreenShot", LoadingState.VIEWER_READY);
	}

	UnityUtil.prototype.requestViewpoint = function(account, model, promise)
	{
		if(vpPromise != null)
		{
			vpPromise.then(_requestViewpoint(account, model, promise));
		}
		else
		{
			_requestViewpoint(account, model, promise);
		}

	}

	function _requestViewpoint(account, model, promise)

	{
		var param = {};
		if(account && model)
		{
			param.namespace = account + "."  + model;
		}
		vpPromise = promise;
		toUnity("RequestViewpoint", LoadingState.MODEL_LOADING, JSON.stringify(param));
	}

	UnityUtil.prototype.setAPIHost = function(hostname)
	{
		toUnity("SetAPIHost", LoadingState.VIEWER_READY, hostname);
	}

	UnityUtil.prototype.setNavigation = function(navMode)
	{
		toUnity("SetNavMode",LoadingState.VIEWER_READY, navMode);
	}

	UnityUtil.prototype.setViewpoint = function(pos, up, forward, account, model)
	{
		var param = {};
		if(account && model)
		{
			param.nameSpace = account + "." + model;
		}

		param.position = pos;
		param.up = up;
		param.forward = forward;
		toUnity("SetViewpoint", LoadingState.MODEL_LOADING, JSON.stringify(param));

	}

	UnityUtil.prototype.toggleStats = function()
	{
		toUnity("ShowStats", LoadingState.VIEWER_READY);
	}

	UnityUtil.prototype.toggleVisibility = function(account, model, ids, visibility)
	{
		var param = {};
		if(account && model)
		{
			param.nameSpace = account + "." + model;
		}

		param.ids = ids;
		param.visible = visibility;
		toUnity("ToggleVisibility",LoadingState.MODEL_LOADED, JSON.stringify(param));

	}

	UnityUtil.prototype.updateClippingPlanes = function (clipPlane, requireBroadcast, account, model)
	{
		var param = {}
		param.clip = clipPlane;
		if(account && model)
		{
			param.nameSpace = account + "." + model;
		}
		param.requiresBroadcast = requireBroadcast;
		toUnity("UpdateClip", LoadingState.MODEL_LOADING, JSON.stringify(param));
	}

	UnityUtil = new UnityUtil();
}());

<commit_msg>Lets only throw an error if it's a memory allocation error<commit_after>/**
 **  Copyright (C) 2014 3D Repo Ltd
 **
 **  This program 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.
 **
 **  This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
 **/

var UnityUtil;

(function() {
	"use strict";

    angular.module("3drepo")
        .factory("UnityUtil", UnityUtil);

	UnityUtil = function() {};

	var LoadingState = {
		VIEWER_READY : 1,  //Viewer has been loaded
		MODEL_LOADING : 2, //model information has been fetched, world offset determined, model starts loading
		MODEL_LOADED : 3 //Models
	};

	var readyPromise;
	var readyResolve;
	var loadedPromise;
	var loadedResolve;
	var loadingPromise;
	var loadingResolve;
	var screenshotPromises = [];
	var vpPromise = null;
	var objectStatusPromise = null;
	var loaded = false;
	var UNITY_GAME_OBJECT = "WebGLInterface";

	var SendMessage_vss, SendMessage_vssn, SendMessage_vsss;
	UnityUtil.prototype._SendMessage = function(gameObject, func, param)
	{
    	if (param === undefined) {
	      if (!SendMessage_vss)
    	    SendMessage_vss = Module.cwrap('SendMessage', 'void', ['string', 'string']);
	      SendMessage_vss(gameObject, func);
    	} else if (typeof param === "string") {
	      if (!SendMessage_vsss)
    	    SendMessage_vsss = Module.cwrap('SendMessageString', 'void', ['string', 'string', 'string']);
	      SendMessage_vsss(gameObject, func, param);
	    } else if (typeof param === "number") {
    	  if (!SendMessage_vssn)
	        SendMessage_vssn = Module.cwrap('SendMessageFloat', 'void', ['string', 'string', 'number']);
    	 SendMessage_vssn(gameObject, func, param);
	    } else
    	    throw "" + param + " is does not have a type which is supported by SendMessage.";
	}

	UnityUtil.prototype.onError = function(err, url, line)
	{

		var conf = "Your browser has failed to load 3D Repo. \nThis may due to insufficient memory.\n"+
					"Please ensure you are using a 64bit web browser (Chrome or FireFox for best results), reduce your memory usage and try again." +
					"\n\nIf you are unable to resolve this problem, please contact support@3drepo.org referencing the following:\n\n\"Error " +
					err +
					" occured at line " +
					line +
					"\"\n\n\nClick ok to refresh this page.\n"

		if (err.indexOf("Array buffer allocation failed") !== -1)
		{
			if(confirm(conf))
			{
				window.location.reload();
			}
		}

		return true;
	}

	UnityUtil.prototype.onLoaded = function()
	{
		if(!loadedPromise)
		{
		   loadedPromise	= new Promise(function(resolve, reject)
			{
				loadedResolve = {resolve: resolve, reject: reject};
			}
			);
		}
		return loadedPromise;

	}

	UnityUtil.prototype.onLoading = function()
	{
		if(!loadingPromise)
		{
		   loadingPromise	= new Promise(function(resolve, reject)
			{
				loadingResolve = {resolve: resolve, reject: reject};
			}
			);
		}
		return loadingPromise;

	}

	UnityUtil.prototype.onReady = function()
	{
		if(!readyPromise)
		{
		   readyPromise	= new Promise(function(resolve, reject)
			{
				readyResolve = {resolve: resolve, reject: reject};
			}
			);
		}
		return readyPromise;

	}

	function toUnity(methodName, requireStatus, params)
	{
		if(requireStatus == LoadingState.MODEL_LOADED)
		{
			//Requires model to be loaded
			UnityUtil.onLoaded().then(function()
			{
				SendMessage(UNITY_GAME_OBJECT, methodName, params);

			});
		}
		else if(requireStatus == LoadingState.MODEL_LOADING)
		{
			//Requires model to be loading
			UnityUtil.onLoading().then(function()
			{
				SendMessage(UNITY_GAME_OBJECT, methodName, params);

			});
		}
		else
		{
			UnityUtil.onReady().then(function()
			{
				SendMessage(UNITY_GAME_OBJECT, methodName, params);

			});
		}

	}

	/*
	 * =============== FROM UNITY ====================
	 */

	UnityUtil.prototype.clipBroadcast = function(clipInfo)
	{
		if(UnityUtil.clipBroadcastCallback)
		{
			UnityUtil.clipBroadcastCallback(JSON.parse(clipInfo));
		}
	}

	UnityUtil.prototype.currentPointInfo = function(pointInfo)
	{
		var point = JSON.parse(pointInfo);
		if(UnityUtil.objectSelectedCallback)
			UnityUtil.objectSelectedCallback(point);
	}

	UnityUtil.prototype.doubleClicked = function(meshInfo)
	{
		var point = JSON.parse(meshInfo);
		UnityUtil.centreToPoint(point.model, point.meshID);
	}

	UnityUtil.prototype.loaded = function(bboxStr)
	{
		var res = {};
		res.bbox = JSON.parse(bboxStr);
		loadedResolve.resolve(res);
		loaded = true;
	}

	UnityUtil.prototype.loading = function(bboxStr)
	{

		loadingResolve.resolve();
	}

	UnityUtil.prototype.objectStatusBroadcast = function(nodeInfo)
	{
		console.log("nodeInfo: " + nodeInfo);
		objectStatusPromise.resolve(JSON.parse(nodeInfo));
		objectStatusPromise = null;

	}

	UnityUtil.prototype.pickPointAlert = function(pointInfo)
	{
		var point = JSON.parse(pointInfo);
		if(UnityUtil.pickPointCallback)
			UnityUtil.pickPointCallback(point);
	}

	UnityUtil.prototype.ready = function()
	{
		//Overwrite the Send Message function to make it run quicker
		//This shouldn't need to be done in the future when the
		//optimisation in added into unity.
		SendMessage = UnityUtil._SendMessage;
		readyResolve.resolve();
	}

	UnityUtil.prototype.screenshotReady = function(screenshot)
	{
		var ssJSON = JSON.parse(screenshot);

		screenshotPromises.forEach(function(promise)
				{
					promise.resolve(ssJSON.ssBytes);
				}
		);
		screenshotPromises = [];
	}

	UnityUtil.prototype.viewpointReturned = function(vpInfo)
	{
		if(vpPromise != null)
		{
			var viewpoint = JSON.parse(vpInfo);
			vpPromise.resolve(viewpoint);
			vpPromise = null;
		}

	}

	/*
	 * =============== TO UNITY ====================
	 */

	UnityUtil.prototype.centreToPoint = function(model, id)
	{
		var params = {};
		params.model = model;
		params.meshID = id
		toUnity("CentreToObject", LoadingState.MODEL_LOADING, JSON.stringify(params));
	}

	UnityUtil.prototype.changePinColour = function(id, colour)
	{
		var params =  {};
		params.color = colour;
		params.pinName = id;
		toUnity("ChangePinColor", LoadingState.MODEL_LOADING, JSON.stringify(params));
	}

	UnityUtil.prototype.clearHighlights = function()
	{
		toUnity("ClearHighlighting", LoadingState.MODEL_LOADED);
	}

	UnityUtil.prototype.disableClippingPlanes = function()
	{
		toUnity("DisableClip");
	}

	UnityUtil.prototype.dropPin = function(id, position, normal, colour)
	{
		var params = {};
		params.id = id;
		params.position = position;
		params.normal = normal;
		params.color = colour;
		toUnity("DropPin", LoadingState.MODEL_LOADING, JSON.stringify(params));

	}

	UnityUtil.prototype.getObjectsStatus = function(account, model, promise)
	{
		var nameSpace = "";
		if(account && model)
		{
			nameSpace = account + "."  + model;
		}
		if(objectStatusPromise)
		{
			objectStatusPromise.then(function(blah){
					_getObjectsStatus(nameSpace, promise);
				}
			);
		}
		else
			_getObjectsStatus(nameSpace, promise)

	}

	function _getObjectsStatus(nameSpace, promise)
	{
		objectStatusPromise = promise;
		toUnity("GetObjectsStatus", LoadingState.MODEL_LOADED, nameSpace);
	}

	UnityUtil.prototype.getPointInfo = function()
	{
		toUnity("GetPointInfo", false, 0);
	}

	UnityUtil.prototype.highlightObjects = function(account, model, idArr, color, toggleMode)
	{
		var params = {};
		params.database = account;
		params.model = model;
		params.ids = idArr;
		params.toggle = toggleMode;
		if(color)
			params.color = color;

		toUnity("HighlightObjects", LoadingState.MODEL_LOADED, JSON.stringify(params));
	}

	UnityUtil.prototype.loadModel  = function(account, model, branch, revision)
	{

		UnityUtil.reset();
		if(!loaded && loadedResolve)
		{
			//If the previous model is being loaded but hasn't finished yet
			loadedResolve.reject();
			loadingResolve.reject();
		}

		loadedPromise = null;
		loadedResolve = null;
		loadingPromise = null;
		loadingResolve = null;
		loaded  = false;
		var params = {};
		params.database = account;
		params.model = model;
		if(revision != "head")
			params.revID = revision;

		UnityUtil.onLoading(); //Initialise the promise
		toUnity("LoadModel", LoadingState.VIEWER_READY, JSON.stringify(params));

		return UnityUtil.onLoaded();
	}

	UnityUtil.prototype.removePin = function(id)
	{
		toUnity("RemovePin", LoadingState.MODEL_LOADING, id);
	}

	UnityUtil.prototype.reset = function()
	{
		toUnity("ClearCanvas", LoadingState.VIEWER_READY);
	}

	UnityUtil.prototype.resetCamera = function()
	{
		toUnity("ResetCamera", LoadingState.VIEWER_READY);
	}

	UnityUtil.prototype.requestScreenShot = function(promise)
	{
		screenshotPromises.push(promise);
		toUnity("RequestScreenShot", LoadingState.VIEWER_READY);
	}

	UnityUtil.prototype.requestViewpoint = function(account, model, promise)
	{
		if(vpPromise != null)
		{
			vpPromise.then(_requestViewpoint(account, model, promise));
		}
		else
		{
			_requestViewpoint(account, model, promise);
		}

	}

	function _requestViewpoint(account, model, promise)

	{
		var param = {};
		if(account && model)
		{
			param.namespace = account + "."  + model;
		}
		vpPromise = promise;
		toUnity("RequestViewpoint", LoadingState.MODEL_LOADING, JSON.stringify(param));
	}

	UnityUtil.prototype.setAPIHost = function(hostname)
	{
		toUnity("SetAPIHost", LoadingState.VIEWER_READY, hostname);
	}

	UnityUtil.prototype.setNavigation = function(navMode)
	{
		toUnity("SetNavMode",LoadingState.VIEWER_READY, navMode);
	}

	UnityUtil.prototype.setViewpoint = function(pos, up, forward, account, model)
	{
		var param = {};
		if(account && model)
		{
			param.nameSpace = account + "." + model;
		}

		param.position = pos;
		param.up = up;
		param.forward = forward;
		toUnity("SetViewpoint", LoadingState.MODEL_LOADING, JSON.stringify(param));

	}

	UnityUtil.prototype.toggleStats = function()
	{
		toUnity("ShowStats", LoadingState.VIEWER_READY);
	}

	UnityUtil.prototype.toggleVisibility = function(account, model, ids, visibility)
	{
		var param = {};
		if(account && model)
		{
			param.nameSpace = account + "." + model;
		}

		param.ids = ids;
		param.visible = visibility;
		toUnity("ToggleVisibility",LoadingState.MODEL_LOADED, JSON.stringify(param));

	}

	UnityUtil.prototype.updateClippingPlanes = function (clipPlane, requireBroadcast, account, model)
	{
		var param = {}
		param.clip = clipPlane;
		if(account && model)
		{
			param.nameSpace = account + "." + model;
		}
		param.requiresBroadcast = requireBroadcast;
		toUnity("UpdateClip", LoadingState.MODEL_LOADING, JSON.stringify(param));
	}

	UnityUtil = new UnityUtil();
}());

<|endoftext|>