<commit_before>/**
 * Copyright 2016 The AMP HTML Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS-IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import {isExperimentOn} from '../../../src/experiments';
import {getService} from '../../../src/service';
import {assertHttpsUrl} from '../../../src/url';
import {user, rethrowAsync} from '../../../src/log';
import {onDocumentReady} from '../../../src/document-ready';
import {xhrFor} from '../../../src/xhr';
import {toArray} from '../../../src/types';
import {startsWith} from '../../../src/string';
import {templatesFor} from '../../../src/template';
import {removeElement, childElementByAttr} from '../../../src/dom';
import {installStyles} from '../../../src/styles';
import {CSS} from '../../../build/amp-form-0.1.css';
import {ValidationBubble} from './validation-bubble';
import {vsyncFor} from '../../../src/vsync';
import {actionServiceForDoc} from '../../../src/action';

/** @type {string} */
const TAG = 'amp-form';

/** @const @enum {string} */
const FormState_ = {
  SUBMITTING: 'submitting',
  SUBMIT_ERROR: 'submit-error',
  SUBMIT_SUCCESS: 'submit-success',
};

/** @type {?./validation-bubble.ValidationBubble|undefined} */
let validationBubble;

export class AmpForm {

  /**
   * Adds functionality to the passed form element and listens to submit event.
   * @param {!HTMLFormElement} element
   */
  constructor(element) {
    /** @const @private {!Window} */
    this.win_ = element.ownerDocument.defaultView;

    /** @const @private {!HTMLFormElement} */
    this.form_ = element;

    /** @const @private {!../../../src/service/vsync-impl.Vsync} */
    this.vsync_ = vsyncFor(this.win_);

    /** @const @private {!Templates} */
    this.templates_ = templatesFor(this.win_);

    /** @const @private {!Xhr} */
    this.xhr_ = xhrFor(this.win_);

    /** @const @private {!../../../src/action-impl.Action} */
    this.actions_ = actionServiceForDoc(this.win_.document.documentElement);

    /** @const @private {string} */
    this.method_ = this.form_.getAttribute('method') || 'GET';

    /** @const @private {string} */
    this.target_ = this.form_.getAttribute('target');

    /** @const @private {?string} */
    this.xhrAction_ = this.form_.getAttribute('action-xhr');
    if (this.xhrAction_) {
      assertHttpsUrl(this.xhrAction_, this.form_, 'action-xhr');
      user.assert(!startsWith(this.xhrAction_, 'https://cdn.ampproject.org'),
          'form action-xhr should not be on cdn.ampproject.org: %s',
          this.form_);
    }

    const submitButtons = this.form_.querySelectorAll('input[type=submit]');
    user.assert(submitButtons && submitButtons.length > 0,
        'form requires at least one <input type=submit>: %s', this.form_);

    /** @const @private {!Array<!Element>} */
    this.submitButtons_ = toArray(submitButtons);

    /** @private {?string} */
    this.state_ = null;

    this.installSubmitHandler_();
  }

  /** @private */
  installSubmitHandler_() {
    this.form_.addEventListener('submit', e => this.handleSubmit_(e), true);
  }

  /**
   * Note on stopImmediatePropagation usage here, it is important to emulate native
   * browser submit event blocking. Otherwise any other submit listeners would get the
   * event.
   *
   * For example, action service shouldn't trigger 'submit' event if form is actually
   * invalid. stopImmediatePropagation allows us to make sure we don't trigger it
   *
   *
   * @param {!Event} e
   * @private
   */
  handleSubmit_(e) {
    if (this.state_ == FormState_.SUBMITTING) {
      e.stopImmediatePropagation();
      return;
    }

    const shouldValidate = !this.form_.hasAttribute('novalidate');
    const isInvalid = shouldValidate &&
        this.form_.checkValidity && !this.form_.checkValidity();
    if (isInvalid) {
      e.stopImmediatePropagation();
      // TODO(#3776): Use .mutate method when it supports passing state.
      this.vsync_.run({
        measure: undefined,
        mutate: reportValidity,
      }, {form: this.form_});
      return;
    }

    if (this.xhrAction_) {
      e.preventDefault();
      this.cleanupRenderedTemplate_();
      this.setState_(FormState_.SUBMITTING);
      this.xhr_.fetchJson(this.xhrAction_, {
        body: new FormData(this.form_),
        method: this.method_,
        credentials: 'include',
        requireAmpResponseSourceOrigin: true,
      }).then(response => {
        this.actions_.trigger(this.form_, 'submit-success', null);
        this.setState_(FormState_.SUBMIT_SUCCESS);
        this.renderTemplate_(response || {});
      }).catch(error => {
        this.actions_.trigger(this.form_, 'submit-error', null);
        this.setState_(FormState_.SUBMIT_ERROR);
        this.renderTemplate_(error.responseJson || {});
        rethrowAsync('Form submission failed:', error);
      });
    } else if (this.target_ == '_top' && this.method_ == 'POST') {
      this.cleanupRenderedTemplate_();
      this.setState_(FormState_.SUBMITTING);
    }
  }

  /**
   * Adds proper classes for the state passed.
   * @param {string} newState
   * @private
   */
  setState_(newState) {
    const previousState = this.state_;
    this.form_.classList.remove(`amp-form-${previousState}`);
    this.form_.classList.add(`amp-form-${newState}`);
    this.state_ = newState;
    this.submitButtons_.forEach(button => {
      if (newState == FormState_.SUBMITTING) {
        button.setAttribute('disabled', '');
      } else {
        button.removeAttribute('disabled');
      }
    });
  }

  /**
   * @param {!Object=} data
   * @private
   */
  renderTemplate_(data = {}) {
    const container = this.form_.querySelector(`[${this.state_}]`);
    if (container) {
      return this.templates_.findAndRenderTemplate(container, data)
          .then(rendered => {
            rendered.setAttribute('i-amp-rendered', '');
            container.appendChild(rendered);
          });
    }
  }

  /**
   * @private
   */
  cleanupRenderedTemplate_() {
    const container = this.form_.querySelector(`[${this.state_}]`);
    if (!container) {
      return;
    }
    const previousRender = childElementByAttr(container, 'i-amp-rendered');
    if (previousRender) {
      removeElement(previousRender);
    }
  }
}

/**
 * Reports validity of the form passed through state object.
 * @param {!Object} state
 */
function reportValidity(state) {
  reportFormValidity(state.form);
}

/**
 * Reports validity for the first invalid input - if any.
 * @param {!HTMLFormElement} form
 */
function reportFormValidity(form) {
  const inputs = form.querySelectorAll('input,select,textarea');
  for (let i = 0; i < inputs.length; i++) {
    if (!inputs[i].checkValidity()) {
      reportInputValidity(inputs[i]);
      break;
    }
  }
}

/**
 * Revalidates the currently focused input after a change.
 * @param {!KeyboardEvent} event
 */
function onInvalidInputKeyUp_(event) {
  if (event.target.checkValidity()) {
    validationBubble.hide();
  } else {
    validationBubble.show(event.target, event.target.validationMessage);
  }
}

/**
 * Hides validation bubble and removes listeners on the invalid input.
 * @param {!Event} event
 */
function onInvalidInputBlur_(event) {
  validationBubble.hide();
  event.target.removeEventListener('blur', onInvalidInputBlur_);
  event.target.removeEventListener('keyup', onInvalidInputKeyUp_);
}

/**
 * Focuses and reports the invalid message of the input in a message bubble.
 * @param {!HTMLInputElement} input
 */
function reportInputValidity(input) {
  input./*OK*/focus();

  // Remove any previous listeners on the same input. This avoids adding many
  // listeners on the same element when the user submit pressing Enter or any
  // other method to submit the form without the element losing focus.
  input.removeEventListener('blur', onInvalidInputBlur_);
  input.removeEventListener('keyup', onInvalidInputKeyUp_);

  input.addEventListener('keyup', onInvalidInputKeyUp_);
  input.addEventListener('blur', onInvalidInputBlur_);
  validationBubble.show(input, input.validationMessage);
}

/**
 * Installs submission handler on all forms in the document.
 * @param {!Window} win
 */
function installSubmissionHandlers(win) {
  onDocumentReady(win.document, () => {
    toArray(win.document.forms).forEach(form => {
      new AmpForm(form);
    });
  });
}

/**
 * @param {!Window} win
 * @private visible for testing.
 */
export function installAmpForm(win) {
  return getService(win, 'amp-form', () => {
    if (isExperimentOn(win, TAG)) {
      installStyles(win.document, CSS, () => {
        validationBubble = new ValidationBubble(win);
        installSubmissionHandlers(win);
      });
    }
    return {};
  });
}

