<commit_before>'use strict';

var
  Promise = require('bluebird'),
  Deque = require('denque'),
  AuthController = require('./authController'),
  BulkController = require('./bulkController'),
  CollectionController = require('./collectionController'),
  DocumentController = require('./documentController'),
  IndexController = require('./indexController'),
  KuzzleError = require('kuzzle-common-objects').errors.KuzzleError,
  MemoryStorageController = require('./memoryStorageController'),
  RealtimeController = require('./realtimeController'),
  SecurityController = require('./securityController'),
  ServerController = require('./serverController'),
  BadRequestError = require('kuzzle-common-objects').errors.BadRequestError,
  UnauthorizedError = require('kuzzle-common-objects').errors.UnauthorizedError,
  ForbiddenError = require('kuzzle-common-objects').errors.ForbiddenError,
  ServiceUnavailableError = require('kuzzle-common-objects').errors.ServiceUnavailableError,
  Request = require('kuzzle-common-objects').Request;

const MIN_TIME_BEFORE_DUMP_PREV_ERROR = 60000;

class CacheItem {
  constructor(request, callback) {
    this.request = request;
    this.callback = callback;
  }
}

/**
 * @property {object} controllers
 * @param {Kuzzle} kuzzle
 */
function FunnelController (kuzzle) {
  this.overloaded = false;
  this.concurrentRequests = 0;
  this.controllers = {};
  this.requestsCache = new Deque();
  this.cachedItems = 0;
  this.lastOverloadTime = 0;
  this.overloadWarned = false;
  this.lastWarningTime = 0;

  this.lastDumpedErrors = {};

  // used only for core-dump analysis
  this.pendingRequests = {};

  // used only for core-dump analysis
  this.requestHistory = new Deque();
  this.historized = 0;

  this.init = function funnelInit () {
    this.controllers.auth = new AuthController(kuzzle);
    this.controllers.bulk = new BulkController(kuzzle);
    this.controllers.collection = new CollectionController(kuzzle);
    this.controllers.document = new DocumentController(kuzzle);
    this.controllers.index = new IndexController(kuzzle);
    this.controllers.memoryStorage = this.controllers.ms = new MemoryStorageController(kuzzle);
    this.controllers.realtime = new RealtimeController(kuzzle);
    this.controllers.security = new SecurityController(kuzzle);
    this.controllers.server = new ServerController(kuzzle);

    kuzzle.pluginsManager.injectControllers();
  };

  /**
   * Execute the right controller/action
   *
   * @param {Request} request
   * @param {Function} callback
   */
  this.execute = function funnelExecute (request, callback) {
    if (this.overloaded) {
      let now = Date.now();

      if ((this.cachedItems > kuzzle.config.server.warningRetainedRequestsLimit) && (this.lastWarningTime === 0 || this.lastWarningTime < now - 500)) {
        let overloadPercentage = Math.round(10000 * this.cachedItems / kuzzle.config.server.maxRetainedRequests) / 100;
        kuzzle.pluginsManager.trigger('core:overload', overloadPercentage);
        kuzzle.pluginsManager.trigger('log:warn', '[!WARNING!] Kuzzle overloaded: ' + overloadPercentage + '%. Delaying requests...');

        this.overloadWarned = true;
        this.lastWarningTime = now;
      }
    }

    // execute the request immediately if kuzzle is not overloaded
    if (this.concurrentRequests < kuzzle.config.server.maxConcurrentRequests) {
      this.concurrentRequests++;

      this.pendingRequests[request.id] = request;

      this.checkRights(request)
        .then(() => this.processRequest(request))
        .then(processResult => callback(null, processResult))
        .catch(err => {
          // JSON.stringify(new NativeError()) === '{}', i.e. Error, SyntaxError, TypeError, ReferenceError, etc.
          kuzzle.pluginsManager.trigger('log:error', err instanceof Error && !(err instanceof KuzzleError) ? err.stack : err);

          request.setError(err);
          callback(err, request);

          this.handleErrorDump(err);

          return null;
        })
        .finally(() => {
          this.concurrentRequests--;
          delete this.pendingRequests[request.id];
        });

      return null;
    }

    /*
     If kuzzle is overloaded, check the requests cache.
     There are two possibilities:

     1- the cache limit has not been reached: the request is cached
     and will be played as soon as the maxConcurrentRequests
     property allows it

     2- the number of cached requests is equal to the maxRetainedRequests property.
     The request is then discarded and an error is returned to the sender
     */
    if (this.cachedItems >= kuzzle.config.server.maxRetainedRequests) {
      const error = new ServiceUnavailableError('Request discarded: Kuzzle Server is temporarily overloaded');

      kuzzle.pluginsManager.trigger('log:error', error);
      request.setError(error);
      callback(error, request);
      return null;
    }

    this.cachedItems++;
    this.requestsCache.push(new CacheItem(request, callback));

    if (!this.overloaded) {
      this.overloaded = true;

      /*
       /!\ Invoking this function here with setTimeout() leads to V8 deoptimizing
       the entire funnelExecute method (as of node.js 6.9.1),
       because of an "out of bounds" heuristic error (caused by node's
       setTimeout code written in JS? this needs further investigations)

       We get better performances by keeping this method optimized by crankshaft
       even if this means executing this function once for nothing each
       time we start overload mode.
       */
      playCachedRequests(kuzzle, this);
    }
  };

  this.handleErrorDump = function funnelHandleErrorDump (err) {
    if (kuzzle.config.dump.enabled && kuzzle.config.dump.handledErrors.enabled) {
      setImmediate(() => {
        var
          lastSeen = Date.now(),
          request,
          errorType,
          errorMessage;

        errorType = typeof err === 'object' && err.name ? err.name : typeof err;

        if (kuzzle.config.dump.handledErrors.whitelist.indexOf(errorType) > -1) {
          request = new Request({
            controller: 'actions',
            action: 'dump',
            body: {}
          });

          // simplify error message to use it in folder dump name
          errorMessage = err.message;
          if (errorMessage.indexOf('\n') > -1) {
            errorMessage = errorMessage.split('\n')[0];
          }
          errorMessage = errorMessage.toLowerCase().replace(/[^a-zA-Z0-9-_]/g, '-').replace(/[-]+/g, '-').split('-');
          errorMessage = errorMessage.filter(value => value !== '').join('-');

          request.input.body.suffix = 'handled-'.concat(errorType.toLowerCase(), '-', errorMessage);

          if (!this.lastDumpedErrors[request.data.body.suffix] || this.lastDumpedErrors[request.data.body.suffix] < lastSeen - MIN_TIME_BEFORE_DUMP_PREV_ERROR) {
            kuzzle.cliController.actions.dump(request);
            this.lastDumpedErrors[request.data.body.suffix] = lastSeen;
          }
        }
      });
    }
  };

  /**
   * Checks if a user has the necessary rights to execute the action
   *
   * @param {Request} request
   * @return {Promise<Boolean>}
   */
  this.checkRights = function checkRights (request) {
    return kuzzle.repositories.token.verifyToken(request.input.jwt)
      .then(userToken => {
        request.context.token = userToken;

        return kuzzle.repositories.user.load(request.context.token.userId);
      })
      .then(user => {
        request.context.user = user;

        return user.isActionAllowed(request, kuzzle);
      })
      .then(isAllowed => {
        if (!isAllowed) {
          // anonymous user => we throw a 401 (Unauthorised) error
          if (request.context.token.userId === -1) {
            return Promise.reject(new UnauthorizedError(
              `Unauthorized action [${request.input.index}/${request.input.collection}/${request.input.controller}/${request.input.action}] for anonymous user`)
            );
          }
          // logged-in user with insufficient permissions => we throw a 403 (Forbidden) error
          return Promise.reject(new ForbiddenError(
            `Forbidden action [${request.input.index}/${request.input.collection}/${request.input.controller}/${request.input.action}] for user ${request.context.user._id}`)
          );
        }
      });
  };

  /**
   * Execute the request immediately
   * @param {KuzzleRequest} request
   * @return {Promise}
   */
  this.processRequest = function processRequest(request) {
    if (
      !this.controllers[request.input.controller]
      || !this.controllers[request.input.controller][request.input.action]
      || typeof this.controllers[request.input.controller][request.input.action] !== 'function'
    ) {
      return Promise.reject(new BadRequestError(`No corresponding action ${request.input.action} in controller ${request.input.controller}`));
    }

    kuzzle.statistics.startRequest(request);

    if (this.historized > kuzzle.config.server.maxRequestHistorySize) {
      this.requestHistory.shift();
    }
    else {
      this.historized++;
    }

    this.requestHistory.push(request);

    let
      beforeEvent = this.getEventName(request.input.controller, 'before', request.input.action),
      modifiedRequest;

    return kuzzle.pluginsManager.trigger(beforeEvent, request)
      .then(newRequest => {
        modifiedRequest = newRequest;

        return this.controllers[request.input.controller][request.input.action](newRequest);
      })
      .then(responseData => {
        let afterEvent = this.getEventName(request.input.controller, 'after', request.input.action);

        modifiedRequest.setResult(responseData, {status: request.status === 102 ? 200 : request.status});

        return kuzzle.pluginsManager.trigger(afterEvent, modifiedRequest);
      })
      .then(newRequest => {
        kuzzle.statistics.completedRequest(request);

        return newRequest;
      })
      .catch(error => {
        kuzzle.statistics.failedRequest(request);
        return Promise.reject(error);
      });
  };

  this.getEventName = function getEventName(controller, prefix, action) {
    var event = controller === 'memoryStorage' ? 'ms' : controller;

    return event + ':' + prefix + capitalizeFirstLetter(action);
  };
}

/**
 * Background task. Checks if there are any requests in cache, and replay them
 * if Kuzzle is not overloaded anymore,
 *
 * @param kuzzle
 * @param {FunnelController} funnel
 */
