aurelia-templating-resources
Version: 
A standard set of behaviors, converters and other resources for use with the Aurelia templating library.
134 lines (121 loc) • 3.69 kB
text/typescript
import {
  createOverrideContext,
  BindingBehavior,
  ValueConverter,
  sourceContext,
  bindingMode,
  OverrideContext
} from 'aurelia-binding';
const oneTime = bindingMode.oneTime;
/**
 * Update the override context.
 * @param startIndex index in collection where to start updating.
 */
export function updateOverrideContexts(views, startIndex) {
  let length = views.length;
  if (startIndex > 0) {
    startIndex = startIndex - 1;
  }
  for (; startIndex < length; ++startIndex) {
    updateOverrideContext(views[startIndex].overrideContext, startIndex, length);
  }
}
/**
 * Creates a complete override context.
 * @param data The item's value.
 * @param index The item's index.
 * @param length The collections total length.
 * @param key The key in a key/value pair.
 */
export function createFullOverrideContext(repeat, data, index, length, key?: string): OverrideContext {
  let bindingContext = {};
  let overrideContext = createOverrideContext(bindingContext, repeat.scope.overrideContext);
  // is key/value pair (Map)
  if (typeof key !== 'undefined') {
    bindingContext[repeat.key] = key;
    bindingContext[repeat.value] = data;
  } else {
    bindingContext[repeat.local] = data;
  }
  updateOverrideContext(overrideContext, index, length);
  return overrideContext;
}
/**
 * Updates the override context.
 * @param context The context to be updated.
 * @param index The context's index.
 * @param length The collection's length.
 */
export function updateOverrideContext(overrideContext, index, length) {
  let first = (index === 0);
  let last = (index === length - 1);
  let even = index % 2 === 0;
  overrideContext.$index = index;
  overrideContext.$first = first;
  overrideContext.$last = last;
  overrideContext.$middle = !(first || last);
  overrideContext.$odd = !even;
  overrideContext.$even = even;
}
/**
 * Gets a repeat instruction's source expression.
 */
export function getItemsSourceExpression(instruction, attrName) {
  return instruction.behaviorInstructions
    .filter(bi => bi.originalAttrName === attrName)[0]
    .attributes
    .items
    .sourceExpression;
}
/**
 * Unwraps an expression to expose the inner, pre-converted / behavior-free expression.
 */
export function unwrapExpression(expression) {
  let unwrapped = false;
  while (expression instanceof BindingBehavior) {
    expression = expression.expression;
  }
  while (expression instanceof ValueConverter) {
    expression = expression.expression;
    unwrapped = true;
  }
  return unwrapped ? expression : null;
}
/**
 * Returns whether an expression has the OneTimeBindingBehavior applied.
 */
export function isOneTime(expression) {
  while (expression instanceof BindingBehavior) {
    if (expression.name === 'oneTime') {
      return true;
    }
    expression = expression.expression;
  }
  return false;
}
/**
 * Forces a binding instance to reevaluate.
 */
export function updateOneTimeBinding(binding) {
  if (binding.call && binding.mode === oneTime) {
    binding.call(sourceContext);
  } else if (binding.updateOneTimeBindings) {
    binding.updateOneTimeBindings();
  }
}
/**
 * Returns the index of the element in an array, optionally using a matcher function.
 */
export function indexOf(array, item, matcher, startIndex?: number) {
  if (!matcher) {
    // native indexOf is more performant than a for loop
    return array.indexOf(item);
  }
  const length = array.length;
  for (let index = startIndex || 0; index < length; index++) {
    if (matcher(array[index], item)) {
      return index;
    }
  }
  return -1;
}