installAmpForm(AMP.win);
<commit_msg>fix action-impl warning on dist (#3867)<commit_after>/**
 * Copyright 2016 The AMP HTML Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS-IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import {isExperimentOn} from '../../../src/experiments';
import {getService} from '../../../src/service';
import {assertHttpsUrl} from '../../../src/url';
import {user, rethrowAsync} from '../../../src/log';
import {onDocumentReady} from '../../../src/document-ready';
import {xhrFor} from '../../../src/xhr';
import {toArray} from '../../../src/types';
import {startsWith} from '../../../src/string';
import {templatesFor} from '../../../src/template';
import {removeElement, childElementByAttr} from '../../../src/dom';
import {installStyles} from '../../../src/styles';
import {CSS} from '../../../build/amp-form-0.1.css';
import {ValidationBubble} from './validation-bubble';
import {vsyncFor} from '../../../src/vsync';
import {actionServiceForDoc} from '../../../src/action';

/** @type {string} */
const TAG = 'amp-form';

/** @const @enum {string} */
const FormState_ = {
  SUBMITTING: 'submitting',
  SUBMIT_ERROR: 'submit-error',
  SUBMIT_SUCCESS: 'submit-success',
};

/** @type {?./validation-bubble.ValidationBubble|undefined} */
let validationBubble;

export class AmpForm {

  /**
   * Adds functionality to the passed form element and listens to submit event.
   * @param {!HTMLFormElement} element
   */
  constructor(element) {
    /** @const @private {!Window} */
    this.win_ = element.ownerDocument.defaultView;

    /** @const @private {!HTMLFormElement} */
    this.form_ = element;

    /** @const @private {!../../../src/service/vsync-impl.Vsync} */
    this.vsync_ = vsyncFor(this.win_);

    /** @const @private {!Templates} */
    this.templates_ = templatesFor(this.win_);

    /** @const @private {!Xhr} */
    this.xhr_ = xhrFor(this.win_);

    /** @const @private {!../../../src/service/action-impl.Action} */
    this.actions_ = actionServiceForDoc(this.win_.document.documentElement);

    /** @const @private {string} */
    this.method_ = this.form_.getAttribute('method') || 'GET';

    /** @const @private {string} */
    this.target_ = this.form_.getAttribute('target');

    /** @const @private {?string} */
    this.xhrAction_ = this.form_.getAttribute('action-xhr');
    if (this.xhrAction_) {
      assertHttpsUrl(this.xhrAction_, this.form_, 'action-xhr');
      user.assert(!startsWith(this.xhrAction_, 'https://cdn.ampproject.org'),
          'form action-xhr should not be on cdn.ampproject.org: %s',
          this.form_);
    }

    const submitButtons = this.form_.querySelectorAll('input[type=submit]');
    user.assert(submitButtons && submitButtons.length > 0,
        'form requires at least one <input type=submit>: %s', this.form_);

    /** @const @private {!Array<!Element>} */
    this.submitButtons_ = toArray(submitButtons);

    /** @private {?string} */
    this.state_ = null;

    this.installSubmitHandler_();
  }

  /** @private */
  installSubmitHandler_() {
    this.form_.addEventListener('submit', e => this.handleSubmit_(e), true);
  }

  /**
   * Note on stopImmediatePropagation usage here, it is important to emulate native
   * browser submit event blocking. Otherwise any other submit listeners would get the
   * event.
   *
   * For example, action service shouldn't trigger 'submit' event if form is actually
   * invalid. stopImmediatePropagation allows us to make sure we don't trigger it
   *
   *
   * @param {!Event} e
   * @private
   */
  handleSubmit_(e) {
    if (this.state_ == FormState_.SUBMITTING) {
      e.stopImmediatePropagation();
      return;
    }

    const shouldValidate = !this.form_.hasAttribute('novalidate');
    const isInvalid = shouldValidate &&
        this.form_.checkValidity && !this.form_.checkValidity();
    if (isInvalid) {
      e.stopImmediatePropagation();
      // TODO(#3776): Use .mutate method when it supports passing state.
      this.vsync_.run({
        measure: undefined,
        mutate: reportValidity,
      }, {form: this.form_});
      return;
    }

    if (this.xhrAction_) {
      e.preventDefault();
      this.cleanupRenderedTemplate_();
      this.setState_(FormState_.SUBMITTING);
      this.xhr_.fetchJson(this.xhrAction_, {
        body: new FormData(this.form_),
        method: this.method_,
        credentials: 'include',
        requireAmpResponseSourceOrigin: true,
      }).then(response => {
        this.actions_.trigger(this.form_, 'submit-success', null);
        this.setState_(FormState_.SUBMIT_SUCCESS);
        this.renderTemplate_(response || {});
      }).catch(error => {
        this.actions_.trigger(this.form_, 'submit-error', null);
        this.setState_(FormState_.SUBMIT_ERROR);
        this.renderTemplate_(error.responseJson || {});
        rethrowAsync('Form submission failed:', error);
      });
    } else if (this.target_ == '_top' && this.method_ == 'POST') {
      this.cleanupRenderedTemplate_();
      this.setState_(FormState_.SUBMITTING);
    }
  }

  /**
   * Adds proper classes for the state passed.
   * @param {string} newState
   * @private
   */
  setState_(newState) {
    const previousState = this.state_;
    this.form_.classList.remove(`amp-form-${previousState}`);
    this.form_.classList.add(`amp-form-${newState}`);
    this.state_ = newState;
    this.submitButtons_.forEach(button => {
      if (newState == FormState_.SUBMITTING) {
        button.setAttribute('disabled', '');
      } else {
        button.removeAttribute('disabled');
      }
    });
  }

  /**
   * @param {!Object=} data
   * @private
   */
  renderTemplate_(data = {}) {
    const container = this.form_.querySelector(`[${this.state_}]`);
    if (container) {
      return this.templates_.findAndRenderTemplate(container, data)
          .then(rendered => {
            rendered.setAttribute('i-amp-rendered', '');
            container.appendChild(rendered);
          });
    }
  }

  /**
   * @private
   */
  cleanupRenderedTemplate_() {
    const container = this.form_.querySelector(`[${this.state_}]`);
    if (!container) {
      return;
    }
    const previousRender = childElementByAttr(container, 'i-amp-rendered');
    if (previousRender) {
      removeElement(previousRender);
    }
  }
}

/**
 * Reports validity of the form passed through state object.
 * @param {!Object} state
 */
function reportValidity(state) {
  reportFormValidity(state.form);
}

/**
 * Reports validity for the first invalid input - if any.
 * @param {!HTMLFormElement} form
 */
function reportFormValidity(form) {
  const inputs = form.querySelectorAll('input,select,textarea');
  for (let i = 0; i < inputs.length; i++) {
    if (!inputs[i].checkValidity()) {
      reportInputValidity(inputs[i]);
      break;
    }
  }
}

/**
 * Revalidates the currently focused input after a change.
 * @param {!KeyboardEvent} event
 */
function onInvalidInputKeyUp_(event) {
  if (event.target.checkValidity()) {
    validationBubble.hide();
  } else {
    validationBubble.show(event.target, event.target.validationMessage);
  }
}

/**
 * Hides validation bubble and removes listeners on the invalid input.
 * @param {!Event} event
 */
function onInvalidInputBlur_(event) {
  validationBubble.hide();
  event.target.removeEventListener('blur', onInvalidInputBlur_);
  event.target.removeEventListener('keyup', onInvalidInputKeyUp_);
}

/**
 * Focuses and reports the invalid message of the input in a message bubble.
 * @param {!HTMLInputElement} input
 */
function reportInputValidity(input) {
  input./*OK*/focus();

  // Remove any previous listeners on the same input. This avoids adding many
  // listeners on the same element when the user submit pressing Enter or any
  // other method to submit the form without the element losing focus.
  input.removeEventListener('blur', onInvalidInputBlur_);
  input.removeEventListener('keyup', onInvalidInputKeyUp_);

  input.addEventListener('keyup', onInvalidInputKeyUp_);
  input.addEventListener('blur', onInvalidInputBlur_);
  validationBubble.show(input, input.validationMessage);
}

/**
 * Installs submission handler on all forms in the document.
 * @param {!Window} win
 */
function installSubmissionHandlers(win) {
  onDocumentReady(win.document, () => {
    toArray(win.document.forms).forEach(form => {
      new AmpForm(form);
    });
  });
}

/**
 * @param {!Window} win
 * @private visible for testing.
 */
export function installAmpForm(win) {
  return getService(win, 'amp-form', () => {
    if (isExperimentOn(win, TAG)) {
      installStyles(win.document, CSS, () => {
        validationBubble = new ValidationBubble(win);
        installSubmissionHandlers(win);
      });
    }
    return {};
  });
}

installAmpForm(AMP.win);
<|endoftext|>


<commit_before>({
    testInteropLifeCycle: {
        test: [
            function (cmp) {
                var expected = [
                    'component render',
                    'interop afterRender',
                    'module connected',
                    'module rendered',
                    'component afterRender'
                ].join('\n');
                $A.test.assertEquals(expected, cmp.get('v.lifeCycleLog').join('\n'));
            }
        ]
    }
})<commit_msg>only run lifecycle test for Chrome (#1842)<commit_after>({
    testInteropLifeCycle: {
        browsers: ['GOOGLECHROME'],
        test: [
            function (cmp) {
                var expected = [
                    'component render',
                    'interop afterRender',
                    'module connected',
                    'module rendered',
                    'component afterRender'
                ].join('\n');
                $A.test.assertEquals(expected, cmp.get('v.lifeCycleLog').join('\n'));
            }
        ]
    }
})<|endoftext|>


<commit_before>import {workToJSON} from '../requestTypeIdentifier.js';
import {log} from '../utils';

function getSoap(agency, profile, q, filterAgency, sort, offset, limit, allObject) {
  return `<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://oss.dbc.dk/ns/opensearch">
  <SOAP-ENV:Body>
    <ns1:searchRequest>
      <ns1:query>${q}${filterAgency && ` AND holdingsitem.agencyId=${filterAgency}` || ''}</ns1:query>
      <ns1:agency>${agency}</ns1:agency>
      <ns1:profile>${profile}</ns1:profile>
      <ns1:start>${offset}</ns1:start>
      <ns1:stepValue>${limit}</ns1:stepValue>
      ${sort ? `<ns1:sort>${sort}</ns1:sort>` : ''}
      <ns1:collectionType>work-1</ns1:collectionType>
      <ns1:allObjects>${allObject}</ns1:allObjects>
      <ns1:objectFormat>briefDisplay</ns1:objectFormat>
      <ns1:objectFormat>dkabm</ns1:objectFormat>
      <ns1:outputType>json</ns1:outputType>
    </ns1:searchRequest>
  </SOAP-ENV:Body>
</SOAP-ENV:Envelope>
`;
}

export default (params, context) => {
  if (!params.q) {
    return Promise.resolve({statusCode: 400, error: 'missing q parameter'});
  }

  const agency = context.get('search.agency', true);
  const profile = (typeof params.profile === 'string' && params.profile.length > 0) ? params.profile : context.get('search.profile', true);
  const filterAgency = context.get('search.holdingsitemagencyid') || null;
  const q = params.q.replace(/</g, '<');
  const sort = (params.sort || '').replace(/</g, '<');
  const offset = 1 + (parseInt(params.offset, 10) || 0);
  const limit = parseInt(params.limit, 10) || 10;

  // We make two simultaneous calls to opensearch improve support for multiVolume works
  // The first call only gets the work we searched for, that way we can set the correct PID, title and coverUrl
  // The second call gets all related objects, so you can identify multiVolume works.
  return Promise.all([
    context.call('opensearch', getSoap(agency, profile, q, filterAgency, sort, offset, limit, 0)),
    context.call('opensearch', getSoap(agency, profile, q, filterAgency, sort, offset, limit, 1))
  ]).then(bodies => {
    const responses = bodies.map(body => {
      body = JSON.parse(body).searchResponse;
      if (body.error) {
        return {
          statusCode: 500,
          error: body.error.$
        };
      }
      body = body.result;

      // let more = body.more.$; // this could be used for paging info later
      let searchResult = body.searchResult || [];
      let result = [];
      const parseErrors = [];
      searchResult.forEach(o => { // eslint-disable-line no-loop-func
        let collection = o.collection.object.map(obj => obj.identifier.$);
        let dkabm = o.collection.object[0].record;
        dkabm = workToJSON(dkabm);

        let briefDisplays = [];
        if (o.formattedCollection.briefDisplay) {
          briefDisplays = o.formattedCollection.briefDisplay.manifestation.map(briefDisplay => {
            delete briefDisplay.fedoraPid;
            return workToJSON(briefDisplay, 'bd');
          });
        }
        else {
          parseErrors.push(`No data found on object(s): ${collection}`);
          log.error('Parse error: briefDisplay could not be found on object', {
            collection: collection[0],
            context: context.data,
            OpenSearch: {trackingId: body.statInfo.trackingId.$}
          });
        }

        // here we would call getObject or moreInfo if needed...
        result.push(Object.assign({
          collection: collection,
          collectionDetails: briefDisplays
        }, dkabm, briefDisplays[0]));
      });

      const response = {statusCode: 200, data: result};

      if (parseErrors.length) {
        response.error = parseErrors;
      }

      return response;
    });

    // Check if either object has failed.
    if (responses[0].statusCode !== 200) {
      return responses[0];
    }

    if (responses[1].statusCode !== 200) {
      return responses[1];
    }

    // Merge the responses of the two arrays.
    if (responses[0].data && responses[1].data) {
      responses[0].data = responses[0].data.map((responseDetails, idx) => {
        const collectionDetails = responses[1].data[idx];

        if (collectionDetails.collection) {
          responseDetails.collection = collectionDetails.collection;
        }

        if (collectionDetails.collectionDetails) {
          responseDetails.collectionDetails = collectionDetails.collectionDetails;
        }

        return responseDetails;
      });
    }
    return responses[0];
  });
};
<commit_msg>improved error message<commit_after>import {workToJSON} from '../requestTypeIdentifier.js';
import {log} from '../utils';