function playCachedRequests(kuzzle, funnel) {
  // If there is room to play bufferized requests, do it now. If not, retry later
  let quantityToInject = funnel.cachedItems > 0 ?
    Math.min(funnel.cachedItems, kuzzle.config.server.maxConcurrentRequests - funnel.concurrentRequests)
    : 0;

  for(let i = 0; i < quantityToInject; i++) {
    let cachedItem = funnel.requestsCache.shift();
    funnel.execute(cachedItem.request, cachedItem.callback);
  }

  funnel.cachedItems -= quantityToInject;

  if (funnel.cachedItems > 0) {
    setTimeout(() => playCachedRequests(kuzzle, funnel), 0);
  } else {
    let now = Date.now();
    // No request remaining in cache => stop the background task and return to normal behavior
    funnel.overloaded = false;

    if (funnel.overloadWarned && (funnel.lastOverloadTime === 0 || funnel.lastOverloadTime < now - 500)) {
      funnel.overloadWarned = false;
      kuzzle.pluginsManager.trigger('log:info', 'End of overloaded state. Resuming normal activity.');
      funnel.lastOverloadTime = now;
    }
  }
}

/**
 * @param {string} string
 * @returns {string}
 */
function capitalizeFirstLetter(string) {
  return string.charAt(0).toUpperCase() + string.slice(1);
}

module.exports = FunnelController;
<commit_msg>fix forbiden access error message<commit_after>'use strict';

var
  Promise = require('bluebird'),
  Deque = require('denque'),
  AuthController = require('./authController'),
  BulkController = require('./bulkController'),
  CollectionController = require('./collectionController'),
  DocumentController = require('./documentController'),
  IndexController = require('./indexController'),
  KuzzleError = require('kuzzle-common-objects').errors.KuzzleError,
  MemoryStorageController = require('./memoryStorageController'),
  RealtimeController = require('./realtimeController'),
  SecurityController = require('./securityController'),
  ServerController = require('./serverController'),
  BadRequestError = require('kuzzle-common-objects').errors.BadRequestError,
  UnauthorizedError = require('kuzzle-common-objects').errors.UnauthorizedError,
  ForbiddenError = require('kuzzle-common-objects').errors.ForbiddenError,
  ServiceUnavailableError = require('kuzzle-common-objects').errors.ServiceUnavailableError,
  Request = require('kuzzle-common-objects').Request;

const MIN_TIME_BEFORE_DUMP_PREV_ERROR = 60000;

class CacheItem {
  constructor(request, callback) {
    this.request = request;
    this.callback = callback;
  }
}

/**
 * @property {object} controllers
 * @param {Kuzzle} kuzzle
 */
function FunnelController (kuzzle) {
  this.overloaded = false;
  this.concurrentRequests = 0;
  this.controllers = {};
  this.requestsCache = new Deque();
  this.cachedItems = 0;
  this.lastOverloadTime = 0;
  this.overloadWarned = false;
  this.lastWarningTime = 0;

  this.lastDumpedErrors = {};

  // used only for core-dump analysis
  this.pendingRequests = {};

  // used only for core-dump analysis
  this.requestHistory = new Deque();
  this.historized = 0;

  this.init = function funnelInit () {
    this.controllers.auth = new AuthController(kuzzle);
    this.controllers.bulk = new BulkController(kuzzle);
    this.controllers.collection = new CollectionController(kuzzle);
    this.controllers.document = new DocumentController(kuzzle);
    this.controllers.index = new IndexController(kuzzle);
    this.controllers.memoryStorage = this.controllers.ms = new MemoryStorageController(kuzzle);
    this.controllers.realtime = new RealtimeController(kuzzle);
    this.controllers.security = new SecurityController(kuzzle);
    this.controllers.server = new ServerController(kuzzle);

    kuzzle.pluginsManager.injectControllers();
  };

  /**
   * Execute the right controller/action
   *
   * @param {Request} request
   * @param {Function} callback
   */
  this.execute = function funnelExecute (request, callback) {
    if (this.overloaded) {
      let now = Date.now();

      if ((this.cachedItems > kuzzle.config.server.warningRetainedRequestsLimit) && (this.lastWarningTime === 0 || this.lastWarningTime < now - 500)) {
        let overloadPercentage = Math.round(10000 * this.cachedItems / kuzzle.config.server.maxRetainedRequests) / 100;
        kuzzle.pluginsManager.trigger('core:overload', overloadPercentage);
        kuzzle.pluginsManager.trigger('log:warn', '[!WARNING!] Kuzzle overloaded: ' + overloadPercentage + '%. Delaying requests...');

        this.overloadWarned = true;
        this.lastWarningTime = now;
      }
    }

    // execute the request immediately if kuzzle is not overloaded
    if (this.concurrentRequests < kuzzle.config.server.maxConcurrentRequests) {
      this.concurrentRequests++;

      this.pendingRequests[request.id] = request;

      this.checkRights(request)
        .then(() => this.processRequest(request))
        .then(processResult => callback(null, processResult))
        .catch(err => {
          // JSON.stringify(new NativeError()) === '{}', i.e. Error, SyntaxError, TypeError, ReferenceError, etc.
          kuzzle.pluginsManager.trigger('log:error', err instanceof Error && !(err instanceof KuzzleError) ? err.stack : err);

          request.setError(err);
          callback(err, request);

          this.handleErrorDump(err);

          return null;
        })
        .finally(() => {
          this.concurrentRequests--;
          delete this.pendingRequests[request.id];
        });

      return null;
    }

    /*
     If kuzzle is overloaded, check the requests cache.
     There are two possibilities:

     1- the cache limit has not been reached: the request is cached
     and will be played as soon as the maxConcurrentRequests
     property allows it

     2- the number of cached requests is equal to the maxRetainedRequests property.
     The request is then discarded and an error is returned to the sender
     */
    if (this.cachedItems >= kuzzle.config.server.maxRetainedRequests) {
      const error = new ServiceUnavailableError('Request discarded: Kuzzle Server is temporarily overloaded');

      kuzzle.pluginsManager.trigger('log:error', error);
      request.setError(error);
      callback(error, request);
      return null;
    }

    this.cachedItems++;
    this.requestsCache.push(new CacheItem(request, callback));

    if (!this.overloaded) {
      this.overloaded = true;

      /*
       /!\ Invoking this function here with setTimeout() leads to V8 deoptimizing
       the entire funnelExecute method (as of node.js 6.9.1),
       because of an "out of bounds" heuristic error (caused by node's
       setTimeout code written in JS? this needs further investigations)

       We get better performances by keeping this method optimized by crankshaft
       even if this means executing this function once for nothing each
       time we start overload mode.
       */
      playCachedRequests(kuzzle, this);
    }
  };

  this.handleErrorDump = function funnelHandleErrorDump (err) {
    if (kuzzle.config.dump.enabled && kuzzle.config.dump.handledErrors.enabled) {
      setImmediate(() => {
        var
          lastSeen = Date.now(),
          request,
          errorType,
          errorMessage;

        errorType = typeof err === 'object' && err.name ? err.name : typeof err;

        if (kuzzle.config.dump.handledErrors.whitelist.indexOf(errorType) > -1) {
          request = new Request({
            controller: 'actions',
            action: 'dump',
            body: {}
          });

          // simplify error message to use it in folder dump name
          errorMessage = err.message;
          if (errorMessage.indexOf('\n') > -1) {
            errorMessage = errorMessage.split('\n')[0];
          }
          errorMessage = errorMessage.toLowerCase().replace(/[^a-zA-Z0-9-_]/g, '-').replace(/[-]+/g, '-').split('-');
          errorMessage = errorMessage.filter(value => value !== '').join('-');

          request.input.body.suffix = 'handled-'.concat(errorType.toLowerCase(), '-', errorMessage);

          if (!this.lastDumpedErrors[request.data.body.suffix] || this.lastDumpedErrors[request.data.body.suffix] < lastSeen - MIN_TIME_BEFORE_DUMP_PREV_ERROR) {
            kuzzle.cliController.actions.dump(request);
            this.lastDumpedErrors[request.data.body.suffix] = lastSeen;
          }
        }
      });
    }
  };

  /**
   * Checks if a user has the necessary rights to execute the action
   *
   * @param {Request} request
   * @return {Promise<Boolean>}
   */
  this.checkRights = function checkRights (request) {
    return kuzzle.repositories.token.verifyToken(request.input.jwt)
      .then(userToken => {
        request.context.token = userToken;

        return kuzzle.repositories.user.load(request.context.token.userId);
      })
      .then(user => {
        request.context.user = user;

        return user.isActionAllowed(request, kuzzle);
      })
      .then(isAllowed => {
        if (!isAllowed) {
          // anonymous user => we throw a 401 (Unauthorised) error
          if (request.context.token.userId === -1) {
            return Promise.reject(new UnauthorizedError(
              `Unauthorized action [${request.input.resource.index}/${request.input.resource.collection}/${request.input.controller}/${request.input.action}] for anonymous user`)
            );
          }
          // logged-in user with insufficient permissions => we throw a 403 (Forbidden) error
          return Promise.reject(new ForbiddenError(
            `Forbidden action [${request.input.resource.index}/${request.input.resource.collection}/${request.input.controller}/${request.input.action}] for user ${request.context.user._id}`)
          );
        }
      });
  };

  /**
   * Execute the request immediately
   * @param {KuzzleRequest} request
   * @return {Promise}
   */
  this.processRequest = function processRequest(request) {
    if (
      !this.controllers[request.input.controller]
      || !this.controllers[request.input.controller][request.input.action]
      || typeof this.controllers[request.input.controller][request.input.action] !== 'function'
    ) {
      return Promise.reject(new BadRequestError(`No corresponding action ${request.input.action} in controller ${request.input.controller}`));
    }

    kuzzle.statistics.startRequest(request);

    if (this.historized > kuzzle.config.server.maxRequestHistorySize) {
      this.requestHistory.shift();
    }
    else {
      this.historized++;
    }

    this.requestHistory.push(request);

    let
      beforeEvent = this.getEventName(request.input.controller, 'before', request.input.action),
      modifiedRequest;

    return kuzzle.pluginsManager.trigger(beforeEvent, request)
      .then(newRequest => {
        modifiedRequest = newRequest;

        return this.controllers[request.input.controller][request.input.action](newRequest);
      })
      .then(responseData => {
        let afterEvent = this.getEventName(request.input.controller, 'after', request.input.action);

        modifiedRequest.setResult(responseData, {status: request.status === 102 ? 200 : request.status});

        return kuzzle.pluginsManager.trigger(afterEvent, modifiedRequest);
      })
      .then(newRequest => {
        kuzzle.statistics.completedRequest(request);

        return newRequest;
      })
      .catch(error => {
        kuzzle.statistics.failedRequest(request);
        return Promise.reject(error);
      });
  };

  this.getEventName = function getEventName(controller, prefix, action) {
    var event = controller === 'memoryStorage' ? 'ms' : controller;

    return event + ':' + prefix + capitalizeFirstLetter(action);
  };
}

/**
 * Background task. Checks if there are any requests in cache, and replay them
 * if Kuzzle is not overloaded anymore,
 *
 * @param kuzzle
 * @param {FunnelController} funnel
 */
function playCachedRequests(kuzzle, funnel) {
  // If there is room to play bufferized requests, do it now. If not, retry later
  let quantityToInject = funnel.cachedItems > 0 ?
    Math.min(funnel.cachedItems, kuzzle.config.server.maxConcurrentRequests - funnel.concurrentRequests)
    : 0;

  for(let i = 0; i < quantityToInject; i++) {
    let cachedItem = funnel.requestsCache.shift();
    funnel.execute(cachedItem.request, cachedItem.callback);
  }

  funnel.cachedItems -= quantityToInject;

  if (funnel.cachedItems > 0) {
    setTimeout(() => playCachedRequests(kuzzle, funnel), 0);
  } else {
    let now = Date.now();
    // No request remaining in cache => stop the background task and return to normal behavior
    funnel.overloaded = false;

    if (funnel.overloadWarned && (funnel.lastOverloadTime === 0 || funnel.lastOverloadTime < now - 500)) {
      funnel.overloadWarned = false;
      kuzzle.pluginsManager.trigger('log:info', 'End of overloaded state. Resuming normal activity.');
      funnel.lastOverloadTime = now;
    }
  }
}

/**
 * @param {string} string
 * @returns {string}
 */
function capitalizeFirstLetter(string) {
  return string.charAt(0).toUpperCase() + string.slice(1);
}

module.exports = FunnelController;
<|endoftext|>


<commit_before>'use strict'

import * as CONSTANTS from '../constants/index'

import { MAX_ARTICLES_PER_FETCH, REQUEST_PAGE_START_FROM } from '../constants/author-page'
import { arrayOf, normalize } from 'normalizr'

import { InternalServerError } from '../lib/custom-error'
import { article as articleSchema } from '../schemas/index'
import { camelizeKeys } from 'humps'
import fetch from 'isomorphic-fetch'
import { formatUrl } from '../utils/index'
import get from 'lodash/get'
import { urlParasToString } from '../utils/url-paras-to-string'

const _ = {
  get
}

export function requestAuthorCollection(authorId) {
  return {
    type: CONSTANTS.FETCH_AUTHOR_COLLECTION_REQUEST,
    authorId
  }
}

export function failToReceiveAuthorCollection(error, failedAt) {
  return {
    type: CONSTANTS.FETCH_AUTHOR_COLLECTION_FAILURE,
    error,
    failedAt
  }
}

export function receiveAuthorCollection({ authorId, items, collectIndexList, currentPage, isFinish, totalResults, receivedAt }) {
  let receiveAuthorCollection = {
    type: CONSTANTS.FETCH_AUTHOR_COLLECTION_SUCCESS,
    authorId,
    response: items // objects {entities:{},result:[]}
  }
  if (typeof authorId === 'string') {
    receiveAuthorCollection[authorId] = {
      collectIndexList, //array
      currentPage,
      isFinish,
      totalResults,
      receivedAt
    }
  }
  return receiveAuthorCollection
}

export function fetchAuthorCollection({ targetPage = REQUEST_PAGE_START_FROM, authorId='' }) {
  return (dispatch, getState) => { // eslint-disable-line no-unused-vars
    const searchParas = {
      keywords: authorId,
      hitsPerPage: MAX_ARTICLES_PER_FETCH,
      page: targetPage
    }
    // Trans searchParas object to url parameters:
    let urlParasString = urlParasToString(searchParas)
    let url = formatUrl(`searchPosts?${urlParasString}`)
    dispatch(requestAuthorCollection(authorId))
    // Call our API server to fetch the data
    return fetch(url)
      .then((response) => {
        if (response.status >= 400) {
          throw new InternalServerError('Bad response from API, response:' + JSON.stringify(response))
        }
        return response.json()
      })
      .then((content) => {
        const hits = _.get(content, 'hits', {})
        const camelizedJson = camelizeKeys(hits)
        let items = normalize(camelizedJson, arrayOf(articleSchema))
        const collectIndexList = items.result
        const currentPage = content.page
        const isFinish = ( currentPage >= content.nbPages-1 )
        const receivedAt = Date.now()
        const totalResults = content.nbHits
        return dispatch(receiveAuthorCollection({ authorId, items, collectIndexList, currentPage, isFinish, totalResults, receivedAt }))
      },
      (error) => {
        let failedAt = Date.now()
        return dispatch(failToReceiveAuthorCollection(error, failedAt))
      })
  }
}

export function fetchAuthorCollectionIfNeeded(authorId) {
  return (dispatch, getState) => {
    const state = getState()
    const isFetching = _.get(state, [ 'author', authorId, 'isFetching' ], false)
    const isFinish = _.get(state, [ 'author', authorId, 'isFinish' ], false)
    let targetPage = _.get(state, [ 'author', authorId, 'currentPage' ], REQUEST_PAGE_START_FROM -1) + 1
    if(!isFetching && !isFinish) {
      return dispatch(fetchAuthorCollection({ targetPage, authorId }))
    }
  }
}
<commit_msg>Set default parameters<commit_after>'use strict'

import * as CONSTANTS from '../constants/index'

import { MAX_ARTICLES_PER_FETCH, REQUEST_PAGE_START_FROM } from '../constants/author-page'
import { arrayOf, normalize } from 'normalizr'

import { InternalServerError } from '../lib/custom-error'
import { article as articleSchema } from '../schemas/index'
import { camelizeKeys } from 'humps'
import fetch from 'isomorphic-fetch'
import { formatUrl } from '../utils/index'
import get from 'lodash/get'
import { urlParasToString } from '../utils/url-paras-to-string'

const _ = {
  get
}

export function requestAuthorCollection(authorId) {
  return {
    type: CONSTANTS.FETCH_AUTHOR_COLLECTION_REQUEST,
    authorId
  }
}

export function failToReceiveAuthorCollection(error, failedAt) {
  return {
    type: CONSTANTS.FETCH_AUTHOR_COLLECTION_FAILURE,
    error,
    failedAt
  }
}

export function receiveAuthorCollection({ authorId, items, collectIndexList, currentPage, isFinish, totalResults, receivedAt } = {}) {
  let receiveAuthorCollection = {
    type: CONSTANTS.FETCH_AUTHOR_COLLECTION_SUCCESS,
    authorId,
    response: items // objects {entities:{},result:[]}
  }
  if (typeof authorId === 'string') {
    receiveAuthorCollection[authorId] = {
      collectIndexList, //array
      currentPage,
      isFinish,
      totalResults,
      receivedAt
    }
  }
  return receiveAuthorCollection
}

export function fetchAuthorCollection({ targetPage = REQUEST_PAGE_START_FROM, authorId='' } = {}) {
  return (dispatch, getState) => { // eslint-disable-line no-unused-vars
    const searchParas = {
      keywords: authorId,
      hitsPerPage: MAX_ARTICLES_PER_FETCH,
      page: targetPage
    }
    // Trans searchParas object to url parameters:
    let urlParasString = urlParasToString(searchParas)
    let url = formatUrl(`searchPosts?${urlParasString}`)
    dispatch(requestAuthorCollection(authorId))
    // Call our API server to fetch the data
    return fetch(url)
      .then((response) => {
        if (response.status >= 400) {
          throw new InternalServerError('Bad response from API, response:' + JSON.stringify(response))
        }
        return response.json()
      })
      .then((content) => {
        const hits = _.get(content, 'hits', {})
        const camelizedJson = camelizeKeys(hits)
        let items = normalize(camelizedJson, arrayOf(articleSchema))
        const collectIndexList = items.result
        const currentPage = content.page
        const isFinish = ( currentPage >= content.nbPages-1 )
        const receivedAt = Date.now()
        const totalResults = content.nbHits
        return dispatch(receiveAuthorCollection({ authorId, items, collectIndexList, currentPage, isFinish, totalResults, receivedAt }))
      },
      (error) => {
        let failedAt = Date.now()
        return dispatch(failToReceiveAuthorCollection(error, failedAt))
      })
  }
}

export function fetchAuthorCollectionIfNeeded(authorId) {
  return (dispatch, getState) => {
    const state = getState()
    const isFetching = _.get(state, [ 'author', authorId, 'isFetching' ], false)
    const isFinish = _.get(state, [ 'author', authorId, 'isFinish' ], false)
    let targetPage = _.get(state, [ 'author', authorId, 'currentPage' ], REQUEST_PAGE_START_FROM -1) + 1
    if(!isFetching && !isFinish) {
      return dispatch(fetchAuthorCollection({ targetPage, authorId }))
    }
  }
}
<|endoftext|>


<commit_before>// @require: OpenLayers/Control/PanZoom.js