function getSoap(agency, profile, q, filterAgency, sort, offset, limit, allObject) {
  return `<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://oss.dbc.dk/ns/opensearch">
  <SOAP-ENV:Body>
    <ns1:searchRequest>
      <ns1:query>${q}${filterAgency && ` AND holdingsitem.agencyId=${filterAgency}` || ''}</ns1:query>
      <ns1:agency>${agency}</ns1:agency>
      <ns1:profile>${profile}</ns1:profile>
      <ns1:start>${offset}</ns1:start>
      <ns1:stepValue>${limit}</ns1:stepValue>
      ${sort ? `<ns1:sort>${sort}</ns1:sort>` : ''}
      <ns1:collectionType>work-1</ns1:collectionType>
      <ns1:allObjects>${allObject}</ns1:allObjects>
      <ns1:objectFormat>briefDisplay</ns1:objectFormat>
      <ns1:objectFormat>dkabm</ns1:objectFormat>
      <ns1:outputType>json</ns1:outputType>
    </ns1:searchRequest>
  </SOAP-ENV:Body>
</SOAP-ENV:Envelope>
`;
}

export default (params, context) => {
  if (!params.q) {
    return Promise.resolve({statusCode: 400, error: 'missing q parameter'});
  }

  const agency = context.get('search.agency', true);
  const profile = (typeof params.profile === 'string' && params.profile.length > 0) ? params.profile : context.get('search.profile', true);
  const filterAgency = context.get('search.holdingsitemagencyid') || null;
  const q = params.q.replace(/</g, '<');
  const sort = (params.sort || '').replace(/</g, '<');
  const offset = 1 + (parseInt(params.offset, 10) || 0);
  const limit = parseInt(params.limit, 10) || 10;

  // We make two simultaneous calls to opensearch improve support for multiVolume works
  // The first call only gets the work we searched for, that way we can set the correct PID, title and coverUrl
  // The second call gets all related objects, so you can identify multiVolume works.
  return Promise.all([
    context.call('opensearch', getSoap(agency, profile, q, filterAgency, sort, offset, limit, 0)),
    context.call('opensearch', getSoap(agency, profile, q, filterAgency, sort, offset, limit, 1))
  ]).then(bodies => {
    const responses = bodies.map(body => {
      body = JSON.parse(body).searchResponse;
      if (body.error) {
        return {
          statusCode: 500,
          error: body.error.$
        };
      }
      body = body.result;

      // let more = body.more.$; // this could be used for paging info later
      let searchResult = body.searchResult || [];
      let result = [];
      const parseErrors = [];
      searchResult.forEach(o => { // eslint-disable-line no-loop-func
        let collection = o.collection.object.map(obj => obj.identifier.$);
        let dkabm = o.collection.object[0].record;
        dkabm = workToJSON(dkabm);

        let briefDisplays = [];
        if (o.formattedCollection.briefDisplay) {
          briefDisplays = o.formattedCollection.briefDisplay.manifestation.map(briefDisplay => {
            delete briefDisplay.fedoraPid;
            return workToJSON(briefDisplay, 'bd');
          });
        }
        else {
          parseErrors.push(`Parse error: briefDisplay could not be found on object ${collection}`);
          log.error('Parse error: briefDisplay could not be found on object', {
            collection: collection[0],
            context: context.data,
            OpenSearch: {trackingId: body.statInfo.trackingId.$}
          });
        }

        // here we would call getObject or moreInfo if needed...
        result.push(Object.assign({
          collection: collection,
          collectionDetails: briefDisplays
        }, dkabm, briefDisplays[0]));
      });

      const response = {statusCode: 200, data: result};

      if (parseErrors.length) {
        response.error = parseErrors;
      }

      return response;
    });

    // Check if either object has failed.
    if (responses[0].statusCode !== 200) {
      return responses[0];
    }

    if (responses[1].statusCode !== 200) {
      return responses[1];
    }

    // Merge the responses of the two arrays.
    if (responses[0].data && responses[1].data) {
      responses[0].data = responses[0].data.map((responseDetails, idx) => {
        const collectionDetails = responses[1].data[idx];

        if (collectionDetails.collection) {
          responseDetails.collection = collectionDetails.collection;
        }

        if (collectionDetails.collectionDetails) {
          responseDetails.collectionDetails = collectionDetails.collectionDetails;
        }

        return responseDetails;
      });
    }
    return responses[0];
  });
};
<|endoftext|>


<commit_before><commit_msg>Added some map camera restrictions<commit_after><|endoftext|>


<commit_before><commit_msg>more changes<commit_after><|endoftext|>


<commit_before><commit_msg>Removed local $LAB declaration.<commit_after><|endoftext|>


<commit_before>angular
    .module('app', [
        'ui.router'
        ])
    .config(['$urlRouterProvider', '$stateProvider', function($urlRouterProvider, $stateProvider){
        $urlRouterProvider.otherwise('/'); //seems to control what happens when navigating directly to routes from within the domain of the app

        //given to us by ui-router
        $stateProvider
            .state('home', {
                url: '/',
                templateUrl: 'app/home/home.html'
            })
    }])<commit_msg>adds another routeto an 'about' page<commit_after>angular
    .module('app', [
        'ui.router'
        ])
    .config(['$urlRouterProvider', '$stateProvider', function($urlRouterProvider, $stateProvider){
        $urlRouterProvider.otherwise('/'); //seems to control what happens when navigating directly to routes from within the domain of the app

        //given to us by ui-router
        $stateProvider
            .state('home', {
                url: '/',
                templateUrl: 'app/home/home.html'
            })
            .state('about', {
                url: '/about',
                templateUrl: 'app/about/about.html'
            })
    }])<|endoftext|>


<commit_before>'use strict';

//Start by defining the main module and adding the module dependencies
angular.module(ApplicationConfiguration.applicationModuleName, ApplicationConfiguration.applicationModuleVendorDependencies);

// Setting HTML5 Location Mode
angular.module(ApplicationConfiguration.applicationModuleName).config(['$locationProvider',
  function($locationProvider) {
    $locationProvider.hashPrefix('!');
  }
]);

//Then define the init function for starting up the application
angular.element(document).ready(function() {
  //Fixing facebook bug with redirect
  if (window.location.hash === '#_=_') {
    window.location.hash = '#!';
  }

  //Then init the app
  angular.bootstrap(document, [ApplicationConfiguration.applicationModuleName]);
});
<commit_msg>Fixes long line<commit_after>'use strict';

//Start by defining the main module and adding the module dependencies
angular.module(ApplicationConfiguration.applicationModuleName,
               ApplicationConfiguration.applicationModuleVendorDependencies);

// Setting HTML5 Location Mode
angular.module(ApplicationConfiguration.applicationModuleName).config(['$locationProvider',
  function($locationProvider) {
    $locationProvider.hashPrefix('!');
  }
]);

//Then define the init function for starting up the application
angular.element(document).ready(function() {
  //Fixing facebook bug with redirect
  if (window.location.hash === '#_=_') {
    window.location.hash = '#!';
  }

  //Then init the app
  angular.bootstrap(document, [ApplicationConfiguration.applicationModuleName]);
});
<|endoftext|>


<commit_before>var CarWatcher = function(locations) {

  this.render = function() {
    var latestLocation = locations[0] && {lat: locations[0].latitude, lng: locations[0].longitude}

    var mapOptions = {
      center: latestLocation || new google.maps.LatLng(59.436961, 24.753575),
      zoom: latestLocation ? 14 : 12
    }

    var map = new google.maps.Map($("#map")[0], mapOptions)
    var markers = addRoute(map, locations)

    $(".location").on("click", function(event) {
      event.preventDefault()
      bounce(markers[$(event.target).data("index")])
    })
  }

  function addRoute(map, locations) {
    var markers = _(locations).chain().select(function(location) {
      return location.time >= new Date().getTime() - 48 * 60 * 60 * 1000
    }).map(function(location, i) {
      return new google.maps.Marker({
        position: {lat: location.latitude, lng: location.longitude},
        animation: google.maps.Animation.DROP,
        title: JSON.stringify(_(location).extend({time: new Date(location.time)}))
      })
    }).value()

    renderMarkers(map, markers, 0)

    return markers
  }

  function renderMarkers(map, markers, index) {
    setTimeout(function() {
      var marker = markers[index]
      if (!marker) return

      map.panTo(marker.getPosition())
      marker.setMap(map)
      renderMarkers(map, markers, ++index)
    }, 500)
  }

  function bounce(marker) {
    marker.setAnimation(google.maps.Animation.BOUNCE)
    setTimeout(function() { marker.setAnimation(null) }, 2000)
  }
}
<commit_msg>Newest location marker is with bigger scale.<commit_after>var CarWatcher = function(locations) {

  this.render = function() {
    var latestLocation = locations[0] && {lat: locations[0].latitude, lng: locations[0].longitude}

    var mapOptions = {
      center: latestLocation || new google.maps.LatLng(59.436961, 24.753575),
      zoom: latestLocation ? 14 : 12
    }

    var map = new google.maps.Map($("#map")[0], mapOptions)
    var markers = addRoute(map, locations)

    $(".location").on("click", function(event) {
      event.preventDefault()
      bounce(map, markers[$(event.target).data("index")])
    })
  }

  function addRoute(map, locations) {
    if (!locations.length) return

    var markers = _(locations).chain().select(function(location) {
      return location.time >= new Date().getTime() - 48 * 60 * 60 * 1000
    }).map(function(location, i) {
      return new google.maps.Marker({
        position: {lat: location.latitude, lng: location.longitude},
        animation: google.maps.Animation.DROP,
        title: JSON.stringify(_(location).extend({time: new Date(location.time)}))
      })
    }).value()

    _(markers).last().setIcon({
      url: "http://mt.googleapis.com/vt/icon/name=icons/spotlight/spotlight-poi.png&scale=1.3"
    });

    renderMarkers(map, markers, 0)

    return markers
  }

  function renderMarkers(map, markers, index) {
    setTimeout(function() {
      var marker = markers[index]
      if (!marker) return

      map.panTo(marker.getPosition())
      marker.setMap(map)
      renderMarkers(map, markers, ++index)
    }, 500)
  }

  function bounce(map, marker) {
    map.panTo(marker.getPosition())
    marker.setAnimation(google.maps.Animation.BOUNCE)
    setTimeout(function() { marker.setAnimation(null) }, 3000)
  }
}
<|endoftext|>


<commit_before>/**
 * Created by shirkan on 11/24/14.
 */

if (isRunningOnMobile()) {
    import chartboost;
}

exports
{
    CBinit = function () {

        if (!isRunningOnMobile()) {
            return;
        }

        chartboost.init();

        //  Init interstitials
        CBprepareInterstitial();
        chartboost.on('AdClicked', function () { LOG('user clicked an ad!'); });
        chartboost.on('AdDismissed', function () { LOG('Ad dismissed'); CBprepareInterstitial(); });

        //  Init watch video
        CBprepareVideo();
    };

    //  Interstitial handling
    CBprepareInterstitial = function () {
        LOG("CBprepareInterstitial");
        chartboost.cacheInterstitial();
    };

    CBshowInterstitial = function () {
        LOG("CBshowInterstitial");
        chartboost.showInterstitialIfAvailable();
    };

    CBs = function () {
        LOG("CBshowInterstitial");
        chartboost.showInterstitial();
    };

    //  Video handling
    CBprepareVideo = function () {
        LOG("CBprepareVideo");
        chartboost.cacheRewardedVideo();
    };

    CBshowVideo = function () {
        LOG("CBshowVideo");
        chartboost.showRewardedVideo();
    };

    CBonGameEnd = function() {
        if (!isRunningOnMobile()) {
            return;
        }

        //  20% - just end the game
        //  20% - suggest watching a video for extra life
        //  60% - show Interstitial

        var decision = randomNumber(0,1);

        if (decision <= 0.2) {
            LOG("CBonGameEnd " + decision);
            return;
        } else if (decision <= 0.4) {
            LOG("CBonGameEnd " + decision);
            //  Suggest watching a video
        } else {
            LOG("CBonGameEnd " + decision);
            CBshowInterstitial();
        }
    }
}<commit_msg>CB show interstitial for now<commit_after>/**
 * Created by shirkan on 11/24/14.
 */

if (isRunningOnMobile()) {
    import chartboost;
}

exports
{
    CBinit = function () {

        if (!isRunningOnMobile()) {
            return;
        }

        chartboost.init();

        //  Init interstitials
        CBprepareInterstitial();
        chartboost.on('AdClicked', function () { LOG('user clicked an ad!'); });
        chartboost.on('AdDismissed', function () { LOG('Ad dismissed'); CBprepareInterstitial(); });

        //  Init watch video
        CBprepareVideo();
    };

    //  Interstitial handling
    CBprepareInterstitial = function () {
        LOG("CBprepareInterstitial");
        chartboost.cacheInterstitial();
    };

    CBshowInterstitial = function () {
        LOG("CBshowInterstitial");
        chartboost.showInterstitialIfAvailable();
    };

    CBs = function () {
        LOG("CBshowInterstitial");
        chartboost.showInterstitial();
    };

    //  Video handling
    CBprepareVideo = function () {
        LOG("CBprepareVideo");
        chartboost.cacheRewardedVideo();
    };

    CBshowVideo = function () {
        LOG("CBshowVideo");
        chartboost.showRewardedVideo();
    };

    CBonGameEnd = function() {
        if (!isRunningOnMobile()) {
            return;
        }

        //  20% - just end the game
        //  20% - suggest watching a video for extra life
        //  60% - show Interstitial

        var decision = randomNumber(0,1);

        if (decision <= 0.2) {
            LOG("CBonGameEnd " + decision);
            return;
        } else if (decision <= 0.4) {
            LOG("CBonGameEnd " + decision);
            //  Suggest watching a video
            CBshowInterstitial();
        } else {
            LOG("CBonGameEnd " + decision);
            CBshowInterstitial();
        }
    }
}<|endoftext|>


<commit_before>/**
 * Created by guangqiang on 2017/9/10.
 */
import React, {Component} from 'react'
import {View, StyleSheet} from 'react-native'
import * as Progress from 'react-native-progress'
import deviceInfo from '../deviceInfo'
import {commonStyle} from '../commonStyle'
import {Actions} from 'react-native-router-flux'
export default class ProgressHUD extends Component {

  componentWillReceiveProps(nextProps) {
    if (!nextProps.showHUD) {
      Actions.pop({ loading: true })
    }
  }

  render() {
    return (
      <View style={styles.maskStyle}>
        <View style={styles.backViewStyle}>
          <Progress.Circle size={deviceInfo.deviceWidth / 8} indeterminate={true} />
        </View>
      </View>
    )
  }
}

const styles = StyleSheet.create({
    maskStyle: {
      position: 'absolute',
      backgroundColor: 'rgba(0,0,0,0.2)',
      width: deviceInfo.deviceWidth,
      height: deviceInfo.deviceHeight,
      alignItems: 'center',
      justifyContent: 'center'
    },
    backViewStyle: {
      backgroundColor: commonStyle.white,
      width: deviceInfo.deviceWidth / 4,
      height: deviceInfo.deviceWidth / 4,
      justifyContent: 'center',
      alignItems: 'center',
      borderRadius: 10,
    }
  }
)<commit_msg>优化Loading组件代码<commit_after>/**
 * Created by guangqiang on 2017/9/10.
 */
import React, {Component} from 'react'
import {View, StyleSheet, ActivityIndicator} from 'react-native'
import deviceInfo from '../deviceInfo'
import {Actions} from 'react-native-router-flux'
export default class ProgressHUD extends Component {

  componentWillReceiveProps(nextProps) {
    if (!nextProps.showHUD) {
      Actions.pop({ loading: true })
    }
  }

  render() {
    return (
      <View style={styles.maskStyle}>
        <View style={styles.backViewStyle}>
          {/*<Progress.Circle size={deviceInfo.deviceWidth / 8} indeterminate={true} />*/}
          <ActivityIndicator size="large" color="white" />
        </View>
      </View>
    )
  }
}

const styles = StyleSheet.create({
    maskStyle: {
      position: 'absolute',
      backgroundColor: 'rgba(0,0,0,0.3)',
      width: deviceInfo.deviceWidth,
      height: deviceInfo.deviceHeight,
      alignItems: 'center',
      justifyContent: 'center'
    },
    backViewStyle: {
      backgroundColor: '#111',
      width: 120,
      height: 100,
      justifyContent: 'center',
      alignItems: 'center',
      borderRadius: 5,
    }
  }
)<|endoftext|>