//
// default zoom/pan controls
//
OpenLayers.Control.PanZoomBar = Class.create();
OpenLayers.Control.PanZoomBar.X = 4;
OpenLayers.Control.PanZoomBar.Y = 4;
OpenLayers.Control.PanZoomBar.prototype =
  Object.extend( new OpenLayers.Control.PanZoom(), {
    /** @type Array(...) */
    buttons: null,

    /** @type int */
    zoomStopWidth: 18,

    /** @type int */
    zoomStopHeight: 11,

    initialize: function() {
        OpenLayers.Control.PanZoom.prototype.initialize.apply(this, arguments);
        this.position = new OpenLayers.Pixel(OpenLayers.Control.PanZoomBar.X,
                                             OpenLayers.Control.PanZoomBar.Y);
    },

    /**
    * @param {OpenLayers.Pixel} px
    */
    draw: function(px) {
        // initialize our internal div
        OpenLayers.Control.prototype.draw.apply(this, arguments);
        px = this.position;

        // place the controls
        this.buttons = new Array();

        var sz = new OpenLayers.Size(18,18);
        var centered = new OpenLayers.Pixel(px.x+sz.w/2, px.y);

        this._addButton("panup", "north-mini.png", centered, sz);
        px.y = centered.y+sz.h;
        this._addButton("panleft", "west-mini.png", px, sz);
        this._addButton("panright", "east-mini.png", px.add(sz.w, 0), sz);
        this._addButton("pandown", "south-mini.png", centered.add(0, sz.h*2), sz);
        this._addButton("zoomin", "zoom-plus-mini.png", centered.add(0, sz.h*3), sz);
        centered = centered.add(0, sz.h*4);
        centered = this._addZoomBar(centered,sz.copyOf());
        this._addButton("zoomout", "zoom-minus-mini.png", centered, sz);
        return this.div;
    },
    _addZoomBar:function(centered,sz) {
        var imgLocation = OpenLayers.Util.getImagesLocation();

        var zoomStopSize = this.zoomStopHeight;
        var slider = OpenLayers.Util.createAlphaImageDiv(imgLocation+"slider.png",
                       new OpenLayers.Pixel(20,9),
                       centered.add(-1, (this.map.getZoomLevels())*zoomStopSize), "absolute",
                       "OpenLayers_Control_PanZoomBar_Slider" + this.map.id);
        slider.style.zIndex = this.div.zIndex + 5;
        this.slider = slider;

        this.sliderEvents = new OpenLayers.Events(this, slider);
        this.sliderEvents.register("mousedown", this, this.zoomBarDown);
        this.sliderEvents.register("mousemove", this, this.zoomBarDrag);
        this.sliderEvents.register("mouseup", this, this.zoomBarUp);
        this.sliderEvents.register("dblclick", this, this.doubleClick);

        sz.h = zoomStopSize*(this.map.getZoomLevels()+1);
        sz.w = this.zoomStopWidth;
        var div = null
        if ((document.body.filters && (version >= 5.5) && (version < 7))) {
            div = OpenLayers.Util.createAlphaImageDiv(imgLocation+"zoombar.png",
                new OpenLayers.Size(sz.w, zoomStopSize),
                centered, "absolute", "OpenLayers_Control_PanZoomBar" + this.map.id);
            div.style.height = sz.h;
        } else {
            div = OpenLayers.Util.createDiv(
                'OpenLayers_Control_PanZoomBar_Zoombar' + this.map.id,
                centered,
                sz);
            div.style.backgroundImage = "url("+imgLocation+"zoombar.png)";
        }
        this.divEvents = new OpenLayers.Events(this, div);
        this.divEvents.register("mousedown", this, this.divClick);
        this.divEvents.register("mousemove", this, this.zoomBarDivDrag);
        this.divEvents.register("dblclick", this, this.doubleClick);

        this.div.appendChild(div);

        this.startTop = parseInt(div.style.top);
        this.div.appendChild(slider);

        this.map.events.register("zoomend", this, this.moveZoomBar);

        centered = centered.add(0, zoomStopSize*(this.map.getZoomLevels()+1));
        return centered;
    },
    divClick: function (evt) {
        var y = evt.xy.y;
        var top = Position.page(evt.object)[1];
        var levels = Math.floor((y - top)/this.zoomStopHeight);
        this.map.zoomTo(this.map.getZoomLevels() - levels);
        Event.stop(evt);
    },
    zoomBarDown:function(evt) {
        this.mouseDragStart = evt.xy.copyOf();
        this.zoomStart = evt.xy.copyOf();
        this.div.style.cursor = "move";
        Event.stop(evt);
    },
    zoomBarDivDrag: function(evt) {
        this.sliderEvents.handleBrowserEvent(evt);
    },
    zoomBarDrag:function(evt) {
        if (this.mouseDragStart != null) {
            var deltaY = this.mouseDragStart.y - evt.xy.y
            this.slider.style.top = (parseInt(this.slider.style.top)-deltaY)+"px";
            this.mouseDragStart = evt.xy.copyOf();
        }
    },
    zoomBarUp:function(evt) {
        this.div.style.cursor="default";
        var deltaY = this.zoomStart.y - evt.xy.y
        this.map.zoomTo(this.map.zoom + Math.round(deltaY/this.zoomStopHeight));
        this.moveZoomBar();
        this.mouseDragStart = null;
        Event.stop(evt);
    },
    moveZoomBar:function() {
        var newTop =
            (this.map.getZoomLevels() - this.map.getZoom()) * this.zoomStopHeight
            + this.startTop + 1;
        this.slider.style.top = newTop + "px";
    },

    destroy: function() {
        OpenLayers.Control.PanZoom.prototype.destroy.apply(this, arguments);
    }
});
<commit_msg>Fix a swap Pixel->Size from r363<commit_after>// @require: OpenLayers/Control/PanZoom.js

//
// default zoom/pan controls
//
OpenLayers.Control.PanZoomBar = Class.create();
OpenLayers.Control.PanZoomBar.X = 4;
OpenLayers.Control.PanZoomBar.Y = 4;
OpenLayers.Control.PanZoomBar.prototype =
  Object.extend( new OpenLayers.Control.PanZoom(), {
    /** @type Array(...) */
    buttons: null,

    /** @type int */
    zoomStopWidth: 18,

    /** @type int */
    zoomStopHeight: 11,

    initialize: function() {
        OpenLayers.Control.PanZoom.prototype.initialize.apply(this, arguments);
        this.position = new OpenLayers.Pixel(OpenLayers.Control.PanZoomBar.X,
                                             OpenLayers.Control.PanZoomBar.Y);
    },

    /**
    * @param {OpenLayers.Pixel} px
    */
    draw: function(px) {
        // initialize our internal div
        OpenLayers.Control.prototype.draw.apply(this, arguments);
        px = this.position;

        // place the controls
        this.buttons = new Array();

        var sz = new OpenLayers.Size(18,18);
        var centered = new OpenLayers.Pixel(px.x+sz.w/2, px.y);

        this._addButton("panup", "north-mini.png", centered, sz);
        px.y = centered.y+sz.h;
        this._addButton("panleft", "west-mini.png", px, sz);
        this._addButton("panright", "east-mini.png", px.add(sz.w, 0), sz);
        this._addButton("pandown", "south-mini.png", centered.add(0, sz.h*2), sz);
        this._addButton("zoomin", "zoom-plus-mini.png", centered.add(0, sz.h*3), sz);
        centered = centered.add(0, sz.h*4);
        centered = this._addZoomBar(centered,sz.copyOf());
        this._addButton("zoomout", "zoom-minus-mini.png", centered, sz);
        return this.div;
    },
    _addZoomBar:function(centered,sz) {
        var imgLocation = OpenLayers.Util.getImagesLocation();

        var zoomStopSize = this.zoomStopHeight;
        var slider = OpenLayers.Util.createAlphaImageDiv(imgLocation+"slider.png",
                       new OpenLayers.Size(20,9),
                       centered.add(-1, (this.map.getZoomLevels())*zoomStopSize), "absolute",
                       "OpenLayers_Control_PanZoomBar_Slider" + this.map.id);
        slider.style.zIndex = this.div.zIndex + 5;
        this.slider = slider;

        this.sliderEvents = new OpenLayers.Events(this, slider);
        this.sliderEvents.register("mousedown", this, this.zoomBarDown);
        this.sliderEvents.register("mousemove", this, this.zoomBarDrag);
        this.sliderEvents.register("mouseup", this, this.zoomBarUp);
        this.sliderEvents.register("dblclick", this, this.doubleClick);

        sz.h = zoomStopSize*(this.map.getZoomLevels()+1);
        sz.w = this.zoomStopWidth;
        var div = null
        if ((document.body.filters && (version >= 5.5) && (version < 7))) {
            div = OpenLayers.Util.createAlphaImageDiv(imgLocation+"zoombar.png",
                new OpenLayers.Size(sz.w, zoomStopSize),
                centered, "absolute", "OpenLayers_Control_PanZoomBar" + this.map.id);
            div.style.height = sz.h;
        } else {
            div = OpenLayers.Util.createDiv(
                'OpenLayers_Control_PanZoomBar_Zoombar' + this.map.id,
                centered,
                sz);
            div.style.backgroundImage = "url("+imgLocation+"zoombar.png)";
        }
        this.divEvents = new OpenLayers.Events(this, div);
        this.divEvents.register("mousedown", this, this.divClick);
        this.divEvents.register("mousemove", this, this.zoomBarDivDrag);
        this.divEvents.register("dblclick", this, this.doubleClick);

        this.div.appendChild(div);

        this.startTop = parseInt(div.style.top);
        this.div.appendChild(slider);

        this.map.events.register("zoomend", this, this.moveZoomBar);

        centered = centered.add(0, zoomStopSize*(this.map.getZoomLevels()+1));
        return centered;
    },
    divClick: function (evt) {
        var y = evt.xy.y;
        var top = Position.page(evt.object)[1];
        var levels = Math.floor((y - top)/this.zoomStopHeight);
        this.map.zoomTo(this.map.getZoomLevels() - levels);
        Event.stop(evt);
    },
    zoomBarDown:function(evt) {
        this.mouseDragStart = evt.xy.copyOf();
        this.zoomStart = evt.xy.copyOf();
        this.div.style.cursor = "move";
        Event.stop(evt);
    },
    zoomBarDivDrag: function(evt) {
        this.sliderEvents.handleBrowserEvent(evt);
    },
    zoomBarDrag:function(evt) {
        if (this.mouseDragStart != null) {
            var deltaY = this.mouseDragStart.y - evt.xy.y
            this.slider.style.top = (parseInt(this.slider.style.top)-deltaY)+"px";
            this.mouseDragStart = evt.xy.copyOf();
        }
    },
    zoomBarUp:function(evt) {
        this.div.style.cursor="default";
        var deltaY = this.zoomStart.y - evt.xy.y
        this.map.zoomTo(this.map.zoom + Math.round(deltaY/this.zoomStopHeight));
        this.moveZoomBar();
        this.mouseDragStart = null;
        Event.stop(evt);
    },
    moveZoomBar:function() {
        var newTop =
            (this.map.getZoomLevels() - this.map.getZoom()) * this.zoomStopHeight
            + this.startTop + 1;
        this.slider.style.top = newTop + "px";
    },

    destroy: function() {
        OpenLayers.Control.PanZoom.prototype.destroy.apply(this, arguments);
    }
});
<|endoftext|>


<commit_before>export const REQUEST_EVENTS = 'REQUEST_EVENTS'
export const requestEvents = () => {
  return {
    type: REQUEST_EVENTS,
    status: 'Loading events'
  }
}

export const RECEIVE_EVENTS = 'RECEIVE_EVENTS'
export const receiveEvents = (tripEvents) => {
  return {
    type: RECEIVE_EVENTS,
    tripEvents
  }
}

export const WILL_DELETE_EVENT = 'WILL_DELETE_EVENT'
export const deletingEvent = () => {
  return {
    type: WILL_DELETE_EVENT,
    status: 'deleting event'
  }
}

export const DELETED_EVENT = 'DELETED_EVENT'
export const deletedEvent = (id) => {
  return {
    type: DELETED_EVENT,
    deletedEventID: id
  }
}

export function fetchEvents(trip_id) {
  return function (dispatch) {
    dispatch(requestEvents())

    return fetch(`http://localhost:8080/api/users/1/trips/${trip_id}/events`)
      .then(response => response.json())
      .then(response => {
        dispatch(receiveEvents(response))
      })
  }
}

export function toDeleteEvent(id) {
  return function (dispatch) {
    dispatch(deletingEvent)
    console.log('hit deleteEvents function, will be fetching')

    return fetch(`http://localhost:8080/api/users/delete-events/${id}`)
      .then(response => response.json())
      .then(response => {
        dispatch(deletedEvent(JSON.parse(response)))
      })
  }
}<commit_msg>fixed fetch events<commit_after>export const REQUEST_EVENTS = 'REQUEST_EVENTS'
export const requestEvents = () => {
  return {
    type: REQUEST_EVENTS,
    status: 'Loading events'
  }
}

export const RECEIVE_EVENTS = 'RECEIVE_EVENTS'
export const receiveEvents = (tripEvents) => {
  return {
    type: RECEIVE_EVENTS,
    tripEvents
  }
}

export const WILL_DELETE_EVENT = 'WILL_DELETE_EVENT'
export const deletingEvent = () => {
  return {
    type: WILL_DELETE_EVENT,
    status: 'deleting event'
  }
}

export const DELETED_EVENT = 'DELETED_EVENT'
export const deletedEvent = (id) => {
  return {
    type: DELETED_EVENT,
    deletedEventID: id
  }
}

export function fetchEvents(trip_id) {
  return function (dispatch) {
    dispatch(requestEvents())

    return fetch(`http://localhost:8080/api/users/${localStorage.getItem('user_id')}/trips/${trip_id}/events`)
      .then(response => response.json())
      .then(response => {
        dispatch(receiveEvents(response))
      })
  }
}

export function toDeleteEvent(id) {
  return function (dispatch) {
    dispatch(deletingEvent)
    console.log('hit deleteEvents function, will be fetching')

    return fetch(`http://localhost:8080/api/users/delete-events/${id}`)
      .then(response => response.json())
      .then(response => {
        dispatch(deletedEvent(JSON.parse(response)))
      })
  }
}
<|endoftext|>


<commit_before>// require ./main

var
    esPhinx;

(function ($) {
    "use strict";

    var
        nodes = [],
        mappedListeners = {},

        mapped = function (node) {
            var
                index = nodes.indexPerEquivalence(node);

            if (index) {
                return true;
            }

            return false;
        },

        mapEventListener = function (node, eventName, callback) {
            var
                map = {},
                index;

            map[eventName] = new Array(callback);
            if (callback) {
              index = nodes.indexPerEquivalence(node);
                if (index) {
                    if (mappedListeners[index][eventName]) {
                        mappedListeners[index][eventName].push(callback);
                    } else {
                        mappedListeners[index][eventName] = map[eventName];
                    }
                } else {
                    nodes.push(node);
                    index = nodes.length - 1;
                    mappedListeners[index] = map;
                }
            } else {
                if (!mapped(node)) {
                    return false;
                }
                index = nodes.indexPerEquivalence(node);
            }
            callback = mappedListeners[index][eventName];
            if (callback) {
                return callback;
            }

            return [];
        },

        removeListener = function (node, eventName, callback) {
            var
                indexObject = nodes.indexPerEquivalence(node),
                callbacks = mappedListeners[indexObject][eventName];

            callbacks.deleteValue(callback);

            if (!mappedListeners[indexObject][eventName].length) {
                delete mappedListeners[indexObject][eventName];
            }

            return mappedListeners[indexObject];
        },

        removeListeners = function (node, eventName) {
            var
                indexObject = nodes.indexPerEquivalence(node);

            delete mappedListeners[indexObject][eventName];

            return mappedListeners[indexObject];
        },

        mapEach = function (name, functionWhenListener, callback) {
            var
                mainReference = this;

            mainReference.each(function (node) {
                if (node instanceof Node || node == window) {
                    mapEventListener(node, name, functionWhenListener);
                    callback.call(node);
                }
            });

        },

        onChangeNode = function (callback) {
            var
                mainReference = this,
                added;

            mapEach.call(mainReference, "changeNode", callback, function () {
                $(this).observe(function (mutations) {
                    mutations.forEach(function (mutation) {
                        added = $(mutation.addedNodes).elements();
                        if (added.some()) {
                            callback.call(mainReference, added.asArray());
                        }
                    });

                });
            });

        },

        // will see how to unbind the node event
        // offChangeNode = function (callback) {
        //     // disconnect
        // },

        on = function (name, capture, callback) {
            var
                mainReference = this;

            if (typeof capture === "function") {
                callback = capture;
                capture = false;
            } else if (typeof capture !== "boolean") {
                capture = false;
            }

            switch (name) {
                case "changeNode":
                    return onChangeNode.call(mainReference, callback);
                default:
                    mapEach.call(mainReference, name, callback, function () {
                        this.addEventListener(name, callback, capture, false);
                    });
            }
        };

    $.extend({

        prototype: {
            on: function (name, capture, callback) {
                on.call(this, name, capture, callback);

                return this;
            },

            off: function (name, capture, callback) {
                if (typeof capture === "function") {
                    callback = capture;
                } else if (typeof capture !== "boolean") {
                    capture = false;
                }

                if (callback) {
                    this.each(function (node) {
                        node.removeEventListener(name, callback, capture);
                        removeListener(node, name, callback);
                    });
                } else {
                    this.each(function (node) {
                        if (mapped(node)) {
                            mapEventListener(node, name)
                            .forEach(function (callback) {
                                [true, false].forEach(function (capture) {
                                    node.removeEventListener(name,
                                        callback, capture);
                                });
                                removeListeners(node, name);
                            });
                        }
                    });
                }

                return this;
            }

        }

    });

})(esPhinx);
<commit_msg>add support to event<commit_after>// require ./main
// require ./extensions/array

var
    esPhinx;