<commit_before>/*!
{
  "name": "canvas.toDataURL type support",
  "property": ["todataurljpeg", "todataurlpng", "todataurlwebp"],
  "tags": ["canvas"],
  "builderAliases": ["canvas_todataurl_type"],
  "notes": [{
    "name": "MDN Docs",
    "href": "https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement.toDataURL"
  }]
}
!*/
define(['Modernizr', 'createElement', 'test/canvas'], function(Modernizr, createElement) {

  var canvas = createElement('canvas');

  Modernizr.addTest('todataurljpeg', function() {
    return !!Modernizr.canvas && canvas.toDataURL('image/jpeg').indexOf('data:image/jpeg') === 0;
  });
  Modernizr.addTest('todataurlpng', function() {
    return !!Modernizr.canvas && canvas.toDataURL('image/png').indexOf('data:image/png') === 0;
  });
  Modernizr.addTest('todataurlwebp', function() {
    var supports = false;

    // firefox 3 throws an error when you use an "invalid" toDataUrl
    try {
      supports = !!Modernizr.canvas && canvas.toDataURL('image/webp').indexOf('data:image/webp') === 0;
    } catch (e) {}

    return supports;
  });

});
<commit_msg>Fixes an issue with AVG Secure browser throwing exceptions when using toDataURL when Anti-Fingerprinting is turned on (#2538)<commit_after>/*!
{
  "name": "canvas.toDataURL type support",
  "property": ["todataurljpeg", "todataurlpng", "todataurlwebp"],
  "tags": ["canvas"],
  "builderAliases": ["canvas_todataurl_type"],
  "notes": [{
    "name": "MDN Docs",
    "href": "https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement.toDataURL"
  }]
}
!*/
define(['Modernizr', 'createElement', 'test/canvas'], function(Modernizr, createElement) {

  var canvas = createElement('canvas');

  Modernizr.addTest('todataurljpeg', function() {
    var supports = false;

    // AVG secure browser with 'Anti-Fingerprinting' turned on throws an exception when using an "invalid" toDataUrl
    try {
      supports = !!Modernizr.canvas && canvas.toDataURL('image/jpeg').indexOf('data:image/jpeg') === 0;
    } catch (e) {}

    return supports;
  });
  Modernizr.addTest('todataurlpng', function() {
    var supports = false;

    // AVG secure browser with 'Anti-Fingerprinting' turned on throws an exception when using an "invalid" toDataUrl
    try {
      supports = !!Modernizr.canvas && canvas.toDataURL('image/png').indexOf('data:image/png') === 0;
    } catch (e) {}

    return supports;
  });
  Modernizr.addTest('todataurlwebp', function() {
    var supports = false;

    // firefox 3 throws an error when you use an "invalid" toDataUrl
    try {
      supports = !!Modernizr.canvas && canvas.toDataURL('image/webp').indexOf('data:image/webp') === 0;
    } catch (e) {}

    return supports;
  });

});
<|endoftext|>


<commit_before>const bunyan = require('bunyan')

const HumanFormatter = require('./human-formatter')

const ERROR_CODES = {
  EADDRINUSE: e => ({
    msg: `Port \`:${ e.port }\` already in use`,
    note: 'Another Logux server or other app already running on this port. ' +
          'Maybe you didn’t not stop server from other project ' +
          'or previous version of this server was not killed.'
  }),
  EACCES: e => ({
    msg: `You are not allowed to run server on port \`:${ e.port }\``,
    note: 'Try to change user (e.g. root) or use port >= 1024'
  }),
  LOGUX_UNKNOWN_OPTION: e => ({
    msg: e.message,
    note: 'Maybe there is a mistake in option name or this version ' +
          'of Logux Server doesn’t support this option'
  }),
  LOGUX_WRONG_OPTIONS: e => ({
    msg: e.message,
    note: 'Check server constructor and Logux Server documentation'
  })
}

const REPORTERS = {
  listen: record => {
    const details = {
      loguxServer: record.loguxServer,
      environment: record.environment,
      nodeId: record.nodeId,
      subprotocol: record.subprotocol,
      supports: record.supports
    }

    if (record.environment === 'development') {
      details.note = [
        'Server was started in non-secure development mode',
        'Press Ctrl-C to shutdown server'
      ]
    }

    if (record.server) {
      details.server = record.server
    } else {
      const protocol = record.cert ? 'wss://' : 'ws://'
      details.listen = `${ protocol }${ record.host }:${ record.port }`
    }

    if (record.backendSend) {
      details.backendListen = 'http://' +
        (record.backendHost || '0.0.0.0') + ':' + (record.backendPort || 1338)
      details.backendSend = record.backendSend
    }

    return { msg: 'Logux server is listening', details }
  },

  connect: () => ({ msg: 'Client was connected' }),

  authenticated: () => ({ msg: 'User was authenticated' }),

  disconnect: () => ({ msg: 'Client was disconnected' }),

  destroy: () => ({ msg: 'Shutting down Logux server' }),

  add: () => ({ msg: 'Action was added' }),

  clean: () => ({ msg: 'Action was cleaned' }),

  processed: () => ({ msg: 'Action was processed' }),

  subscribed: () => ({ msg: 'Client was subscribed' }),

  unsubscribed: () => ({ msg: 'Client was unsubscribed' }),

  unauthenticated: () => ({ level: 'warn', msg: 'Bad authentication' }),

  useless: () => ({ level: 'warn', msg: 'Useless action' }),

  denied: () => ({ level: 'warn', msg: 'Action was denied' }),

  zombie: () => ({ level: 'warn', msg: 'Zombie client was disconnected' }),

  unknownType: record => ({
    level: /^ server(:| )/.test(record.actionId) ? 'error' : 'warn',
    msg: 'Action with unknown type'
  }),

  wrongChannel: () => ({
    level: 'warn',
    msg: 'Wrong channel name'
  }),

  error: record => {
    const result = {
      level: record.fatal ? 'fatal' : 'error',
      msg: record.err.message,
      details: {
        err: {
          message: record.err.message,
          name: record.err.name,
          stack: record.err.stack
        }
      }
    }

    const helper = ERROR_CODES[record.err.code]
    if (helper) {
      const help = helper(record.err)
      result.msg = help.msg
      result.details.note = help.note
      delete result.details.err.stack
    }

    const isClient = record.clientId || record.nodeId
    if (isClient && record.err.name === 'SyncError') {
      result.level = 'warn'
      if (record.err.received) {
        result.msg = `Client error: ${ record.err.description }`
      } else {
        result.msg = `Sync error: ${ record.err.description }`
      }
      delete result.details.err.stack
    }

    for (const i in record) {
      if (i !== 'err' && i !== 'fatal') {
        result.details[i] = record[i]
      }
    }

    return result
  }
}

function createReporter (options) {
  let logger
  if (options.bunyan) {
    logger = options.bunyan
  } else {
    let streams
    if (options.reporter === 'human') {
      const env = options.env || process.env.NODE_ENV || 'development'
      const color = env !== 'development' ? false : undefined
      streams = [
        {
          type: 'raw',
          stream: new HumanFormatter({ basepath: options.root, color })
        }
      ]
    }
    logger = bunyan.createLogger({ name: 'logux-server', streams })
  }

  function reporter (type, details) {
    const report = REPORTERS[type](details)
    const level = report.level || 'info'
    reporter.logger[level](report.details || details || { }, report.msg)
  }
  reporter.logger = logger
  return reporter
}

module.exports = createReporter
<commit_msg>Clean up tests<commit_after>const bunyan = require('bunyan')

const HumanFormatter = require('./human-formatter')

const ERROR_CODES = {
  EADDRINUSE: e => ({
    msg: `Port \`:${ e.port }\` already in use`,
    note: 'Another Logux server or other app already running on this port. ' +
          'Maybe you didn’t not stop server from other project ' +
          'or previous version of this server was not killed.'
  }),
  EACCES: e => ({
    msg: `You are not allowed to run server on port \`:${ e.port }\``,
    note: 'Try to change user (e.g. root) or use port >= 1024'
  }),
  LOGUX_UNKNOWN_OPTION: e => ({
    msg: e.message,
    note: 'Maybe there is a mistake in option name or this version ' +
          'of Logux Server doesn’t support this option'
  }),
  LOGUX_WRONG_OPTIONS: e => ({
    msg: e.message,
    note: 'Check server constructor and Logux Server documentation'
  })
}

const REPORTERS = {
  listen: r => {
    const details = {
      loguxServer: r.loguxServer,
      environment: r.environment,
      nodeId: r.nodeId,
      subprotocol: r.subprotocol,
      supports: r.supports
    }

    if (r.environment === 'development') {
      details.note = [
        'Server was started in non-secure development mode',
        'Press Ctrl-C to shutdown server'
      ]
    }

    if (r.server) {
      details.server = r.server
    } else {
      const protocol = r.cert ? 'wss://' : 'ws://'
      details.listen = `${ protocol }${ r.host }:${ r.port }`
    }

    if (r.backendSend) {
      details.backendListen = `http://${ r.backendHost }:${ r.backendPort }`
      details.backendSend = r.backendSend
    }

    return { msg: 'Logux server is listening', details }
  },

  connect: () => ({ msg: 'Client was connected' }),

  authenticated: () => ({ msg: 'User was authenticated' }),

  disconnect: () => ({ msg: 'Client was disconnected' }),

  destroy: () => ({ msg: 'Shutting down Logux server' }),

  add: () => ({ msg: 'Action was added' }),

  clean: () => ({ msg: 'Action was cleaned' }),

  processed: () => ({ msg: 'Action was processed' }),

  subscribed: () => ({ msg: 'Client was subscribed' }),

  unsubscribed: () => ({ msg: 'Client was unsubscribed' }),

  unauthenticated: () => ({ level: 'warn', msg: 'Bad authentication' }),

  useless: () => ({ level: 'warn', msg: 'Useless action' }),

  denied: () => ({ level: 'warn', msg: 'Action was denied' }),

  zombie: () => ({ level: 'warn', msg: 'Zombie client was disconnected' }),

  unknownType: record => ({
    level: /^ server(:| )/.test(record.actionId) ? 'error' : 'warn',
    msg: 'Action with unknown type'
  }),

  wrongChannel: () => ({
    level: 'warn',
    msg: 'Wrong channel name'
  }),

  error: record => {
    const result = {
      level: record.fatal ? 'fatal' : 'error',
      msg: record.err.message,
      details: {
        err: {
          message: record.err.message,
          name: record.err.name,
          stack: record.err.stack
        }
      }
    }

    const helper = ERROR_CODES[record.err.code]
    if (helper) {
      const help = helper(record.err)
      result.msg = help.msg
      result.details.note = help.note
      delete result.details.err.stack
    }

    const isClient = record.clientId || record.nodeId
    if (isClient && record.err.name === 'SyncError') {
      result.level = 'warn'
      if (record.err.received) {
        result.msg = `Client error: ${ record.err.description }`
      } else {
        result.msg = `Sync error: ${ record.err.description }`
      }
      delete result.details.err.stack
    }

    for (const i in record) {
      if (i !== 'err' && i !== 'fatal') {
        result.details[i] = record[i]
      }
    }

    return result
  }
}

function createReporter (options) {
  let logger
  if (options.bunyan) {
    logger = options.bunyan
  } else {
    let streams
    if (options.reporter === 'human') {
      const env = options.env || process.env.NODE_ENV || 'development'
      const color = env !== 'development' ? false : undefined
      streams = [
        {
          type: 'raw',
          stream: new HumanFormatter({ basepath: options.root, color })
        }
      ]
    }
    logger = bunyan.createLogger({ name: 'logux-server', streams })
  }

  function reporter (type, details) {
    const report = REPORTERS[type](details)
    const level = report.level || 'info'
    reporter.logger[level](report.details || details || { }, report.msg)
  }
  reporter.logger = logger
  return reporter
}

module.exports = createReporter
<|endoftext|>


<commit_before>import React, { PropTypes } from "react";
import { PropTypes as CustomPropTypes, TextSize, Helpers } from "../victory-util/index";
import { merge, isEmpty, defaults, sumBy, maxBy } from "lodash";
import VictoryLabel from "../victory-label/victory-label";
import VictoryContainer from "../victory-container/victory-container";
import Point from "../victory-primitives/point";

const defaultStyles = {
  symbol: {
    fill: "black",
    type: "circle"
  },
  label: {
    fontSize: 14,
    fontFamily: "'Gill Sans', 'Gill Sans MT', 'Seravek', 'Trebuchet MS', sans-serif",
    color: "#252525",
    backgroundColor: "#d9d9d9",
    stroke: "transparent"
  }
};
const defaultLegendData = [
  { name: "Series 1" },
  { name: "Series 2" }
];

export default class VictoryLegend extends React.Component {
  static displayName = "VictoryLegend";

  static role = "legend";

  static propTypes = {
    x: PropTypes.number.isRequired,
    y: PropTypes.number.isRequired,
    width: PropTypes.oneOfType([
      CustomPropTypes.nonNegative,
      PropTypes.func
    ]),
    height: PropTypes.oneOfType([
      CustomPropTypes.nonNegative,
      PropTypes.func
    ]),
    padding: PropTypes.oneOfType([
      PropTypes.number,
      PropTypes.shape({
        top: PropTypes.number,
        bottom: PropTypes.number,
        left: PropTypes.number,
        right: PropTypes.number
      })
    ]),
    orientation: PropTypes.oneOf(["horizontal", "vertical"]),
    data: PropTypes.arrayOf(
      PropTypes.shape({
        name: PropTypes.string.isRequired,
        label: PropTypes.object,
        symbol: PropTypes.object
      })
    ),
    containerComponent: PropTypes.element,
    dataComponent: PropTypes.element,
    groupComponent: PropTypes.element,
    labelComponent: PropTypes.element,
    symbolSpacer: PropTypes.number,
    gutter: PropTypes.number,
    standalone: PropTypes.bool,
    style: PropTypes.shape({
      symbol: PropTypes.object,
      label: PropTypes.object
    })
  };

  static defaultProps = {
    x: 0,
    y: 0,
    padding: 0,
    symbolSpacer: 8,
    gutter: 10,
    orientation: "vertical",
    dataComponent: <Point/>,
    labelComponent: <VictoryLabel/>,
    containerComponent: <VictoryContainer/>,
    groupComponent: <g/>,
    standalone: true,
    style: {}
  };

  calculateLegendHeight(props, textSizes, padding) {
    const { data, gutter, orientation } = props;
    const contentHeight = orientation === "horizontal"
      ? maxBy(textSizes, "height").height
      : sumBy(textSizes, "height") + gutter * (data.length - 1);

    return padding.top + contentHeight + padding.bottom;
  }

  calculateLegendWidth(props, textSizes, padding) {
    const { data, gutter, orientation, symbolSpacer } = props;
    const contentWidth = orientation === "horizontal"
      ? sumBy(textSizes, "width") + (gutter + symbolSpacer * 3) * (data.length - 1)
      : maxBy(textSizes, "width").width + symbolSpacer * 2;

    return padding.left + contentWidth + padding.right;
  }

  getCalculatedProps(props) {
    let { height, width } = props;
    const padding = Helpers.getPadding(props);
    const symbolStyles = [];
    const labelStyles = [];
    let leftOffset = 0;

    const textSizes = props.data.map((datum, i) => {
      const styles = this.getComponentStyles(datum, props, "label");
      symbolStyles[i] = this.getComponentStyles(datum, props, "symbol");
      labelStyles[i] = styles;

      const textSize = TextSize.approximateTextSize(datum.name, styles);
      textSize.leftOffset = leftOffset;
      leftOffset += textSize.width;

      return textSize;
    });

    if (!height) {
      height = this.calculateLegendHeight(props, textSizes, padding);
    }
    if (!width) {
      width = this.calculateLegendWidth(props, textSizes, padding);
    }

    return Object.assign({},
      props,
      { padding, textSizes, height, width, labelStyles, symbolStyles }
    );
  }

  getComponentStyles(datum, props, key) {
    return merge({}, defaultStyles[key], props.style[key], datum[key]);
  }

  getSymbolSize(datum, fontSize) {
    return datum.symbol && datum.symbol.size ? datum.symbol.size : fontSize / 2.5;
  }

  getSymbolProps(datum, props, i) {
    const {
      gutter, labelStyles, orientation, padding, symbolSpacer, symbolStyles, textSizes
    } = props;
    const { leftOffset } = textSizes[i];
    const { fontSize } = labelStyles[i];
    const symbolShift = fontSize / 2;
    const style = symbolStyles[i];

    const symbolCoords = orientation === "horizontal" ? {
      x: padding.left + leftOffset + symbolShift + (fontSize + symbolSpacer + gutter) * i,
      y: padding.top + symbolShift
    } : {
      x: padding.left + symbolShift,
      y: padding.top + symbolShift + (fontSize + gutter) * i
    };

    return {
      key: `symbol-${i}`,
      style,
      size: this.getSymbolSize(datum, fontSize),
      symbol: style.type,
      ...symbolCoords
    };
  }

  getLabelProps(datum, props, i) {
    const { gutter, orientation, symbolSpacer, labelStyles, textSizes, padding } = props;
    const style = labelStyles[i];
    const { fontSize } = style;
    const symbolShift = fontSize / 2;

    const labelCoords = orientation === "horizontal" ? {
      x: padding.left + textSizes[i].leftOffset + (fontSize + symbolSpacer) * (i + 1) + gutter * i,
      y: padding.top + symbolShift
    } : {
      x: padding.left + fontSize + symbolSpacer,
      y: padding.top + symbolShift + (fontSize + gutter) * i
    };

    return {
      key: `label-${i}`,
      style,
      text: datum.name,
      ...labelCoords
    };
  }

  renderLegendItems(props) {
    const { data, dataComponent, labelComponent } = props;
    const legendData = isEmpty(data) ? defaultLegendData : data;
    const length = legendData.length;
    const dataComponents = [];
    const labelComponents = [];

    for (let i = 0; i < length; i++) {
      const datum = legendData[i];

      dataComponents[i] = React.cloneElement(
        dataComponent,
        this.getSymbolProps(datum, props, i)
      );
      labelComponents[i] = React.cloneElement(
        labelComponent,
        this.getLabelProps(datum, props, i)
      );
    }

    return [...dataComponents, ...labelComponents];
  }

  renderGroup(props, children) {
    const { groupComponent, height, width, standalone, x, y } = props;
    const groupProps = { role: "presentation" };

    if (!standalone) {
      Object.assign(groupProps, { height, width, x, y });
    }

    return React.cloneElement(groupComponent, groupProps, children);
  }

  renderContainer(props, children) {
    const { containerComponent, height, width, x, y, style } = props;

    return React.cloneElement(
      containerComponent,
      { x, y, height, width, style: defaults({}, style) },
      children
    );
  }

  render() {
    const props = this.getCalculatedProps(this.props);
    const group = this.renderGroup(props, this.renderLegendItems(props));
    return props.standalone ? this.renderContainer(props, group) : group;
  }
}
<commit_msg>consolidate orientation checks by storing in calculated props<commit_after>import React, { PropTypes } from "react";
import { PropTypes as CustomPropTypes, TextSize, Helpers } from "../victory-util/index";
import { merge, isEmpty, defaults, sumBy, maxBy } from "lodash";
import VictoryLabel from "../victory-label/victory-label";
import VictoryContainer from "../victory-container/victory-container";
import Point from "../victory-primitives/point";

const defaultStyles = {
  symbol: {
    fill: "black",
    type: "circle"
  },
  label: {
    fontSize: 14,
    fontFamily: "'Gill Sans', 'Gill Sans MT', 'Seravek', 'Trebuchet MS', sans-serif",
    color: "#252525",
    backgroundColor: "#d9d9d9",
    stroke: "transparent"
  }
};
const defaultLegendData = [
  { name: "Series 1" },
  { name: "Series 2" }
];

export default class VictoryLegend extends React.Component {
  static displayName = "VictoryLegend";

  static role = "legend";

  static propTypes = {
    x: PropTypes.number.isRequired,
    y: PropTypes.number.isRequired,
    width: PropTypes.oneOfType([
      CustomPropTypes.nonNegative,
      PropTypes.func
    ]),
    height: PropTypes.oneOfType([
      CustomPropTypes.nonNegative,
      PropTypes.func
    ]),
    padding: PropTypes.oneOfType([
      PropTypes.number,
      PropTypes.shape({
        top: PropTypes.number,
        bottom: PropTypes.number,
        left: PropTypes.number,
        right: PropTypes.number
      })
    ]),
    orientation: PropTypes.oneOf(["horizontal", "vertical"]),
    data: PropTypes.arrayOf(
      PropTypes.shape({
        name: PropTypes.string.isRequired,
        label: PropTypes.object,
        symbol: PropTypes.object
      })
    ),
    containerComponent: PropTypes.element,
    dataComponent: PropTypes.element,
    groupComponent: PropTypes.element,
    labelComponent: PropTypes.element,
    symbolSpacer: PropTypes.number,
    gutter: PropTypes.number,
    standalone: PropTypes.bool,
    style: PropTypes.shape({
      symbol: PropTypes.object,
      label: PropTypes.object
    })
  };

  static defaultProps = {
    x: 0,
    y: 0,
    padding: 0,
    symbolSpacer: 8,
    gutter: 10,
    orientation: "vertical",
    dataComponent: <Point/>,
    labelComponent: <VictoryLabel/>,
    containerComponent: <VictoryContainer/>,
    groupComponent: <g/>,
    standalone: true,
    style: {}
  };

  calculateLegendHeight(textSizes, padding, isHorizontal) {
    const { data, gutter } = this.props;
    const contentHeight = isHorizontal
      ? maxBy(textSizes, "height").height
      : sumBy(textSizes, "height") + gutter * (data.length - 1);

    return padding.top + contentHeight + padding.bottom;
  }

  calculateLegendWidth(textSizes, padding, isHorizontal) {
    const { data, gutter, symbolSpacer } = this.props;
    const contentWidth = isHorizontal
      ? sumBy(textSizes, "width") + (gutter + symbolSpacer * 3) * (data.length - 1)
      : maxBy(textSizes, "width").width + symbolSpacer * 2;

    return padding.left + contentWidth + padding.right;
  }

  getCalculatedProps(props) {
    let { height, width } = props;
    const isHorizontal = props.orientation === "horizontal";
    const padding = Helpers.getPadding(props);
    const symbolStyles = [];
    const labelStyles = [];
    let leftOffset = 0;

    const textSizes = props.data.map((datum, i) => {
      const styles = this.getComponentStyles(datum, props, "label");
      symbolStyles[i] = this.getComponentStyles(datum, props, "symbol");
      labelStyles[i] = styles;

      const textSize = TextSize.approximateTextSize(datum.name, styles);
      textSize.leftOffset = leftOffset;
      leftOffset += textSize.width;

      return textSize;
    });

    if (!height) {
      height = this.calculateLegendHeight(textSizes, padding, isHorizontal);
    }
    if (!width) {
      width = this.calculateLegendWidth(textSizes, padding, isHorizontal);
    }

    return Object.assign({},
      props,
      { isHorizontal, padding, textSizes, height, width, labelStyles, symbolStyles }
    );
  }

  getComponentStyles(datum, props, key) {
    return merge({}, defaultStyles[key], props.style[key], datum[key]);
  }

  getSymbolSize(datum, fontSize) {
    return datum.symbol && datum.symbol.size ? datum.symbol.size : fontSize / 2.5;
  }

  getSymbolProps(datum, props, i) {
    const {
      gutter, labelStyles, isHorizontal, padding, symbolSpacer, symbolStyles, textSizes
    } = props;
    const { leftOffset } = textSizes[i];
    const { fontSize } = labelStyles[i];
    const symbolShift = fontSize / 2;
    const style = symbolStyles[i];

    const symbolCoords = isHorizontal ? {
      x: padding.left + leftOffset + symbolShift + (fontSize + symbolSpacer + gutter) * i,
      y: padding.top + symbolShift
    } : {
      x: padding.left + symbolShift,
      y: padding.top + symbolShift + (fontSize + gutter) * i
    };

    return {
      key: `symbol-${i}`,
      style,
      size: this.getSymbolSize(datum, fontSize),
      symbol: style.type,
      ...symbolCoords
    };
  }

  getLabelProps(datum, props, i) {
    const { gutter, isHorizontal, symbolSpacer, labelStyles, textSizes, padding } = props;
    const style = labelStyles[i];
    const { fontSize } = style;
    const symbolShift = fontSize / 2;

    const labelCoords = isHorizontal ? {
      x: padding.left + textSizes[i].leftOffset + (fontSize + symbolSpacer) * (i + 1) + gutter * i,
      y: padding.top + symbolShift
    } : {
      x: padding.left + fontSize + symbolSpacer,
      y: padding.top + symbolShift + (fontSize + gutter) * i
    };

    return {
      key: `label-${i}`,
      style,
      text: datum.name,
      ...labelCoords
    };
  }

  renderLegendItems(props) {
    const { data, dataComponent, labelComponent } = props;
    const legendData = isEmpty(data) ? defaultLegendData : data;
    const length = legendData.length;
    const dataComponents = [];
    const labelComponents = [];

    for (let i = 0; i < length; i++) {
      const datum = legendData[i];

      dataComponents[i] = React.cloneElement(
        dataComponent,
        this.getSymbolProps(datum, props, i)
      );
      labelComponents[i] = React.cloneElement(
        labelComponent,
        this.getLabelProps(datum, props, i)
      );
    }

    return [...dataComponents, ...labelComponents];
  }

  renderGroup(props, children) {
    const { groupComponent, height, width, standalone, x, y } = props;
    const groupProps = { role: "presentation" };

    if (!standalone) {
      Object.assign(groupProps, { height, width, x, y });
    }

    return React.cloneElement(groupComponent, groupProps, children);
  }

  renderContainer(props, children) {
    const { containerComponent, height, width, x, y, style } = props;

    return React.cloneElement(
      containerComponent,
      { x, y, height, width, style: defaults({}, style) },
      children
    );
  }

  render() {
    const props = this.getCalculatedProps(this.props);
    const group = this.renderGroup(props, this.renderLegendItems(props));
    return props.standalone ? this.renderContainer(props, group) : group;
  }
}
<|endoftext|>


<commit_before>$(document).ready(function() {
    $.fn.editable.defaults.mode = 'inline';
    $('.x-editable').editable({
        ajaxOptions: {
            dataType: 'json'
        },
        success: function(response, newValue) {
            if (!response) {
                return "There was an unknown error!";
            }
            if (response.success === false) {
                if (!response.msg) {
                    return "There was an unknown error!";
                }
                return response.msg;
            }
        },
        error: function(response, newValue) {
            if (!response) {
                return "There was an unknown error!";
            }
            if (response.success === false) {
                if (!response.msg) {
                    return "There was an unknown error!";
                }
                return response.msg;
            }
            return "There was an unknown error!";
        }
    });
});
<commit_msg>Log json responses<commit_after>$(document).ready(function() {
    $.fn.editable.defaults.mode = 'inline';
    $('.x-editable').editable({
        ajaxOptions: {
            dataType: 'json'
        },
        success: function(response, newValue) {
            console.log(response);
            if (!response) {
                return "There was an unknown error!";
            }
            if (response.success === false) {
                if (!response.msg) {
                    return "There was an unknown error!";
                }
                return response.msg;
            }
        },
        error: function(response, newValue) {
            console.log(response);
            if (!response) {
                return "There was an unknown error!";
            }
            if (response.success === false) {
                if (!response.msg) {
                    return "There was an unknown error!";
                }
                return response.msg;
            }
            return "There was an unknown error!";
        }
    });
});
<|endoftext|>


<commit_before>'use strict';

// Call this function when the page loads (the "ready" event)
$(document).ready(function() {
	initializePage();
})

/*
 * Function that is called when the document is ready.
 */
function initializePage() {
	$('.project a').click(addProjectDetails);

	$('#colorBtn').click(randomizeColors);
}

/*
 * Make an AJAX call to retrieve project details and add it in
 */
function addProjectDetails(e) {
	// Prevent following the link
	e.preventDefault();

	// Get the div ID, e.g., "project3"
	var projectID = $(this).closest('.project').attr('id');
	// get rid of 'project' from the front of the id 'project3'
	var idNumber = projectID.substr('project'.length);

	console.log("User clicked on project " + idNumber);

	$.get("/project/" + idNumber, addProject);
	console.log("project/" + idNumber);
}

function addProject(result) {
	console.log(result);
	var projectHTML = '' +
										'' +
										'
' + result['title'] + '
' +
										'
' + result['date'] + '
';
	$('#project' + result['id'] + ' .details').html(projectHTML);
}

/*
 * Make an AJAX call to retrieve a color palette for the site
 * and apply it
 */
function randomizeColors(e) {
	console.log("User clicked on color button");
	$.get("/palette", changeColor);
}

function changeColor(result) {
	var colors = result['colors']['hex'];
	$('body').css('background-color', colors[0]);
	$('.thumbnail').css('background-color', colors[1]);
	$('h1, h2, h3, h4, h5, h5').css('color', colors[2]);
	$('p').css('color', colors[3]);
	$('.project img').css('opacity', .75);
}<commit_msg>update<commit_after>'use strict';

// Call this function when the page loads (the "ready" event)
$(document).ready(function() {
	initializePage();
})

/*
 * Function that is called when the document is ready.
 */
function initializePage() {
	$('.project a').click(addProjectDetails);

	$('#colorBtn').click(randomizeColors);
}

/*
 * Make an AJAX call to retrieve project details and add it in
 */
function addProjectDetails(e) {
	// Prevent following the link
	e.preventDefault();

	// Get the div ID, e.g., "project3"
	var projectID = $(this).closest('.project').attr('id');
	// get rid of 'project' from the front of the id 'project3'
	var idNumber = projectID.substr('project'.length);

	console.log("User clicked on project " + idNumber);

	$.get("/project/" + idNumber, addProject);
	console.log("project/" + idNumber);
}

function addProject(result) {
	console.log(result);
	var projectHTML = '' +
										'
' + result['title'] + '
' +
										'
' + result['date'] + '
' +
										'
' + result['summary'] + '
'
	$('#project' + result['id'] + ' .details').html(projectHTML);
}

/*
 * Make an AJAX call to retrieve a color palette for the site
 * and apply it
 */
function randomizeColors(e) {
	console.log("User clicked on color button");
	$.get("/palette", changeColor);
}

function changeColor(result) {
	var colors = result['colors']['hex'];
	$('body').css('background-color', colors[0]);
	$('.thumbnail').css('background-color', colors[1]);
	$('h1, h2, h3, h4, h5, h5').css('color', colors[2]);
	$('p').css('color', colors[3]);
	$('.project img').css('opacity', .75);
}<|endoftext|>


<commit_before>// This happends when we've replaced the uneditable table-element with
// an input-box. Now we want to listen to changes.
var saveArticle = function(elem) {
  var compiledModified = $('#modified-template').html();
  var modifiedTemplate = Handlebars.compile(compiledModified);

  var newValue = $(elem).val();
  $(elem).parent().html(modifiedTemplate({ name: newValue })).click(function() {
    modifyArticle.call($(this).find('span'));
  });
};

// Here we listen for clicks in an element of the table.
// When someone clicks we replace the html with an input box.
var modifyArticle = function(tablerow) {
  var compiledModify = $('#modify-template').html();
  var modifyTemplate = Handlebars.compile(compiledModify);
  var slug = tablerow.data("slug");

  // This will essentially remove itself
  var currentValue = $(this).text();
  $(this).parent().html(modifyTemplate({ value: currentValue }));
  $('#currently-modifying').select().keydown(function(e) {
    // 13 = the enter key
    if (e.keyCode == 13) {
      saveArticle(this);

      var name = tablerow.find("td[data-name='name']").find("span").html();
      var storage = tablerow.find("td[data-name='storage']").find("span").html();
      var section = tablerow.find("td[data-name='section']").find("span").html();
      var shelf = tablerow.find("td[data-name='shelf']").find("span").html();
      var tray = tablerow.find("td[data-name='tray']").find("span").html();

      $.ajax({
        type: 'GET',
        url: '/api/update/Kartotekartikel/' + slug,
        data: {
          name: name,
          storage: storage,
          section: section,
          shelf: shelf,
          tray: tray
        }
      })
        .done(function( msg ) {
        })
        .fail(function(err, status){
          console.log('Kunde inte ändra artikeln.');
          console.log(err);
          console.log(status);
        });
    }
  });
};

var addArticle = function() {
  var compiledArticle = $('#article-template').html();
  var articleTemplate = Handlebars.compile(compiledArticle);
  var row = $(this).parent().parent();

  $.ajax({
    type: 'POST',
    url: '/api/kartotekartikels',
    data: {
      name: row.find('[data-type="name"]').val(),
      storage: row.find('[data-type="storage"]').val(),
      section: row.find('[data-type="section"]').val(),
      shelf: row.find('[data-type="shelf"]').val(),
      tray: row.find('[data-type="tray"]').val()
    }
  })
    .done(function (newArticle) {
      console.log(newArticle._id);
      // The first row (index 0) contains the header, the second row (index 1)
      // contains the "create" row, we want to append the new article after the create row.
      var createRow = $('#articles').find('tr')[1];
      var newArticleElement = $(articleTemplate(newArticle)).insertAfter(createRow);

      $('#' + newArticle._id + 'remove').click(function() {
        removeArticle.call(this, newArticle.slug);
      });

      $('.modifyable-article-column > span').click(function(){
        modifyArticle.call(this, $(this).parent().parent());
      });
    })
    .fail(function (err, status) {
      console.log('Kartoteksartikel kunde inte läggas till!');
      console.log(err);
      console.log(status);
    });
};

var removeArticle = function(slug) {
  var confirmed = confirm("Är du säker på att du vill ta bort artikeln från kartoteket?");
  if(confirmed){
    $.ajax({
      type: 'DELETE',
      url: '/api/kartotekartikels/' + slug
    })
      .done(function () {
      })
      .fail(function (err, status) {
        console.log('Kartoteksartikel kunde inte tas bort!');
        console.log(err);
        console.log(status);
      });

    var row = $(this).parent().parent();
    row.remove();
  }
  else{
    return false;
  }
};

$(function() {
  // TODO: (Connect to)/(update in) database
  $('#article-add').click(addArticle);
  $('.article-remove').click(function() {
    removeArticle.call(this, $(this).parent().parent().data("slug"));
  });
  $('.modifyable-article-column > span').click(function(){
    modifyArticle.call(this, $(this).parent().parent());
  });
});
<commit_msg>Whitespace<commit_after>// This happends when we've replaced the uneditable table-element with
// an input-box. Now we want to listen to changes.
var saveArticle = function(elem) {
  var compiledModified = $('#modified-template').html();
  var modifiedTemplate = Handlebars.compile(compiledModified);

  var newValue = $(elem).val();
  $(elem).parent().html(modifiedTemplate({ name: newValue })).click(function() {
    modifyArticle.call($(this).find('span'));
  });
};

// Here we listen for clicks in an element of the table.
// When someone clicks we replace the html with an input box.
var modifyArticle = function(tablerow) {
  var compiledModify = $('#modify-template').html();
  var modifyTemplate = Handlebars.compile(compiledModify);
  var slug = tablerow.data("slug");

  // This will essentially remove itself
  var currentValue = $(this).text();
  $(this).parent().html(modifyTemplate({ value: currentValue }));
  $('#currently-modifying').select().keydown(function(e) {
    // 13 = the enter key
    if (e.keyCode == 13) {
      saveArticle(this);

      var name = tablerow.find("td[data-name='name']").find("span").html();
      var storage = tablerow.find("td[data-name='storage']").find("span").html();
      var section = tablerow.find("td[data-name='section']").find("span").html();
      var shelf = tablerow.find("td[data-name='shelf']").find("span").html();
      var tray = tablerow.find("td[data-name='tray']").find("span").html();

      $.ajax({
        type: 'GET',
        url: '/api/update/Kartotekartikel/' + slug,
        data: {
          name: name,
          storage: storage,
          section: section,
          shelf: shelf,
          tray: tray
        }
      })
        .done(function( msg ) {
        })
        .fail(function(err, status) {
          console.log('Kunde inte ändra artikeln.');
          console.log(err);
          console.log(status);
        });
    }
  });
};

var addArticle = function() {
  var compiledArticle = $('#article-template').html();
  var articleTemplate = Handlebars.compile(compiledArticle);
  var row = $(this).parent().parent();

  $.ajax({
    type: 'POST',
    url: '/api/kartotekartikels',
    data: {
      name: row.find('[data-type="name"]').val(),
      storage: row.find('[data-type="storage"]').val(),
      section: row.find('[data-type="section"]').val(),
      shelf: row.find('[data-type="shelf"]').val(),
      tray: row.find('[data-type="tray"]').val()
    }
  })
    .done(function (newArticle) {
      console.log(newArticle._id);
      // The first row (index 0) contains the header, the second row (index 1)
      // contains the "create" row, we want to append the new article after the create row.
      var createRow = $('#articles').find('tr')[1];
      var newArticleElement = $(articleTemplate(newArticle)).insertAfter(createRow);

      $('#' + newArticle._id + 'remove').click(function() {
        removeArticle.call(this, newArticle.slug);
      });

      $('.modifyable-article-column > span').click(function(){
        modifyArticle.call(this, $(this).parent().parent());
      });
    })
    .fail(function (err, status) {
      console.log('Kartoteksartikel kunde inte läggas till!');
      console.log(err);
      console.log(status);
    });
};

var removeArticle = function(slug) {
  var confirmed = confirm("Är du säker på att du vill ta bort artikeln från kartoteket?");
  if (confirmed) {
    $.ajax({
      type: 'DELETE',
      url: '/api/kartotekartikels/' + slug
    })
      .fail(function (err, status) {
        console.log('Kartoteksartikel kunde inte tas bort!');
        console.log(err);
        console.log(status);
      });

    var row = $(this).parent().parent();
    row.remove();
  }
  else {
    return false;
  }
};

$(function() {
  $('#article-add').click(addArticle);
  $('.article-remove').click(function() {
    removeArticle.call(this, $(this).parent().parent().data("slug"));
  });
  $('.modifyable-article-column > span').click(function(){
    modifyArticle.call(this, $(this).parent().parent());
  });
});
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

// Search Constants
var DEFAULT_SIZE = 10;

// For Versioning
var apiVersion = "@VERSION",
    makeAPI, Make, module, request,
    escape;

// Shim module so we can safely check what environment this is being included in.
module = module || undefined;

function doXHRServer( type, path, data, callback ) {
  if ( module ) {
    request({
      method: type,
      uri: path,
      json: data
    }, function( err, res, body ) {

      if ( err ) {
        callback( { error: err } );
        return;
      }

      if ( res.statusCode === 200 ) {
        callback( body );
      }
    });
  }
}

function doXHR( type, path, data, callback ) {

  if ( typeof data === "function" ) {
    callback = data;
    data = {};
  } else if ( typeof data === "string" ) {
    path += "?s=" + data;
    data = {};
  }

  path = makeAPI + path;

  if ( !module ) {
    var request = new XMLHttpRequest();

    request.open( type, path, true );
    request.setRequestHeader( "Content-Type", "application/json; charset=utf-8" );
    request.onreadystatechange = function() {
      if ( this.readyState === 4 ) {
        callback( JSON.parse( this.responseText ) );
      }
    };
    request.send( JSON.stringify( data ) );
  } else {
    doXHRServer( type, path, data, callback );
  }
}

// Shorthand for creating a Make Object
Make = function Make( options ) {
  makeAPI = options.makeAPI;

  var MakeMap = {
      author: function( name ) {
        return {
          query: {
            field: {
              author: name
            }
          }
        };
      },
      email: function( name ) {
        return {
          term: {
            email: name
          }
        };
      },
      tags: function( tagOptions ) {
        if ( typeof tagOptions.tags === "string" ) {
          tagOptions.tags = [ tagOptions.tags ];
        }

        return {
          terms: tagOptions
        };
      },
      field: function( field, direction ) {
        var sortObj;
        if ( direction ) {
          sortObj = {};
          sortObj[ field ] = direction;
          return sortObj;
        }

        return field;
      },
      limit: function ( num ) {
        return parseInt( num ) || DEFAULT_SIZE;
      },
      id: function( id ) {
        return {
          query: {
            field: {
              "_id": id
            }
          }
        };
      },
      tagPrefix: function( prefix ) {
        return {
          prefix: {
            "tags": prefix
          }
        };
      },
      url: function( makeUrl ) {
        return {
          term: {
            url: escape( makeUrl )
          }
        };
      }
    },
    BASE_QUERY = {
      query: {
        filtered: {
          query: {
            match_all: {}
          }
        }
      }
    };

  return {
    searchFilters: [],
    sortBy: [],
    size: DEFAULT_SIZE,

    find: function( options ) {
      options = options || {};

      for ( var key in options ) {
        if ( options.hasOwnProperty( key ) && MakeMap[ key ] ) {
          if ( key === "field" ) {
            this.sortedBy.push( MakeMap[ key ]( options[ key ] ) );
          } else {
            this.searchFilters.push( MakeMap[ key ]( options[ key ] ) );
          }
        }
      }

      return this;
    },

    author: function( name ) {
      this.searchFilters.push( MakeMap.author( name ) );
      return this;
    },

    email: function( name ) {
      this.searchFilters.push( MakeMap.email( name ) );
      return this;
    },

    tags: function( tagOptions ) {
      this.searchFilters.push( MakeMap.tags( tagOptions ) );
      return this;
    },

    tagPrefix: function( prefix ) {
      if ( !prefix || typeof prefix !== "string" ) {
        return this;
      }

      this.searchFilters.push( MakeMap.tagPrefix( prefix ) );

      return this;
    },

    limit: function( num ) {
      this.size = MakeMap.limit( num );
      return this;
    },

    field: function( field, direction ) {
      if ( !field || typeof field !== "string" ) {
        return this;
      }

      this.sortBy.push( MakeMap.field( field, direction ) );

      return this;
    },

    url: function( makeUrl ) {
      this.searchFilters.push( MakeMap.url( makeUrl ) );
      return this;
    },

    id: function( id ) {
      this.searchFilters.push( MakeMap.id( id ) );
      return this;
    },

    then: function( callback ) {
      var searchQuery = BASE_QUERY;

      searchQuery.size = this.size;

      if ( this.searchFilters.length ) {
        searchQuery.query.filtered.filter = {};
        searchQuery.query.filtered.filter.and = this.searchFilters;
      }

      if ( this.sortBy.length ) {
        searchQuery.sort = this.sortBy;
      }

      this.size = DEFAULT_SIZE;
      this.searchFilters = [];
      this.sortBy = [];

      doXHR( "GET", "/api/makes/search", escape( JSON.stringify( searchQuery ) ), callback );
    },

    create: function create( options, callback ) {
      doXHR( "POST", "/api/make", options, callback );
      return this;
    },

    update: function update( id, options, callback ) {
      doXHR( "PUT", "/api/make/" + id, options, callback );
      return this;
    },

    remove: function remove( id, callback ) {
      doXHR( "DELETE", "/api/make/" + id, callback );
      return this;
    }
  };
};

// Depending on the environment we need to export our "Make" object differently.
if ( module ) {
  request = require( "request" );
  escape = require( "querystring" ).escape;
  // npm install makeapi support
  module.exports = Make;
} else if ( typeof define === "function" && define.amd ) {
  escape = window.encodeURIComponent;
  // Support for requirejs
  define(function() {
    return Make;
  });
} else {
  escape = window.encodeURIComponent;
  // Support for include on individual pages.
  window.Make = Make;
}
<commit_msg>[#864624] Author field should use term filter rather than query filter w/ field query. Less prone to potential issues down the road.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 * You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

// Search Constants
var DEFAULT_SIZE = 10;

// For Versioning
var apiVersion = "@VERSION",
    makeAPI, Make, module, request,
    escape;

// Shim module so we can safely check what environment this is being included in.
module = module || undefined;

function doXHRServer( type, path, data, callback ) {
  if ( module ) {
    request({
      method: type,
      uri: path,
      json: data
    }, function( err, res, body ) {

      if ( err ) {
        callback( { error: err } );
        return;
      }

      if ( res.statusCode === 200 ) {
        callback( body );
      }
    });
  }
}

function doXHR( type, path, data, callback ) {

  if ( typeof data === "function" ) {
    callback = data;
    data = {};
  } else if ( typeof data === "string" ) {
    path += "?s=" + data;
    data = {};
  }

  path = makeAPI + path;

  if ( !module ) {
    var request = new XMLHttpRequest();

    request.open( type, path, true );
    request.setRequestHeader( "Content-Type", "application/json; charset=utf-8" );
    request.onreadystatechange = function() {
      if ( this.readyState === 4 ) {
        callback( JSON.parse( this.responseText ) );
      }
    };
    request.send( JSON.stringify( data ) );
  } else {
    doXHRServer( type, path, data, callback );
  }
}

// Shorthand for creating a Make Object
Make = function Make( options ) {
  makeAPI = options.makeAPI;

  var MakeMap = {
      author: function( name ) {
        return {
          term: {
            author: name
          }
        };
      },
      email: function( name ) {
        return {
          term: {
            email: name
          }
        };
      },
      tags: function( tagOptions ) {
        if ( typeof tagOptions.tags === "string" ) {
          tagOptions.tags = [ tagOptions.tags ];
        }

        return {
          terms: tagOptions
        };
      },
      field: function( field, direction ) {
        var sortObj;
        if ( direction ) {
          sortObj = {};
          sortObj[ field ] = direction;
          return sortObj;
        }

        return field;
      },
      limit: function ( num ) {
        return parseInt( num ) || DEFAULT_SIZE;
      },
      id: function( id ) {
        return {
          query: {
            field: {
              "_id": id
            }
          }
        };
      },
      tagPrefix: function( prefix ) {
        return {
          prefix: {
            "tags": prefix
          }
        };
      },
      url: function( makeUrl ) {
        return {
          term: {
            url: escape( makeUrl )
          }
        };
      }
    },
    BASE_QUERY = {
      query: {
        filtered: {
          query: {
            match_all: {}
          }
        }
      }
    };

  return {
    searchFilters: [],
    sortBy: [],
    size: DEFAULT_SIZE,

    find: function( options ) {
      options = options || {};

      for ( var key in options ) {
        if ( options.hasOwnProperty( key ) && MakeMap[ key ] ) {
          if ( key === "field" ) {
            this.sortedBy.push( MakeMap[ key ]( options[ key ] ) );
          } else {
            this.searchFilters.push( MakeMap[ key ]( options[ key ] ) );
          }
        }
      }

      return this;
    },

    author: function( name ) {
      this.searchFilters.push( MakeMap.author( name ) );
      return this;
    },

    email: function( name ) {
      this.searchFilters.push( MakeMap.email( name ) );
      return this;
    },

    tags: function( tagOptions ) {
      this.searchFilters.push( MakeMap.tags( tagOptions ) );
      return this;
    },

    tagPrefix: function( prefix ) {
      if ( !prefix || typeof prefix !== "string" ) {
        return this;
      }

      this.searchFilters.push( MakeMap.tagPrefix( prefix ) );

      return this;
    },

    limit: function( num ) {
      this.size = MakeMap.limit( num );
      return this;
    },

    field: function( field, direction ) {
      if ( !field || typeof field !== "string" ) {
        return this;
      }

      this.sortBy.push( MakeMap.field( field, direction ) );

      return this;
    },

    url: function( makeUrl ) {
      this.searchFilters.push( MakeMap.url( makeUrl ) );
      return this;
    },

    id: function( id ) {
      this.searchFilters.push( MakeMap.id( id ) );
      return this;
    },

    then: function( callback ) {
      var searchQuery = BASE_QUERY;

      searchQuery.size = this.size;

      if ( this.searchFilters.length ) {
        searchQuery.query.filtered.filter = {};
        searchQuery.query.filtered.filter.and = this.searchFilters;
      }

      if ( this.sortBy.length ) {
        searchQuery.sort = this.sortBy;
      }

      this.size = DEFAULT_SIZE;
      this.searchFilters = [];
      this.sortBy = [];

      doXHR( "GET", "/api/makes/search", escape( JSON.stringify( searchQuery ) ), callback );
    },

    create: function create( options, callback ) {
      doXHR( "POST", "/api/make", options, callback );
      return this;
    },

    update: function update( id, options, callback ) {
      doXHR( "PUT", "/api/make/" + id, options, callback );
      return this;
    },

    remove: function remove( id, callback ) {
      doXHR( "DELETE", "/api/make/" + id, callback );
      return this;
    }
  };
};

// Depending on the environment we need to export our "Make" object differently.
if ( module ) {
  request = require( "request" );
  escape = require( "querystring" ).escape;
  // npm install makeapi support
  module.exports = Make;
} else if ( typeof define === "function" && define.amd ) {
  escape = window.encodeURIComponent;
  // Support for requirejs
  define(function() {
    return Make;
  });
} else {
  escape = window.encodeURIComponent;
  // Support for include on individual pages.
  window.Make = Make;
}
<|endoftext|>