(function ($) {
    "use strict";

    var
        nodes = [],
        mappedListeners = {},

        mapped = function (node) {
            var
                index = nodes.indexPerEquivalence(node);

            if (index) {
                return true;
            }

            return false;
        },

        mapEventListener = function (node, eventName, callback) {
            var
                map = {},
                index;

            map[eventName] = new Array(callback);
            if (callback) {
              index = nodes.indexPerEquivalence(node);
                if (index) {
                    if (mappedListeners[index][eventName]) {
                        mappedListeners[index][eventName].push(callback);
                    } else {
                        mappedListeners[index][eventName] = map[eventName];
                    }
                } else {
                    nodes.push(node);
                    index = nodes.length - 1;
                    mappedListeners[index] = map;
                }
            } else {
                if (!mapped(node)) {
                    return false;
                }
                index = nodes.indexPerEquivalence(node);
            }
            callback = mappedListeners[index][eventName];
            if (callback) {
                return callback;
            }

            return [];
        },

        removeListener = function (node, eventName, callback) {
            var
                indexObject = nodes.indexPerEquivalence(node),
                callbacks = mappedListeners[indexObject][eventName];

            callbacks.deleteValue(callback);

            if (!mappedListeners[indexObject][eventName].length) {
                delete mappedListeners[indexObject][eventName];
            }

            return mappedListeners[indexObject];
        },

        removeListeners = function (node, eventName) {
            var
                indexObject = nodes.indexPerEquivalence(node);

            delete mappedListeners[indexObject][eventName];

            return mappedListeners[indexObject];
        },

        mapEach = function (name, functionWhenListener, callback) {
            var
                mainReference = this;

            mainReference.each(function (node) {
                if (node instanceof Node || node == window) {
                    mapEventListener(node, name, functionWhenListener);
                    callback.call(node);
                }
            });

        },

        onChangeNode = function (callback) {
            var
                mainReference = this,
                added;

            mapEach.call(mainReference, "changeNode", callback, function () {
                $(this).observe(function (mutations) {
                    mutations.forEach(function (mutation) {
                        added = $(mutation.addedNodes).elements();
                        if (added.some()) {
                            callback.call(mainReference, added.asArray());
                        }
                    });

                });
            });

        },

        // will see how to unbind the node event
        // offChangeNode = function (callback) {
        //     // disconnect
        // },

        on = function (name, capture, callback) {
            var
                mainReference = this,
                html = $("html"),
                mapWasVisible = [],
                index,
                imgs,
                img,
                listeningToLoad;

            if (typeof capture === "function") {
                callback = capture;
                capture = false;
            } else if (typeof capture !== "boolean") {
                capture = false;
            }

            switch (name) {
                case "changeNode":
                    return onChangeNode.call(mainReference, callback);
                case "load":
                    if (mainReference.tagName() == "img") {
                        imgs = mainReference;
                    } else {
                        imgs = mainReference.find("img");
                    }

                    if (imgs.some()) {

                        listeningToLoad = function () {
                            html.remove(imgs);
                            imgs.each(function (node) {
                                img = $(node);
                                index = mapWasVisible.indexPerEquivalence(node);
                                if (index) {
                                    mapWasVisible.delete(index);
                                    img.show();
                                }

                                node.removeEventListener(name, listeningToLoad,
                                                         false);
                            });

                        };

                        imgs.each(function (node) {
                            img = $(node);
                            if (img.visible()) {
                                mapWasVisible.push(node);
                                img.hide();
                            }
                            html.prepend(img);

                            node.addEventListener(name, listeningToLoad, false);
                        });
                    }

                default:
                    mapEach.call(mainReference, name, callback, function () {
                        this.addEventListener(name, callback, capture);
                    });
            }
        };

    $.extend({

        prototype: {
            on: function (name, capture, callback) {
                on.call(this, name, capture, callback);

                return this;
            },

            off: function (name, capture, callback) {
                if (typeof capture === "function") {
                    callback = capture;
                    capture = false;
                } else if (typeof capture !== "boolean") {
                    capture = false;
                }

                if (callback) {
                    this.each(function (node) {
                        node.removeEventListener(name, callback, capture);
                        removeListener(node, name, callback);
                    });
                } else {
                    this.each(function (node) {
                        if (mapped(node)) {
                            mapEventListener(node, name)
                            .forEach(function (callback) {
                                [true, false].forEach(function (capture) {
                                    node.removeEventListener(name,
                                        callback, capture);
                                });
                                removeListeners(node, name);
                            });
                        }
                    });
                }

                return this;
            }

        }

    });

})(esPhinx);
<|endoftext|>


<commit_before>// require ./main
// require ./extensions/array

var
    esPhinx;

(function ($) {
    "use strict";

    var
        nodes = [],
        mappedListeners = {},

        mapped = function (node) {
            var
                index = nodes.indexPerEquivalence(node);

            if (index) {
                return true;
            }

            return false;
        },

        mapEventListener = function (node, eventName, callback) {
            var
                map = {},
                index;

            map[eventName] = new Array(callback);
            if (callback) {
              index = nodes.indexPerEquivalence(node);
                if (index) {
                    if (mappedListeners[index][eventName]) {
                        mappedListeners[index][eventName].push(callback);
                    } else {
                        mappedListeners[index][eventName] = map[eventName];
                    }
                } else {
                    nodes.push(node);
                    index = nodes.length - 1;
                    mappedListeners[index] = map;
                }
            } else {
                if (!mapped(node)) {
                    return false;
                }
                index = nodes.indexPerEquivalence(node);
            }
            callback = mappedListeners[index][eventName];
            if (callback) {
                return callback;
            }

            return [];
        },

        removeListener = function (node, eventName, callback) {
            var
                indexObject = nodes.indexPerEquivalence(node),
                callbacks = mappedListeners[indexObject][eventName];

            callbacks.deleteValue(callback);

            if (!mappedListeners[indexObject][eventName].length) {
                delete mappedListeners[indexObject][eventName];
            }

            return mappedListeners[indexObject];
        },

        removeListeners = function (node, eventName) {
            var
                indexObject = nodes.indexPerEquivalence(node);

            delete mappedListeners[indexObject][eventName];

            return mappedListeners[indexObject];
        },

        mapEach = function (name, functionWhenListener, callback) {
            var
                mainReference = this;

            mainReference.each(function (node) {
                if (node instanceof Node || node == window) {
                    mapEventListener(node, name, functionWhenListener);
                    callback.call(node);
                }
            });

        },

        onChangeNode = function (callback) {
            var
                mainReference = this,
                added;

            mapEach.call(mainReference, "changeNode", callback, function () {
                $(this).observe(function (mutations) {
                    mutations.forEach(function (mutation) {
                        added = $(mutation.addedNodes).elements();
                        if (added.some()) {
                            callback.call(mainReference, added.asArray());
                        }
                    });

                });
            });

        },

        // will see how to unbind the node event
        // offChangeNode = function (callback) {
        //     // disconnect
        // },

        on = function (name, capture, callback) {
            var
                mainReference = this,
                html = $("html"),
                nodesClone = mainReference.clone(),
                cloned,
                listeningToLoad;

            if (typeof capture === "function") {
                callback = capture;
                capture = false;
            } else if (typeof capture !== "boolean") {
                capture = false;
            }

            switch (name) {
                case "changeNode":
                    return onChangeNode.call(mainReference, callback);
                case "load":
                    listeningToLoad = function (e) {
                        callback.call(this, e);
                        html.remove(nodesClone);

                        nodesClone.each(function (node) {
                            node.removeEventListener(name, listeningToLoad,
                                                     false);
                        });
                    };

                    nodesClone.each(function (node) {
                        cloned = $(node);
                        cloned.hide();
                        html.prepend(node);

                        node.addEventListener(name, listeningToLoad,
                                              false);
                    });
                break;

                default:
                    mapEach.call(mainReference, name, callback, function () {
                        this.addEventListener(name, callback, capture);
                    });
            }
        };

    $.extend({

        prototype: {
            on: function (name, capture, callback) {
                on.call(this, name, capture, callback);

                return this;
            },

            off: function (name, capture, callback) {
                if (typeof capture === "function") {
                    callback = capture;
                    capture = false;
                } else if (typeof capture !== "boolean") {
                    capture = false;
                }

                if (callback) {
                    this.each(function (node) {
                        node.removeEventListener(name, callback, capture);
                        removeListener(node, name, callback);
                    });
                } else {
                    this.each(function (node) {
                        if (mapped(node)) {
                            mapEventListener(node, name)
                            .forEach(function (callback) {
                                [true, false].forEach(function (capture) {
                                    node.removeEventListener(name,
                                        callback, capture);
                                });
                                removeListeners(node, name);
                            });
                        }
                    });
                }

                return this;
            }

        }

    });

})(esPhinx);
<commit_msg>add support to event<commit_after>// require ./main
// require ./extensions/array

var
    esPhinx;

(function ($) {
    "use strict";

    var
        nodes = [],
        mappedListeners = {},

        mapped = function (node) {
            var
                index = nodes.indexPerEquivalence(node);

            if (index) {
                return true;
            }

            return false;
        },

        mapEventListener = function (node, eventName, callback) {
            var
                map = {},
                index;

            map[eventName] = new Array(callback);
            if (callback) {
              index = nodes.indexPerEquivalence(node);
                if (index) {
                    if (mappedListeners[index][eventName]) {
                        mappedListeners[index][eventName].push(callback);
                    } else {
                        mappedListeners[index][eventName] = map[eventName];
                    }
                } else {
                    nodes.push(node);
                    index = nodes.length - 1;
                    mappedListeners[index] = map;
                }
            } else {
                if (!mapped(node)) {
                    return false;
                }
                index = nodes.indexPerEquivalence(node);
            }
            callback = mappedListeners[index][eventName];
            if (callback) {
                return callback;
            }

            return [];
        },

        removeListener = function (node, eventName, callback) {
            var
                indexObject = nodes.indexPerEquivalence(node),
                callbacks = mappedListeners[indexObject][eventName];

            callbacks.deleteValue(callback);

            if (!mappedListeners[indexObject][eventName].length) {
                delete mappedListeners[indexObject][eventName];
            }

            return mappedListeners[indexObject];
        },

        removeListeners = function (node, eventName) {
            var
                indexObject = nodes.indexPerEquivalence(node);

            delete mappedListeners[indexObject][eventName];

            return mappedListeners[indexObject];
        },

        mapEach = function (name, functionWhenListener, callback) {
            var
                mainReference = this;

            mainReference.each(function (node) {
                if (node instanceof Node || node == window) {
                    mapEventListener(node, name, functionWhenListener);
                    callback.call(node);
                }
            });

        },

        onChangeNode = function (callback) {
            var
                mainReference = this,
                added;

            mapEach.call(mainReference, "changeNode", callback, function () {
                $(this).observe(function (mutations) {
                    mutations.forEach(function (mutation) {
                        added = $(mutation.addedNodes).elements();
                        if (added.some()) {
                            callback.call(mainReference, added.asArray());
                        }
                    });

                });
            });

        },

        // will see how to unbind the node event
        // offChangeNode = function (callback) {
        //     // disconnect
        // },

        on = function (name, capture, callback) {
            var
                mainReference = this,
                html = $("html"),
                nodesClone = mainReference.clone(true),
                cloned,
                listeningToLoad,
                imgs;

            if (typeof capture === "function") {
                callback = capture;
                capture = false;
            } else if (typeof capture !== "boolean") {
                capture = false;
            }

            switch (name) {
                case "changeNode":
                    return onChangeNode.call(mainReference, callback);
                case "load":
                    imgs = cloned.find("img");
                    if (imgs.some()) {
                        nodesClone = imgs;
                    }

                    listeningToLoad = function (e) {
                        callback.call(this, e);
                        html.remove(nodesClone);

                        nodesClone.each(function (node) {
                            node.removeEventListener(name, listeningToLoad,
                                                     false);
                        });
                    };

                    nodesClone.each(function (node) {
                        cloned = $(node);
                        cloned.hide();
                        html.prepend(node);

                        node.addEventListener(name, listeningToLoad,
                                              false);
                    });
                break;

                default:
                    mapEach.call(mainReference, name, callback, function () {
                        this.addEventListener(name, callback, capture);
                    });
            }
        };

    $.extend({

        prototype: {
            on: function (name, capture, callback) {
                on.call(this, name, capture, callback);

                return this;
            },

            off: function (name, capture, callback) {
                if (typeof capture === "function") {
                    callback = capture;
                    capture = false;
                } else if (typeof capture !== "boolean") {
                    capture = false;
                }

                if (callback) {
                    this.each(function (node) {
                        node.removeEventListener(name, callback, capture);
                        removeListener(node, name, callback);
                    });
                } else {
                    this.each(function (node) {
                        if (mapped(node)) {
                            mapEventListener(node, name)
                            .forEach(function (callback) {
                                [true, false].forEach(function (capture) {
                                    node.removeEventListener(name,
                                        callback, capture);
                                });
                                removeListeners(node, name);
                            });
                        }
                    });
                }

                return this;
            }

        }

    });

})(esPhinx);
<|endoftext|>


<commit_before><commit_msg>gulp task for uploading articles into database<commit_after><|endoftext|>


<commit_before>//= require ./main

"use strict";

var
    esPhinx;

(function ($) {

    var
        nodes = [],
        mappedListeners = {},

        mapped = function (node) {
            var
                index = nodes.indexPerEquivalence(node);

            if (index) {
                return true;
            }

            return false;
        },

        mapEventListener = function (node, eventName, listener) {
            var
                map = {},
                index;

            map[eventName] = new Array(listener);
            if (listener) {
                if (index = nodes.indexPerEquivalence(node)) {
                    if (mappedListeners[index][eventName]) {
                        mappedListeners[index][eventName].push(listener);
                    } else {
                        mappedListeners[index][eventName] = map[eventName];
                    }
                } else {
                    nodes.push(node);
                    index = nodes.length - 1;
                    mappedListeners[index] = map;
                }
            } else {
                if (!mapped(node)) {
                    return false;
                }
                index = nodes.indexPerEquivalence(node);
            }

            return mappedListeners[index][eventName];
        },

        removeListener = function (node, eventName, listener) {
            var
                indexObject = nodes.indexPerEquivalence(node),
                listeners = mappedListeners[indexObject][eventName];

            listeners.delete(listener);

            if (!mappedListeners[indexObject][eventName].length) {
                delete mappedListeners[indexObject][eventName];
            }

            return mappedListeners[indexObject];
        },

        removeListeners = function (node, eventName) {
            var
                indexObject = nodes.indexPerEquivalence(node);

            delete mappedListeners[indexObject][eventName];

            return mappedListeners[indexObject];
        };

    $.extend({

        prototype: {

            on: function (eventName, capture, listener) {
                var
                    self = this;

                if (typeof capture === "function") {
                    listener = capture;
                } else if (typeof capture !== "boolean") {
                    capture = true;
                }

                self.each(function (node) {
                    mapEventListener(node, eventName, listener);
                    node.addEventListener(eventName, listener, capture, false);
                });
                return self;
            },

            off: function (eventName, options, listener) {
                options = options || {};

                var
                    self = this;

                if (options && !listener) {
                    if (typeof options === "function") {
                        listener = options;
                        options = {};
                    }
                }
                if (listener) {
                    self.each(function (node) {
                        node.removeEventListener(eventName, listener,
                            (options.capture || false));

                        removeListener(node, eventName, listener);
                    });
                } else {
                    self.each(function (node) {
                        if (mapped(node)) {
                            mapEventListener(node, eventName)
                                .forEach(function (listener) {
                                node.removeEventListener(eventName,
                                    listener, (options.capture || false));
                                removeListeners(node, eventName);
                            });
                        }
                    });
                }

                return self;
            }

        }

    });

})(esPhinx);
<commit_msg>fix event<commit_after>//= require ./main

"use strict";

var
    esPhinx;

(function ($) {

    var
        nodes = [],
        mappedListeners = {},

        mapped = function (node) {
            var
                index = nodes.indexPerEquivalence(node);

            if (index) {
                return true;
            }

            return false;
        },

        mapEventListener = function (node, eventName, listener) {
            var
                map = {},
                index;

            map[eventName] = new Array(listener);
            if (listener) {
                if (index = nodes.indexPerEquivalence(node)) {
                    if (mappedListeners[index][eventName]) {
                        mappedListeners[index][eventName].push(listener);
                    } else {
                        mappedListeners[index][eventName] = map[eventName];
                    }
                } else {
                    nodes.push(node);
                    index = nodes.length - 1;
                    mappedListeners[index] = map;
                }
            } else {
                if (!mapped(node)) {
                    return false;
                }
                index = nodes.indexPerEquivalence(node);
            }

            return mappedListeners[index][eventName];
        },

        removeListener = function (node, eventName, listener) {
            var
                indexObject = nodes.indexPerEquivalence(node),
                listeners = mappedListeners[indexObject][eventName];

            debugger
            listeners.delete(listener);

            if (!mappedListeners[indexObject][eventName].length) {
                delete mappedListeners[indexObject][eventName];
            }

            return mappedListeners[indexObject];
        },

        removeListeners = function (node, eventName) {
            var
                indexObject = nodes.indexPerEquivalence(node);

            delete mappedListeners[indexObject][eventName];

            return mappedListeners[indexObject];
        };

    $.extend({

        prototype: {

            on: function (eventName, capture, listener) {
                var
                    self = this;

                if (typeof capture === "function") {
                    listener = capture;
                } else if (typeof capture !== "boolean") {
                    capture = false;
                }

                self.each(function (node) {
                    mapEventListener(node, eventName, listener);
                    node.addEventListener(eventName, listener, capture, false);
                });
                return self;
            },

            off: function (eventName, capture, listener) {
                var
                    self = this;

                if (typeof capture === "function") {
                    listener = capture;
                } else if (typeof capture !== "boolean") {
                    capture = false;
                }

                if (listener) {
                    self.each(function (node) {
                        node.removeEventListener(eventName, listener, capture);
                        removeListener(node, eventName, listener);
                    });
                } else {
                    self.each(function (node) {
                        if (mapped(node)) {
                            mapEventListener(node, eventName)
                            .forEach(function (listener) {
                                [true, false].forEach(function (capture) {
                                    node.removeEventListener(eventName,
                                        listener, capture);
                                });
                                removeListeners(node, eventName);
                            });
                        }
                    });
                }

                return self;
            }

        }

    });

})(esPhinx);
<|endoftext|>


<commit_before><commit_msg>fixes site not compiling<commit_after><|endoftext|>


<commit_before><commit_msg>Removed sass:comb task<commit_after><|endoftext|>


<commit_before><commit_msg>Remove imagemin<commit_after><|endoftext|>


<commit_before><commit_msg>low power shutdown<commit_after><|endoftext|>


<commit_before>const whitelist = ['SCRIPT', 'HEAD', 'TITLE', 'NOSCRIPT', 'STYLE', 'TEMPLATE'];
if (
  !whitelist.includes(node.tagName.toUpperCase()) &&
  axe.commons.dom.hasContent(virtualNode)
) {

  const styles = window.getComputedStyle(node);
  if (styles.getPropertyValue('display') === 'none') {
    return undefined;
  } else if (styles.getPropertyValue('visibility') === 'hidden') {
    // Check if visibility isn't inherited
    const parent = axe.commons.dom.getComposedParent(node);
    const parentStyle = parent && window.getComputedStyle(parent);
    if (!parentStyle || parentStyle.getPropertyValue('visibility') !== 'hidden') {
      return undefined;
    }
  }
}
return true;
<commit_msg>fix: Adjust if formatting<commit_after>const whitelist = ['SCRIPT', 'HEAD', 'TITLE', 'NOSCRIPT', 'STYLE', 'TEMPLATE'];
if (!whitelist.includes(node.tagName.toUpperCase()) &&
    axe.commons.dom.hasContent(virtualNode)) {

  const styles = window.getComputedStyle(node);
  if (styles.getPropertyValue('display') === 'none') {
    return undefined;
  } else if (styles.getPropertyValue('visibility') === 'hidden') {
    // Check if visibility isn't inherited
    const parent = axe.commons.dom.getComposedParent(node);
    const parentStyle = parent && window.getComputedStyle(parent);
    if (!parentStyle || parentStyle.getPropertyValue('visibility') !== 'hidden') {
      return undefined;
    }
  }
}
return true;
<|endoftext|>


<commit_before>/* ***** BEGIN LICENSE BLOCK *****
 * Distributed under the BSD license:
 *
 * Copyright (c) 2010, Ajax.org B.V.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of Ajax.org B.V. nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * ***** END LICENSE BLOCK ***** */

define(function(require, exports, module) {
"use strict";

var lang = require("../lib/lang");

var TextHighlightRules = function() {

    // regexp must not have capturing parentheses
    // regexps are ordered -> the first match is used

    this.$rules = {
        "start" : [{
            token : "empty_line",
            regex : '^$'
        }, {
            defaultToken : "text"
        }]
    };
};

(function() {

    this.addRules = function(rules, prefix) {
        if (!prefix) {
            for (var key in rules)
                this.$rules[key] = rules[key];
            return;
        }
        for (var key in rules) {
            var state = rules[key];
            for (var i = 0; i < state.length; i++) {
                var rule = state[i];
                if (rule.next) {
                    if (typeof rule.next != "string") {
                        if (rule.nextState && rule.nextState.indexOf(prefix) !== 0)
                            rule.nextState = prefix + rule.nextState;
                    } else {
                        if (rule.next.indexOf(prefix) !== 0)
                            rule.next = prefix + rule.next;
                    }

                }
            }
            this.$rules[prefix + key] = state;
        }
    };

    this.getRules = function() {
        return this.$rules;
    };

    this.embedRules = function (HighlightRules, prefix, escapeRules, states, append) {
        var embedRules = new HighlightRules().getRules();
        if (states) {
            for (var i = 0; i < states.length; i++)
                states[i] = prefix + states[i];
        } else {
            states = [];
            for (var key in embedRules)
                states.push(prefix + key);
        }

        this.addRules(embedRules, prefix);

        if (escapeRules) {
            var addRules = Array.prototype[append ? "push" : "unshift"];
            for (var i = 0; i < states.length; i++)
                addRules.apply(this.$rules[states[i]], lang.deepCopy(escapeRules));
        }

        if (!this.$embeds)
            this.$embeds = [];
        this.$embeds.push(prefix);
    };

    this.getEmbeds = function() {
        return this.$embeds;
    };

    var pushState = function(currentState, stack) {
        if (currentState != "start")
            stack.unshift(this.nextState, currentState);
        return this.nextState;
    };
    var popState = function(currentState, stack) {
        if (stack[0] !== currentState)
            return "start";
        stack.shift();
        return stack.shift();
    };

    this.normalizeRules = function() {
        var id = 0;
        var rules = this.$rules;
        function processState(key) {
            var state = rules[key];
            state.processed = true;
            for (var i = 0; i < state.length; i++) {
                var rule = state[i];
                if (!rule.regex && rule.start) {
                    rule.regex = rule.start;
                    if (!rule.next)
                        rule.next = [];
                    rule.next.push({
                        defaultToken: rule.token
                    }, {
                        token: rule.token + ".end",
                        regex: rule.end || rule.start,
                        next: "pop"
                    });
                    rule.token = rule.token + ".start";
                    rule.push = true;
                }
                var next = rule.next || rule.push;
                if (next && Array.isArray(next)) {
                    var stateName = rule.stateName;
                    if (!stateName)  {
                        stateName = rule.token;
                        if (typeof stateName != "string")
                            stateName = stateName[0] || "";
                        if (rules[stateName])
                            stateName += id++;
                    }
                    rules[stateName] = next;
                    rule.next = stateName;
                    processState(stateName);
                } else if (next == "pop") {
                    rule.next = popState;
                }

                if (rule.push) {
                    rule.nextState = rule.next || rule.push;
                    rule.next = pushState;
                    delete rule.push;
                }

                if (rule.rules) {
                    for (var r in rule.rules) {
                        if (rules[r]) {
                            if (rules[r].push)
                                rules[r].push.apply(rules[r], rule.rules[r]);
                        } else {
                            rules[r] = rule.rules[r];
                        }
                    }
                }
                if (rule.include || typeof rule == "string") {
                    var includeName = rule.include || rule;
                    var toInsert = rules[includeName];
                } else if (Array.isArray(rule))
                    toInsert = rule;

                if (toInsert) {
                    var args = [i, 1].concat(toInsert);
                    if (rule.noEscape)
                        args = args.filter(function(x) {return !x.next;});
                    state.splice.apply(state, args);
                    // skip included rules since they are already processed
                    //i += args.length - 3;
                    i--;
                    toInsert = null
                }

                if (rule.keywordMap) {
                    rule.token = this.createKeywordMapper(
                        rule.keywordMap, rule.defaultToken || "text", rule.caseInsensitive
                    );
                    delete rule.defaultToken;
                }
            }
        };
        Object.keys(rules).forEach(processState, this);
    };

    this.createKeywordMapper = function(map, defaultToken, ignoreCase, splitChar) {
        var keywords = Object.create(null);
        Object.keys(map).forEach(function(className) {
            var a = map[className];
            if (ignoreCase)
                a = a.toLowerCase();
            var list = a.split(splitChar || "|");
            for (var i = list.length; i--; )
                keywords[list[i]] = className;
        });
        // in old versions of opera keywords["__proto__"] sets prototype
        // even on objects with __proto__=null
        if (Object.getPrototypeOf(keywords)) {
            keywords.__proto__ = null;
        }
        this.$keywordList = Object.keys(keywords);
        map = null;
        return ignoreCase
            ? function(value) {return keywords[value.toLowerCase()] || defaultToken }
            : function(value) {return keywords[value] || defaultToken };
    }

    this.getKeywords = function() {
        return this.$keywords;
    };

}).call(TextHighlightRules.prototype);

exports.TextHighlightRules = TextHighlightRules;
});
<commit_msg>fix ejs highlighting<commit_after>/* ***** BEGIN LICENSE BLOCK *****
 * Distributed under the BSD license:
 *
 * Copyright (c) 2010, Ajax.org B.V.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of Ajax.org B.V. nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * ***** END LICENSE BLOCK ***** */

define(function(require, exports, module) {
"use strict";

var lang = require("../lib/lang");

var TextHighlightRules = function() {

    // regexp must not have capturing parentheses
    // regexps are ordered -> the first match is used

    this.$rules = {
        "start" : [{
            token : "empty_line",
            regex : '^$'
        }, {
            defaultToken : "text"
        }]
    };
};

(function() {

    this.addRules = function(rules, prefix) {
        if (!prefix) {
            for (var key in rules)
                this.$rules[key] = rules[key];
            return;
        }
        for (var key in rules) {
            var state = rules[key];
            for (var i = 0; i < state.length; i++) {
                var rule = state[i];
                if (rule.next) {
                    if (typeof rule.next != "string") {
                        if (rule.nextState && rule.nextState.indexOf(prefix) !== 0)
                            rule.nextState = prefix + rule.nextState;
                    } else {
                        if (rule.next.indexOf(prefix) !== 0)
                            rule.next = prefix + rule.next;
                    }

                }
            }
            this.$rules[prefix + key] = state;
        }
    };

    this.getRules = function() {
        return this.$rules;
    };

    this.embedRules = function (HighlightRules, prefix, escapeRules, states, append) {
        var embedRules = new HighlightRules().getRules();
        if (states) {
            for (var i = 0; i < states.length; i++)
                states[i] = prefix + states[i];
        } else {
            states = [];
            for (var key in embedRules)
                states.push(prefix + key);
        }

        this.addRules(embedRules, prefix);

        if (escapeRules) {
            var addRules = Array.prototype[append ? "push" : "unshift"];
            for (var i = 0; i < states.length; i++)
                addRules.apply(this.$rules[states[i]], lang.deepCopy(escapeRules));
        }

        if (!this.$embeds)
            this.$embeds = [];
        this.$embeds.push(prefix);
    };

    this.getEmbeds = function() {
        return this.$embeds;
    };

    var pushState = function(currentState, stack) {
        if (currentState != "start" || stack.length)
            stack.unshift(this.nextState, currentState);
        return this.nextState;
    };
    var popState = function(currentState, stack) {
        // if (stack[0] === currentState)
        stack.shift();
        return stack.shift() || "start";
    };

    this.normalizeRules = function() {
        var id = 0;
        var rules = this.$rules;
        function processState(key) {
            var state = rules[key];
            state.processed = true;
            for (var i = 0; i < state.length; i++) {
                var rule = state[i];
                if (!rule.regex && rule.start) {
                    rule.regex = rule.start;
                    if (!rule.next)
                        rule.next = [];
                    rule.next.push({
                        defaultToken: rule.token
                    }, {
                        token: rule.token + ".end",
                        regex: rule.end || rule.start,
                        next: "pop"
                    });
                    rule.token = rule.token + ".start";
                    rule.push = true;
                }
                var next = rule.next || rule.push;
                if (next && Array.isArray(next)) {
                    var stateName = rule.stateName;
                    if (!stateName)  {
                        stateName = rule.token;
                        if (typeof stateName != "string")
                            stateName = stateName[0] || "";
                        if (rules[stateName])
                            stateName += id++;
                    }
                    rules[stateName] = next;
                    rule.next = stateName;
                    processState(stateName);
                } else if (next == "pop") {
                    rule.next = popState;
                }

                if (rule.push) {
                    rule.nextState = rule.next || rule.push;
                    rule.next = pushState;
                    delete rule.push;
                }

                if (rule.rules) {
                    for (var r in rule.rules) {
                        if (rules[r]) {
                            if (rules[r].push)
                                rules[r].push.apply(rules[r], rule.rules[r]);
                        } else {
                            rules[r] = rule.rules[r];
                        }
                    }
                }
                if (rule.include || typeof rule == "string") {
                    var includeName = rule.include || rule;
                    var toInsert = rules[includeName];
                } else if (Array.isArray(rule))
                    toInsert = rule;

                if (toInsert) {
                    var args = [i, 1].concat(toInsert);
                    if (rule.noEscape)
                        args = args.filter(function(x) {return !x.next;});
                    state.splice.apply(state, args);
                    // skip included rules since they are already processed
                    //i += args.length - 3;
                    i--;
                    toInsert = null
                }

                if (rule.keywordMap) {
                    rule.token = this.createKeywordMapper(
                        rule.keywordMap, rule.defaultToken || "text", rule.caseInsensitive
                    );
                    delete rule.defaultToken;
                }
            }
        };
        Object.keys(rules).forEach(processState, this);
    };

    this.createKeywordMapper = function(map, defaultToken, ignoreCase, splitChar) {
        var keywords = Object.create(null);
        Object.keys(map).forEach(function(className) {
            var a = map[className];
            if (ignoreCase)
                a = a.toLowerCase();
            var list = a.split(splitChar || "|");
            for (var i = list.length; i--; )
                keywords[list[i]] = className;
        });
        // in old versions of opera keywords["__proto__"] sets prototype
        // even on objects with __proto__=null
        if (Object.getPrototypeOf(keywords)) {
            keywords.__proto__ = null;
        }
        this.$keywordList = Object.keys(keywords);
        map = null;
        return ignoreCase
            ? function(value) {return keywords[value.toLowerCase()] || defaultToken }
            : function(value) {return keywords[value] || defaultToken };
    }

    this.getKeywords = function() {
        return this.$keywords;
    };

}).call(TextHighlightRules.prototype);

exports.TextHighlightRules = TextHighlightRules;
});
<|endoftext|>