text:
import {
  AfterContentInit,
  ChangeDetectorRef,
  ContentChildren,
  Directive,
  ElementRef,
  forwardRef,
  inject,
  Input,
  OnDestroy,
  Output,
  QueryList,
} from '@angular/core';
import {ActiveDescendantKeyManager, Highlightable, ListKeyManagerOption} from '@angular/cdk/a11y';
import {
  A,
  DOWN_ARROW,
  END,
  ENTER,
  hasModifierKey,
  HOME,
  LEFT_ARROW,
  RIGHT_ARROW,
  SPACE,
  UP_ARROW,
} from '@angular/cdk/keycodes';
import {BooleanInput, coerceArray, coerceBooleanProperty} from '@angular/cdk/coercion';
import {SelectionModel} from '@angular/cdk/collections';
import {defer, merge, Observable, Subject} from 'rxjs';
import {filter, map, startWith, switchMap, takeUntil} from 'rxjs/operators';
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from '@angular/forms';
import {Directionality} from '@angular/cdk/bidi';

/** The next id to use for creating unique DOM IDs. */
let nextId = 0;

/**
 * An implementation of SelectionModel that internally always represents the selection as a
 * multi-selection. This is necessary so that we can recover the full selection if the user
 * switches the listbox from single-selection to multi-selection after initialization.
 *
 * This selection model may report multiple selected values, even if it is in single-selection
 * mode. It is up to the user (CdkListbox) to check for invalid selections.
 */
class ListboxSelectionModel<T> extends SelectionModel<T> {
  constructor(
    public multiple = false,
    initiallySelectedValues?: T[],
    emitChanges = true,
    compareWith?: (o1: T, o2: T) => boolean,
  ) {
    super(true, initiallySelectedValues, emitChanges, compareWith);
  }

  override isMultipleSelection(): boolean {
    return this.multiple;
  }

  override select(...values: T[]) {
    // The super class is always in multi-selection mode, so we need to override the behavior if
    // this selection model actually belongs to a single-selection listbox.
    if (this.multiple) {
      return super.select(...values);
    } else {
      return super.setSelection(...values);
    }
  }
}

/** A selectable option in a listbox. */
@Directive({
  selector: '[cdkOption]',
  exportAs: 'cdkOption',
  host: {
    'role': 'option',
    'class': 'cdk-option',
    '[id]': 'id',
    '[attr.aria-selected]': 'isSelected()',
    '[attr.tabindex]': '_getTabIndex()',
    '[attr.aria-disabled]': 'disabled',
    '[class.cdk-option-active]': 'isActive()',
    '(click)': '_clicked.next($event)',
    '(focus)': '_handleFocus()',
  },
})
export class CdkOption<T = unknown> implements ListKeyManagerOption, Highlightable, OnDestroy {
  /** The id of the option's host element. */
  @Input()
  get id() {
    return this._id || this._generatedId;
  }
  set id(value) {
    this._id = value;
  }
  private _id: string;
  private _generatedId = `cdk-option-${nextId++}`;

  /** The value of this option. */
  @Input('cdkOption') value: T;

  /**
   * The text used to locate this item during listbox typeahead. If not specified,
   * the `textContent` of the item will be used.
   */
  @Input('cdkOptionTypeaheadLabel') typeaheadLabel: string;

  /** Whether this option is disabled. */
  @Input('cdkOptionDisabled')
  get disabled(): boolean {
    return this.listbox.disabled || this._disabled;
  }
  set disabled(value: BooleanInput) {
    this._disabled = coerceBooleanProperty(value);
  }
  private _disabled: boolean = false;

  /** The tabindex of the option when it is enabled. */
  @Input('tabindex')
  get enabledTabIndex() {
    return this._enabledTabIndex === undefined
      ? this.listbox.enabledTabIndex
      : this._enabledTabIndex;
  }
  set enabledTabIndex(value) {
    this._enabledTabIndex = value;
  }
  private _enabledTabIndex?: number | null;

  /** The option's host element */
  readonly element: HTMLElement = inject(ElementRef).nativeElement;

  /** The parent listbox this option belongs to. */
  protected readonly listbox: CdkListbox<T> = inject(CdkListbox);

  /** Emits when the option is destroyed. */
  protected destroyed = new Subject<void>();

  /** Emits when the option is clicked. */
  readonly _clicked = new Subject<MouseEvent>();

  /** Whether the option is currently active. */
  private _active = false;

  ngOnDestroy() {
    this.destroyed.next();
    this.destroyed.complete();
  }

  /** Whether this option is selected. */
  isSelected() {
    return this.listbox.isSelected(this);
  }

  /** Whether this option is active. */
  isActive() {
    return this._active;
  }

  /** Toggle the selected state of this option. */
  toggle() {
    this.listbox.toggle(this);
  }

  /** Select this option if it is not selected. */
  select() {
    this.listbox.select(this);
  }

  /** Deselect this option if it is selected. */
  deselect() {
    this.listbox.deselect(this);
  }

  /** Focus this option. */
  focus() {
    this.element.focus();
  }

  /** Get the label for this element which is required by the FocusableOption interface. */
  getLabel() {
    return (this.typeaheadLabel ?? this.element.textContent?.trim()) || '';
  }

  /**
   * Set the option as active.
   * @docs-private
   */
  setActiveStyles() {
    this._active = true;
  }

  /**
   * Set the option as inactive.
   * @docs-private
   */
  setInactiveStyles() {
    this._active = false;
  }

  /** Handle focus events on the option. */
  protected _handleFocus() {
    // Options can wind up getting focused in active descendant mode if the user clicks on them.
    // In this case, we push focus back to the parent listbox to prevent an extra tab stop when
    // the user performs a shift+tab.
    if (this.listbox.useActiveDescendant) {
      this.listbox._setActiveOption(this);
      this.listbox.focus();
    }
  }

  /** Get the tabindex for this option. */
  protected _getTabIndex() {
    if (this.listbox.useActiveDescendant || this.disabled) {
      return -1;
    }
    return this.isActive() ? this.enabledTabIndex : -1;
  }
}

@Directive({
  selector: '[cdkListbox]',
  exportAs: 'cdkListbox',
  host: {
    'role': 'listbox',
    'class': 'cdk-listbox',
    '[id]': 'id',
    '[attr.tabindex]': '_getTabIndex()',
    '[attr.aria-disabled]': 'disabled',
    '[attr.aria-multiselectable]': 'multiple',
    '[attr.aria-activedescendant]': '_getAriaActiveDescendant()',
    '[attr.aria-orientation]': 'orientation',
    '(focus)': '_handleFocus()',
    '(keydown)': '_handleKeydown($event)',
    '(focusout)': '_handleFocusOut($event)',
  },
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => CdkListbox),
      multi: true,
    },
  ],
})
export class CdkListbox<T = unknown> implements AfterContentInit, OnDestroy, ControlValueAccessor {
  /** The id of the option's host element. */
  @Input()
  get id() {
    return this._id || this._generatedId;
  }
  set id(value) {
    this._id = value;
  }
  private _id: string;
  private _generatedId = `cdk-listbox-${nextId++}`;

  /** The tabindex to use when the listbox is enabled. */
  @Input('tabindex')
  get enabledTabIndex() {
    return this._enabledTabIndex === undefined ? 0 : this._enabledTabIndex;
  }
  set enabledTabIndex(value) {
    this._enabledTabIndex = value;
  }
  private _enabledTabIndex?: number | null;

  /** The value selected in the listbox, represented as an array of option values. */
  @Input('cdkListboxValue')
  get value(): readonly T[] {
    return this._invalid ? [] : this.selectionModel.selected;
  }
  set value(value: readonly T[]) {
    this._setSelection(value);
  }

  /**
   * Whether the listbox allows multiple options to be selected. If the value switches from `true`
   * to `false`, and more than one option is selected, all options are deselected.
   */
  @Input('cdkListboxMultiple')
  get multiple(): boolean {
    return this.selectionModel.multiple;
  }
  set multiple(value: BooleanInput) {
    this.selectionModel.multiple = coerceBooleanProperty(value);

    if (this.options) {
      this._updateInternalValue();
    }
  }

  /** Whether the listbox is disabled. */
  @Input('cdkListboxDisabled')
  get disabled(): boolean {
    return this._disabled;
  }
  set disabled(value: BooleanInput) {
    this._disabled = coerceBooleanProperty(value);
  }
  private _disabled: boolean = false;

  /** Whether the listbox will use active descendant or will move focus onto the options. */
  @Input('cdkListboxUseActiveDescendant')
  get useActiveDescendant(): boolean {
    return this._useActiveDescendant;
  }
  set useActiveDescendant(shouldUseActiveDescendant: BooleanInput) {
    this._useActiveDescendant = coerceBooleanProperty(shouldUseActiveDescendant);
  }
  private _useActiveDescendant: boolean = false;

  /** The orientation of the listbox. Only affects keyboard interaction, not visual layout. */
  @Input('cdkListboxOrientation')
  get orientation() {
    return this._orientation;
  }
  set orientation(value: 'horizontal' | 'vertical') {
    this._orientation = value === 'horizontal' ? 'horizontal' : 'vertical';
    if (value === 'horizontal') {
      this.listKeyManager?.withHorizontalOrientation(this._dir?.value || 'ltr');
    } else {
      this.listKeyManager?.withVerticalOrientation();
    }
  }
  private _orientation: 'horizontal' | 'vertical' = 'vertical';

  /** The function used to compare option values. */
  @Input('cdkListboxCompareWith')
  get compareWith(): undefined | ((o1: T, o2: T) => boolean) {
    return this.selectionModel.compareWith;
  }
  set compareWith(fn: undefined | ((o1: T, o2: T) => boolean)) {
    this.selectionModel.compareWith = fn;
  }

  /**
   * Whether the keyboard navigation should wrap when the user presses arrow down on the last item
   * or arrow up on the first item.
   */
  @Input('cdkListboxNavigationWrapDisabled')
  get navigationWrapDisabled() {
    return this._navigationWrapDisabled;
  }
  set navigationWrapDisabled(wrap: BooleanInput) {
    this._navigationWrapDisabled = coerceBooleanProperty(wrap);
    this.listKeyManager?.withWrap(!this._navigationWrapDisabled);
  }
  private _navigationWrapDisabled = false;

  /** Whether keyboard navigation should skip over disabled items. */
  @Input('cdkListboxNavigatesDisabledOptions')
  get navigateDisabledOptions() {
    return this._navigateDisabledOptions;
  }
  set navigateDisabledOptions(skip: BooleanInput) {
    this._navigateDisabledOptions = coerceBooleanProperty(skip);
    this.listKeyManager?.skipPredicate(
      this._navigateDisabledOptions ? this._skipNonePredicate : this._skipDisabledPredicate,
    );
  }
  private _navigateDisabledOptions = false;

  /** Emits when the selected value(s) in the listbox change. */
  @Output('cdkListboxValueChange') readonly valueChange = new Subject<ListboxValueChangeEvent<T>>();

  /** The child options in this listbox. */
  @ContentChildren(CdkOption, {descendants: true}) protected options: QueryList<CdkOption<T>>;

  /** The selection model used by the listbox. */
  protected selectionModel = new ListboxSelectionModel<T>();

  /** The key manager that manages keyboard navigation for this listbox. */
  protected listKeyManager: ActiveDescendantKeyManager<CdkOption<T>>;

  /** Emits when the listbox is destroyed. */
  protected readonly destroyed = new Subject<void>();

  /** The host element of the listbox. */
  protected readonly element: HTMLElement = inject(ElementRef).nativeElement;

  /** The change detector for this listbox. */
  protected readonly changeDetectorRef = inject(ChangeDetectorRef);

  /** Whether the currently selected value in the selection model is invalid. */
  private _invalid = false;

  /** The last user-triggered option. */
  private _lastTriggered: CdkOption<T> | null = null;

  /** Callback called when the listbox has been touched */
  private _onTouched = () => {};

  /** Callback called when the listbox value changes */
  private _onChange: (value: readonly T[]) => void = () => {};

  /** Emits when an option has been clicked. */
  private _optionClicked = defer(() =>
    (this.options.changes as Observable<CdkOption<T>[]>).pipe(
      startWith(this.options),
      switchMap(options =>
        merge(...options.map(option => option._clicked.pipe(map(event => ({option, event}))))),
      ),
    ),
  );

  /** The directionality of the page. */
  private readonly _dir = inject(Directionality, {optional: true});

  /** A predicate that skips disabled options. */
  private readonly _skipDisabledPredicate = (option: CdkOption<T>) => option.disabled;

  /** A predicate that does not skip any options. */
  private readonly _skipNonePredicate = () => false;

  ngAfterContentInit() {
    if (typeof ngDevMode === 'undefined' || ngDevMode) {
      this._verifyNoOptionValueCollisions();
      this._verifyOptionValues();
    }

    this._initKeyManager();

    // Update the internal value whenever the options or the model value changes.
    merge(this.selectionModel.changed, this.options.changes)
      .pipe(startWith(null), takeUntil(this.destroyed))
      .subscribe(() => this._updateInternalValue());

    this._optionClicked
      .pipe(
        filter(({option}) => !option.disabled),
        takeUntil(this.destroyed),
      )
      .subscribe(({option, event}) => this._handleOptionClicked(option, event));
  }

  ngOnDestroy() {
    this.listKeyManager?.destroy();
    this.destroyed.next();
    this.destroyed.complete();
  }

  /**
   * Toggle the selected state of the given option.
   * @param option The option to toggle
   */
  toggle(option: CdkOption<T>) {
    this.toggleValue(option.value);
  }

  /**
   * Toggle the selected state of the given value.
   * @param value The value to toggle
   */
  toggleValue(value: T) {
    if (this._invalid) {
      this.selectionModel.clear(false);
    }
    this.selectionModel.toggle(value);
  }

  /**
   * Select the given option.
   * @param option The option to select
   */
  select(option: CdkOption<T>) {
    this.selectValue(option.value);
  }

  /**
   * Select the given value.
   * @param value The value to select
   */
  selectValue(value: T) {
    if (this._invalid) {
      this.selectionModel.clear(false);
    }
    this.selectionModel.select(value);
  }

  /**
   * Deselect the given option.
   * @param option The option to deselect
   */
  deselect(option: CdkOption<T>) {
    this.deselectValue(option.value);
  }

  /**
   * Deselect the given value.
   * @param value The value to deselect
   */
  deselectValue(value: T) {
    if (this._invalid) {
      this.selectionModel.clear(false);
    }
    this.selectionModel.deselect(value);
  }

  /**
   * Set the selected state of all options.
   * @param isSelected The new selected state to set
   */
  setAllSelected(isSelected: boolean) {
    if (!isSelected) {
      this.selectionModel.clear();
    } else {
      if (this._invalid) {
        this.selectionModel.clear(false);
      }
      this.selectionModel.select(...this.options.map(option => option.value));
    }
  }

  /**
   * Get whether the given option is selected.
   * @param option The option to get the selected state of
   */
  isSelected(option: CdkOption<T>) {
    return this.isValueSelected(option.value);
  }

  /**
   * Get whether the given value is selected.
   * @param value The value to get the selected state of
   */
  isValueSelected(value: T) {
    if (this._invalid) {
      return false;
    }
    return this.selectionModel.isSelected(value);
  }

  /**
   * Registers a callback to be invoked when the listbox's value changes from user input.
   * @param fn The callback to register
   * @docs-private
   */
  registerOnChange(fn: (value: readonly T[]) => void): void {
    this._onChange = fn;
  }

  /**
   * Registers a callback to be invoked when the listbox is blurred by the user.
   * @param fn The callback to register
   * @docs-private
   */
  registerOnTouched(fn: () => {}): void {
    this._onTouched = fn;
  }

  /**
   * Sets the listbox's value.
   * @param value The new value of the listbox
   * @docs-private
   */
  writeValue(value: readonly T[]): void {
    this._setSelection(value);
    this._verifyOptionValues();
  }

  /**
   * Sets the disabled state of the listbox.
   * @param isDisabled The new disabled state
   * @docs-private
   */
  setDisabledState(isDisabled: boolean): void {
    this.disabled = isDisabled;
  }

  /** Focus the listbox's host element. */
  focus() {
    this.element.focus();
  }

  /**
   * Triggers the given option in response to user interaction.
   * - In single selection mode: selects the option and deselects any other selected option.
   * - In multi selection mode: toggles the selected state of the option.
   * @param option The option to trigger
   */
  protected triggerOption(option: CdkOption<T> | null) {
    if (option && !option.disabled) {
      this._lastTriggered = option;
      const changed = this.multiple
        ? this.selectionModel.toggle(option.value)
        : this.selectionModel.select(option.value);
      if (changed) {
        this._onChange(this.value);
        this.valueChange.next({
          value: this.value,
          listbox: this,
          option: option,
        });
      }
    }
  }

  /**
   * Trigger the given range of options in response to user interaction.
   * Should only be called in multi-selection mode.
   * @param trigger The option that was triggered
   * @param from The start index of the options to toggle
   * @param to The end index of the options to toggle
   * @param on Whether to toggle the option range on
   */
  protected triggerRange(trigger: CdkOption<T> | null, from: number, to: number, on: boolean) {
    if (this.disabled || (trigger && trigger.disabled)) {
      return;
    }
    this._lastTriggered = trigger;
    const isEqual = this.compareWith ?? Object.is;
    const updateValues = [...this.options]
      .slice(Math.max(0, Math.min(from, to)), Math.min(this.options.length, Math.max(from, to) + 1))
      .filter(option => !option.disabled)
      .map(option => option.value);
    const selected = [...this.value];
    for (const updateValue of updateValues) {
      const selectedIndex = selected.findIndex(selectedValue =>
        isEqual(selectedValue, updateValue),
      );
      if (on && selectedIndex === -1) {
        selected.push(updateValue);
      } else if (!on && selectedIndex !== -1) {
        selected.splice(selectedIndex, 1);
      }
    }
    let changed = this.selectionModel.setSelection(...selected);
    if (changed) {
      this._onChange(this.value);
      this.valueChange.next({
        value: this.value,
        listbox: this,
        option: trigger,
      });
    }
  }

  /**
   * Sets the given option as active.
   * @param option The option to make active
   */
  _setActiveOption(option: CdkOption<T>) {
    this.listKeyManager.setActiveItem(option);
  }

  /** Called when the listbox receives focus. */
  protected _handleFocus() {
    if (!this.useActiveDescendant) {
      this.listKeyManager.setNextItemActive();
      this._focusActiveOption();
    }
  }

  /** Called when the user presses keydown on the listbox. */
  protected _handleKeydown(event: KeyboardEvent) {
    if (this._disabled) {
      return;
    }

    const {keyCode} = event;
    const previousActiveIndex = this.listKeyManager.activeItemIndex;
    const ctrlKeys = ['ctrlKey', 'metaKey'] as const;

    if (this.multiple && keyCode === A && hasModifierKey(event, ...ctrlKeys)) {
      // Toggle all options off if they're all selected, otherwise toggle them all on.
      this.triggerRange(
        null,
        0,
        this.options.length - 1,
        this.options.length !== this.value.length,
      );
      event.preventDefault();
      return;
    }

    if (
      this.multiple &&
      (keyCode === SPACE || keyCode === ENTER) &&
      hasModifierKey(event, 'shiftKey')
    ) {
      if (this.listKeyManager.activeItem && this.listKeyManager.activeItemIndex != null) {
        this.triggerRange(
          this.listKeyManager.activeItem,
          this._getLastTriggeredIndex() ?? this.listKeyManager.activeItemIndex,
          this.listKeyManager.activeItemIndex,
          !this.listKeyManager.activeItem.isSelected(),
        );
      }
      event.preventDefault();
      return;
    }

    if (
      this.multiple &&
      keyCode === HOME &&
      hasModifierKey(event, ...ctrlKeys) &&
      hasModifierKey(event, 'shiftKey')
    ) {
      const trigger = this.listKeyManager.activeItem;
      if (trigger) {
        const from = this.listKeyManager.activeItemIndex!;
        this.listKeyManager.setFirstItemActive();
        this.triggerRange(
          trigger,
          from,
          this.listKeyManager.activeItemIndex!,
          !trigger.isSelected(),
        );
      }
      event.preventDefault();
      return;
    }

    if (
      this.multiple &&
      keyCode === END &&
      hasModifierKey(event, ...ctrlKeys) &&
      hasModifierKey(event, 'shiftKey')
    ) {
      const trigger = this.listKeyManager.activeItem;
      if (trigger) {
        const from = this.listKeyManager.activeItemIndex!;
        this.listKeyManager.setLastItemActive();
        this.triggerRange(
          trigger,
          from,
          this.listKeyManager.activeItemIndex!,
          !trigger.isSelected(),
        );
      }
      event.preventDefault();
      return;
    }

    if (keyCode === SPACE || keyCode === ENTER) {
      this.triggerOption(this.listKeyManager.activeItem);
      event.preventDefault();
      return;
    }

    const isNavKey =
      keyCode === UP_ARROW ||
      keyCode === DOWN_ARROW ||
      keyCode === LEFT_ARROW ||
      keyCode === RIGHT_ARROW ||
      keyCode === HOME ||
      keyCode === END;
    this.listKeyManager.onKeydown(event);
    // Will select an option if shift was pressed while navigating to the option
    if (isNavKey && event.shiftKey && previousActiveIndex !== this.listKeyManager.activeItemIndex) {
      this.triggerOption(this.listKeyManager.activeItem);
    }
  }

  /**
   * Called when the focus leaves an element in the listbox.
   * @param event The focusout event
   */
  protected _handleFocusOut(event: FocusEvent) {
    const otherElement = event.relatedTarget as Element;
    if (this.element !== otherElement && !this.element.contains(otherElement)) {
      this._onTouched();
    }
  }

  /** Get the id of the active option if active descendant is being used. */
  protected _getAriaActiveDescendant(): string | null | undefined {
    return this._useActiveDescendant ? this.listKeyManager?.activeItem?.id : null;
  }

  /** Get the tabindex for the listbox. */
  protected _getTabIndex() {
    if (this.disabled) {
      return -1;
    }
    return this.useActiveDescendant || !this.listKeyManager.activeItem ? this.enabledTabIndex : -1;
  }

  /** Initialize the key manager. */
  private _initKeyManager() {
    this.listKeyManager = new ActiveDescendantKeyManager(this.options)
      .withWrap(!this._navigationWrapDisabled)
      .withTypeAhead()
      .withHomeAndEnd()
      .withAllowedModifierKeys(['shiftKey'])
      .skipPredicate(
        this._navigateDisabledOptions ? this._skipNonePredicate : this._skipDisabledPredicate,
      );

    if (this.orientation === 'vertical') {
      this.listKeyManager.withVerticalOrientation();
    } else {
      this.listKeyManager.withHorizontalOrientation(this._dir?.value || 'ltr');
    }

    this.listKeyManager.change.subscribe(() => this._focusActiveOption());
  }

  /** Focus the active option. */
  private _focusActiveOption() {
    if (!this.useActiveDescendant) {
      this.listKeyManager.activeItem?.focus();
    }
    this.changeDetectorRef.markForCheck();
  }

  /**
   * Set the selected values.
   * @param value The list of new selected values.
   */
  private _setSelection(value: readonly T[]) {
    if (this._invalid) {
      this.selectionModel.clear(false);
    }
    this.selectionModel.setSelection(...this._coerceValue(value));
  }

  /** Update the internal value of the listbox based on the selection model. */
  private _updateInternalValue() {
    const indexCache = new Map<T, number>();
    this.selectionModel.sort((a: T, b: T) => {
      const aIndex = this._getIndexForValue(indexCache, a);
      const bIndex = this._getIndexForValue(indexCache, b);
      return aIndex - bIndex;
    });
    const selected = this.selectionModel.selected;
    this._invalid =
      (!this.multiple && selected.length > 1) || !!this._getInvalidOptionValues(selected).length;
    this.changeDetectorRef.markForCheck();
  }

  /**
   * Gets the index of the given value in the given list of options.
   * @param cache The cache of indices found so far
   * @param value The value to find
   * @return The index of the value in the options list
   */
  private _getIndexForValue(cache: Map<T, number>, value: T) {
    const isEqual = this.compareWith || Object.is;
    if (!cache.has(value)) {
      let index = -1;
      for (let i = 0; i < this.options.length; i++) {
        if (isEqual(value, this.options.get(i)!.value)) {
          index = i;
          break;
        }
      }
      cache.set(value, index);
    }
    return cache.get(value)!;
  }

  /**
   * Handle the user clicking an option.
   * @param option The option that was clicked.
   */
  private _handleOptionClicked(option: CdkOption<T>, event: MouseEvent) {
    event.preventDefault();
    this.listKeyManager.setActiveItem(option);
    if (event.shiftKey && this.multiple) {
      this.triggerRange(
        option,
        this._getLastTriggeredIndex() ?? this.listKeyManager.activeItemIndex!,
        this.listKeyManager.activeItemIndex!,
        !option.isSelected(),
      );
    } else {
      this.triggerOption(option);
    }
  }

  /** Verifies that no two options represent the same value under the compareWith function. */
  private _verifyNoOptionValueCollisions() {
    this.options.changes.pipe(startWith(this.options), takeUntil(this.destroyed)).subscribe(() => {
      const isEqual = this.compareWith ?? Object.is;
      for (let i = 0; i < this.options.length; i++) {
        const option = this.options.get(i)!;
        let duplicate: CdkOption<T> | null = null;
        for (let j = i + 1; j < this.options.length; j++) {
          const other = this.options.get(j)!;
          if (isEqual(option.value, other.value)) {
            duplicate = other;
            break;
          }
        }
        if (duplicate) {
          // TODO(mmalerba): Link to docs about this.
          if (this.compareWith) {
            console.warn(
              `Found multiple CdkOption representing the same value under the given compareWith function`,
              {
                option1: option.element,
                option2: duplicate.element,
                compareWith: this.compareWith,
              },
            );
          } else {
            console.warn(`Found multiple CdkOption with the same value`, {
              option1: option.element,
              option2: duplicate.element,
            });
          }
          return;
        }
      }
    });
  }

  /** Verifies that the option values are valid. */
  private _verifyOptionValues() {
    if (this.options && (typeof ngDevMode === 'undefined' || ngDevMode)) {
      const selected = this.selectionModel.selected;
      const invalidValues = this._getInvalidOptionValues(selected);

      if (!this.multiple && selected.length > 1) {
        throw Error('Listbox cannot have more than one selected value in multi-selection mode.');
      }

      if (invalidValues.length) {
        throw Error('Listbox has selected values that do not match any of its options.');
      }
    }
  }

  /**
   * Coerces a value into an array representing a listbox selection.
   * @param value The value to coerce
   * @return An array
   */
  private _coerceValue(value: readonly T[]) {
    return value == null ? [] : coerceArray(value);
  }

  /**
   * Get the sublist of values that do not represent valid option values in this listbox.
   * @param values The list of values
   * @return The sublist of values that are not valid option values
   */
  private _getInvalidOptionValues(values: readonly T[]) {
    const isEqual = this.compareWith || Object.is;
    const validValues = (this.options || []).map(option => option.value);
    return values.filter(value => !validValues.some(validValue => isEqual(value, validValue)));
  }

  /** Get the index of the last triggered option. */
  private _getLastTriggeredIndex() {
    const index = this.options.toArray().indexOf(this._lastTriggered!);
    return index === -1 ? null : index;
  }
}

/** Change event that is fired whenever the value of the listbox changes. */
export interface ListboxValueChangeEvent<T> {
  /** The new value of the listbox. */
  readonly value: readonly T[];

  /** Reference to the listbox that emitted the event. */
  readonly listbox: CdkListbox<T>;

  /** Reference to the option that was triggered. */
  readonly option: CdkOption<T> | null;
}


text:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE ldml SYSTEM "../../common/dtd/ldml.dtd">
<!-- Copyright © 1991-2014 Unicode, Inc.
CLDR data files are interpreted according to the LDML specification (http://unicode.org/reports/tr35/)
For terms of use, see http://www.unicode.org/copyright.html
-->
<ldml>
	<identity>
		<version number="$Revision: 13159 $"/>
		<language type="ayb"/>
	</identity>
	<layout>
		<orientation>
			<characterOrder>left-to-right</characterOrder>
			<lineOrder>top-to-bottom</lineOrder>
		</orientation>
	</layout>
	<characters>
		<exemplarCharacters draft="unconfirmed">[a b d ɗ e ɛ f g h i j k l m n o ɔ p r s t u v w x z]</exemplarCharacters>
		<exemplarCharacters type="auxiliary" draft="unconfirmed">[c q y]</exemplarCharacters>
	</characters>
</ldml>

redpajama_set_name: RedPajamaGithub


text:
function [J grad] = nnCostFunction(nn_params, ...
                                   input_layer_size, ...
                                   hidden_layer_size, ...
                                   num_labels, ...
                                   X, y, lambda)
%NNCOSTFUNCTION Implements the neural network cost function for a two layer
%neural network which performs classification
%   [J grad] = NNCOSTFUNCTON(nn_params, hidden_layer_size, num_labels, ...
%   X, y, lambda) computes the cost and gradient of the neural network. The
%   parameters for the neural network are "unrolled" into the vector
%   nn_params and need to be converted back into the weight matrices.
%
%   The returned parameter grad should be a "unrolled" vector of the
%   partial derivatives of the neural network.
%

% Reshape nn_params back into the parameters Theta1 and Theta2, the weight matrices
% for our 2 layer neural network
Theta1 = reshape(nn_params(1:hidden_layer_size * (input_layer_size + 1)), ...
                 hidden_layer_size, (input_layer_size + 1));

Theta2 = reshape(nn_params((1 + (hidden_layer_size * (input_layer_size + 1))):end), ...
                 num_labels, (hidden_layer_size + 1));

% Setup some useful variables
m = size(X, 1);

% You need to return the following variables correctly
J = 0;
Theta1_grad = zeros(size(Theta1));
Theta2_grad = zeros(size(Theta2));

% ====================== YOUR CODE HERE ======================
% Instructions: You should complete the code by working through the
%               following parts.
%
% Part 1: Feedforward the neural network and return the cost in the
%         variable J. After implementing Part 1, you can verify that your
%         cost function computation is correct by verifying the cost
%         computed in ex4.m
%
% Part 2: Implement the backpropagation algorithm to compute the gradients
%         Theta1_grad and Theta2_grad. You should return the partial derivatives of
%         the cost function with respect to Theta1 and Theta2 in Theta1_grad and
%         Theta2_grad, respectively. After implementing Part 2, you can check
%         that your implementation is correct by running checkNNGradients
%
%         Note: The vector y passed into the function is a vector of labels
%               containing values from 1..K. You need to map this vector into a
%               binary vector of 1's and 0's to be used with the neural network
%               cost function.
%
%         Hint: We recommend implementing backpropagation using a for-loop
%               over the training examples if you are implementing it for the
%               first time.
%
% Part 3: Implement regularization with the cost function and gradients.
%
%         Hint: You can implement this around the code for
%               backpropagation. That is, you can compute the gradients for
%               the regularization separately and then add them to Theta1_grad
%               and Theta2_grad from Part 2.
%

% Part 1
y_recoded = eye(num_labels)(y,:);

h1 = sigmoid([ones(m, 1) X] * Theta1');
h2 = sigmoid([ones(m, 1) h1] * Theta2');

cosfFn = sum(sum(-y_recoded .* log(h2) - (1 - y_recoded) .* log(1 - h2)));
regularization = (sum(sum(Theta1(:,2:end) .^ 2)) + sum(sum(Theta2(:,2:end) .^ 2))) * lambda / (2 * m);

J = (1 / m) * cosfFn + regularization;

% Part 2

for t = 1:m
  % For the input layer, where l=1:
  a1 = [1; X(t,:)'];

  % For the hidden layers, where l=2:
  z2 = Theta1 * a1;
  a2 = [1; sigmoid(z2)];

  z3 = Theta2 * a2;
  a3 = sigmoid(z3);

  yy = ([1:num_labels]==y(t))';
  % For the delta values:
  delta_3 = a3 - yy;

  delta_2 = (Theta2' * delta_3) .* [1; sigmoidGradient(z2)];
  delta_2 = delta_2(2:end); % Taking of the bias row

  % delta_1 is not calculated because we do not associate error with the input

  % Big delta update
  Theta1_grad = Theta1_grad + delta_2 * a1';
  Theta2_grad = Theta2_grad + delta_3 * a2';
end

Theta1_grad = (1/m) * Theta1_grad + (lambda/m) * [zeros(size(Theta1, 1), 1) Theta1(:,2:end)];
Theta2_grad = (1/m) * Theta2_grad + (lambda/m) * [zeros(size(Theta2, 1), 1) Theta2(:,2:end)];

% -------------------------------------------------------------

% =========================================================================

% Unroll gradients
grad = [Theta1_grad(:) ; Theta2_grad(:)];

end


text:
  La Chapelle-Rambaud est
       un village
       géographiquement positionné dans le département de Haute-Savoie en Rhône-Alpes. Elle comptait 210 habitants en 2008.

À proximité de La Chapelle-Rambaud sont positionnées géographiquement les villes de
Le Sappey localisée à 6 km, 375 habitants,
Etaux localisée à 4 km, 1 520 habitants,
Cornier à 5 km, 1 026 habitants,
Pers-Jussy située à 4 km, 2 426 habitants,
Amancy localisée à 6 km, 1 872 habitants,
Menthonnex-en-Bornes située à 5 km, 814 habitants,
 entre autres. De plus, La Chapelle-Rambaud est située à seulement 20 km de Annecy.

Le nombre d'habitations, à La Chapelle-Rambaud, était réparti en 2011 en cinq appartements et 117 maisons soit
 un marché plutôt équilibré.

La ville compte quelques  aménagements, elle dispose, entre autres, de un domaine skiable, un centre d'équitation et un terrain de sport.

Si vous envisagez de emmenager à La Chapelle-Rambaud, vous pourrez facilement trouver une maison à acheter.


text:
layout: post
title: Secure C++11/14 ZeroMQ Applications with ZMQPP
---

Coming soon...


text:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.cms.dao.admin.RoleMapper" >
    <resultMap type="com.cms.entities.admin.RoleInfo" id="roleMap">
        <!-- 主键必须用id标签映射 -->
        <id column="DUTY_ID" property="id" jdbcType="NUMERIC"/>
        <!-- column为数据库字段名,property为实体类属性名 -->
        <result column="DUTY_NAME" property="name" jdbcType="VARCHAR" />
        <result column="STATUS" property="status" jdbcType="VARCHAR"/>
        <result column="DESCRIPTION" property="description" jdbcType="VARCHAR" />
    </resultMap>
    <select id="roleQueryPage" parameterType="java.util.Map" resultMap="roleMap">
        select *
        from B_DUTY u
        WHERE  1=1 AND u.STATUS='A'
            <if test="name!=null and name !=''">
              AND u.DUTY_NAME LIKE '%'||#{name}||'%'
            </if>
        <if test="description!=null and description !=''">
            AND u.DESCRIPTION LIKE '%'||#{description}||'%'
        </if>
    </select>
    <insert id="insertRole" parameterType="com.cms.entities.admin.RoleInfo" >
        <selectKey keyProperty="id" resultType="int" order="BEFORE">
            select SEQ_AMS.nextval  from dual
        </selectKey>
        insert into B_DUTY (DUTY_ID, DUTY_NAME, STATUS, DESCRIPTION)
        values(#{id}, #{name}, #{status},#{description})
    </insert>
    <select id="getRoleById" parameterType="java.lang.Integer" resultMap="roleMap">
        SELECT * FROM B_DUTY t WHERE t.DUTY_ID=#{id}
    </select>
    <update id="updateRole" parameterType="com.cms.entities.admin.UserInfo">
        UPDATE B_DUTY
        SET DUTY_NAME=#{name, jdbcType=VARCHAR},DESCRIPTION=#{description, jdbcType=VARCHAR}
        WHERE DUTY_ID=#{id}
    </update>
    <update id="deleteRole">
        UPDATE B_DUTY set STATUS='D' WHERE DUTY_ID  in
        <foreach collection="array" item="id" open="(" separator="," close=")">#{id}</foreach>
    </update>
    <select id="findAll"  resultMap="roleMap">
        SELECT * FROM B_DUTY u WHERE u.STATUS='A'
    </select>
    <select id="getAccountRole" parameterType="java.lang.Integer" resultMap="roleMap">
        SELECT * FROM B_DUTY u WHERE u.DUTY_ID IN (
          SELECT ad.duty_id FROM b_account_duty ad WHERE ad.acct_id=#{accountId}
        )
    </select>
    <delete id="deleteSourceDuty" parameterType="java.lang.Integer">
      DELETE  FROM b_duty_source WHERE duty_id=#{roleId}
    </delete>
    <insert id="insertSourceDuty" parameterType="java.util.List">
       <selectKey keyProperty="id" resultType="int" order="BEFORE">
            select SEQ_AMS.nextval  from dual
        </selectKey>
        insert into b_duty_source (DUTY_SOURCE_ID, SOURCE_ID, DUTY_ID)
        SELECT  SEQ_AMS.nextval,a.* FROM (
        <foreach collection="list" item="item" index="index" separator="UNION ALL" >
            SELECT
            #{item.sourceId},#{item.roleId} FROM  dual
        </foreach>) a
    </insert>
</mapper>


text:
<?php
namespace Craft;

class FieldLayoutTabRecord extends BaseRecord
{
	// Public Methods
	// =========================================================================

	/**
	 * @inheritDoc BaseRecord::getTableName()
	 *
	 * @return string
	 */
	public function getTableName()
	{
		return 'fieldlayouttabs';
	}

	/**
	 * @inheritDoc BaseRecord::defineRelations()
	 *
	 * @return array
	 */
	public function defineRelations()
	{
		return array(
			'layout' => array(static::BELONGS_TO, 'FieldLayoutRecord', 'required' => true, 'onDelete' => static::CASCADE),
			'fields' => array(static::HAS_MANY, 'FieldLayoutFieldRecord', 'tabId', 'order' => 'fields.sortOrder'),
		);
	}

	/**
	 * @inheritDoc BaseRecord::defineIndexes()
	 *
	 * @return array
	 */
	public function defineIndexes()
	{
		return array(
			array('columns' => array('sortOrder')),
		);
	}

	// Protected Methods
	// =========================================================================

	/**
	 * @inheritDoc BaseRecord::defineAttributes()
	 *
	 * @return array
	 */
	protected function defineAttributes()
	{
		return array(
			'name'      => array(AttributeType::Name, 'required' => true),
			'sortOrder' => AttributeType::SortOrder,
		);
	}
}


text:
'use strict';

const _ = require('lodash');
const chromeApi = require('chromeback')(chrome);

const alt = require('../alt');
const getErrorMessage = require('../lib/get-error-message');
const { setConfig } = require('../actions/config');

class ConfigStore {
  constructor(){
    this.state = {
      autoFill: null,
      errorMessage: null
    };

    this.bindListeners({
      handleSetConfig: setConfig
    });

    chromeApi.storage.sync.get('autoFill', (err, res)=>{
      if(err){
        this.setState({ errorMessage: getErrorMessage(err) });
      } else {
        this.setState({ autoFill: res.autoFill });
      }
    });
  }

  handleSetConfig(settings){
    chromeApi.storage.sync.set({ autoFill: settings }, (err)=>{
      if(err){
        this.setState({ errorMessage: getErrorMessage(err) });
      } else {
        this.setState({ autoFill: settings });
        if(settings){
          chrome.tabs.query({ url: 'https://github.com/*/*' }, function(tabs){
            _.forEach(tabs, function(tab){
              chrome.tabs.sendMessage(tab.id, { fillPR: true });
            });
          });
        }
      }
    });
  }
}

ConfigStore.config = {
  stateKey: 'state'
};

module.exports = alt.createStore(ConfigStore);


text:
namespace starboard {
namespace android {
namespace shared {

class VideoFrameTracker {
 public:
  explicit VideoFrameTracker(int max_pending_frames_size)
      : max_pending_frames_size_(max_pending_frames_size) {}

  SbTime seek_to_time() const;

  void OnInputBuffer(SbTime timestamp);

  void OnFrameRendered(int64_t frame_timestamp);

  void Seek(SbTime seek_to_time);

  int UpdateAndGetDroppedFrames();

 private:
  void UpdateDroppedFrames();

  ::starboard::shared::starboard::ThreadChecker thread_checker_;

  std::list<SbTime> frames_to_be_rendered_;

  const int max_pending_frames_size_;
  int dropped_frames_ = 0;
  SbTime seek_to_time_ = 0;

  Mutex rendered_frames_mutex_;
  std::vector<SbTime> rendered_frames_on_tracker_thread_;
  std::vector<SbTime> rendered_frames_on_decoder_thread_;
};

}  // namespace shared
}  // namespace android
}  // namespace starboard

#endif  // STARBOARD_ANDROID_SHARED_VIDEO_FRAME_TRACKER_H_


text:
"""Exporters for tf.estimator training."""

from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import os
import re

from language.common.utils import file_utils
import six
import tensorflow.compat.v1 as tf
from tensorflow.compat.v1 import estimator as tf_estimator
import tensorflow_hub as hub

class BestSavedModelAndCheckpointExporter(tf_estimator.BestExporter):
  """Exporter that saves the best SavedModel and checkpoint."""

  def __init__(self,
               eval_spec_name,
               serving_input_receiver_fn,
               compare_fn=None,
               metric_name=None,
               higher_is_better=True,
               assets_extra=None):
    """Creates an exporter that compares models on the given eval and metric.

    While the SavedModel is useful for inference, the checkpoint is useful for
    warm-starting another stage of training (e.g., fine-tuning).

    Args:
      eval_spec_name: Name of the EvalSpec to use to compare models.
      serving_input_receiver_fn: Callable that returns a ServingInputReceiver.
      compare_fn: Callable that compares eval metrics of two models.  See
        tf.estimator.BestExporter for details on the expected API.  Specify
        either this or `metric_name`.
      metric_name: Name of the eval metric to use to compare models.  Specify
        either this or `compare_fn`.
      higher_is_better: Whether higher or lower eval metric values are better.
        Only used when `metric_name` is specified.
      assets_extra: An optional dict specifying how to populate the
        `assets.extra` directory within the exported SavedModel. Each key should
        give the destination path (including the filename) relative to the
        `assets.extra` directory. The corresponding value gives the full path of
        the source file to be copied. For example, the simple case of copying a
        single file without renaming it is specified as
        {'my_asset_file.txt': '/path/to/my_asset_file.txt'}.
    """
    self._metric_name = metric_name

    def _default_compare_fn(best_eval_result, current_eval_result):
      """Returns True if the current metric is better than the best metric."""
      if higher_is_better:
        return current_eval_result[metric_name] > best_eval_result[metric_name]
      else:
        return current_eval_result[metric_name] < best_eval_result[metric_name]

    super(BestSavedModelAndCheckpointExporter, self).__init__(
        name="best_%s" % eval_spec_name,
        serving_input_receiver_fn=serving_input_receiver_fn,
        event_file_pattern="eval_%s/*.tfevents.*" % eval_spec_name,
        compare_fn=compare_fn or _default_compare_fn,
        assets_extra=assets_extra)

  def export(self, estimator, export_path, checkpoint_path, eval_result,
             is_the_final_export):
    """Implements Exporter.export()."""
    # Since export() returns None if export was skipped, we can use this to
    # detect when the current model is the new best model.
    exported_dir = super(BestSavedModelAndCheckpointExporter, self).export(
        estimator=estimator,
        export_path=export_path,
        checkpoint_path=checkpoint_path,
        eval_result=eval_result,
        is_the_final_export=is_the_final_export)
    if exported_dir is None:
      return None  # best model unchanged

    checkpoint_dir = os.path.join(export_path, "checkpoint")
    tf.logging.info("Saving new best checkpoint to %s", checkpoint_dir)
    file_utils.make_empty_dir(checkpoint_dir)
    file_utils.copy_files_to_dir(
        source_filepattern=checkpoint_path + ".*", dest_dir=checkpoint_dir)

    # Also save the new best metrics.
    all_metrics = "".join(
        "  %r: %r,\n" % (name, metric)
        for name, metric in sorted(self._best_eval_result.items()))
    file_utils.set_file_contents(
        data="{\n" + all_metrics + "}\n",
        path=os.path.join(export_path, "all_metrics.txt"))
    file_utils.set_file_contents(
        data="%d %r\n" % (self._best_eval_result["global_step"],
                          self._best_eval_result[self._metric_name]),
        path=os.path.join(export_path, "best_metric.txt"))
    file_utils.set_file_contents(
        data="%s\n" % exported_dir,
        path=os.path.join(export_path, "best_saved_model.txt"))

    return exported_dir

class LatestExporterWithSteps(hub.LatestModuleExporter):
  """Hub exporter that also writes a 'global_step.txt' file."""

  _step_re = re.compile(r"ckpt-(?P<steps>\d+)(?:\.(?:meta|index|data.*))?$")

  def export(self,
             estimator,
             export_path,
             checkpoint_path=None,
             eval_result=None,
             is_the_final_export=None):
    if checkpoint_path is None:
      checkpoint_path = estimator.latest_checkpoint()
    path = super(LatestExporterWithSteps, self).export(
        estimator,
        export_path,
        checkpoint_path=checkpoint_path,
        eval_result=eval_result,
        is_the_final_export=is_the_final_export)
    path = six.ensure_text(path)
    checkpoint_path = six.ensure_text(checkpoint_path)
    tf.logging.info("Path: %s, checkpoint path: %s", path, checkpoint_path)
    if path:
      match = self._step_re.search(checkpoint_path)
      if match:
        with tf.gfile.GFile(os.path.join(path, "global_step.txt"), "w") as f:
          f.write(six.ensure_binary(match.groupdict()["steps"]))
      else:
        tf.logging.warning("Couldn't find step counter in %r", checkpoint_path)

class BestModuleExporter(tf_estimator.Exporter):
  """Export the registered modules with the best metric value."""

  def __init__(self, name, serving_input_fn, compare_fn, exports_to_keep=5):
    """Creates a BestModuleExporter to use with tf.estimator.EvalSpec.

    Args:
      name: unique name of this Exporter, which will be used in the export path.
      serving_input_fn: A function with no arguments that returns a
        ServingInputReceiver. LatestModuleExporter does not care about the
        actual behavior of this function, so any return value that looks like a
        ServingInputReceiver is fine.
      compare_fn: A function that compares two evaluation results. It should
        take two arguments, best_eval_result and current_eval_result, and return
        True if the current result is better; False otherwise. See the
        loss_smaller method for an example.
      exports_to_keep: Number of exports to keep. Older exports will be garbage
        collected. Set to None to disable.
    """
    self._compare_fn = compare_fn
    self._best_eval_result = None
    self._latest_module_exporter = hub.LatestModuleExporter(
        name, serving_input_fn, exports_to_keep=exports_to_keep)

  @property
  def name(self):
    return self._latest_module_exporter.name

  def export(self,
             estimator,
             export_path,
             checkpoint_path,
             eval_result,
             is_the_final_export=None):
    """Actually performs the export of registered Modules."""
    if self._best_eval_result is None or self._compare_fn(
        best_eval_result=self._best_eval_result,
        current_eval_result=eval_result):
      tf.logging.info("Exporting the best modules.")
      self._best_eval_result = eval_result
      return self._latest_module_exporter.export(estimator, export_path)

def metric_compare_fn(key, compare):

  def _compare_fn(best_eval_result, current_eval_result):
    tf.logging.info("Old %s: %s | New %s: %s", key, key, best_eval_result[key],
                    current_eval_result[key])
    return compare(best_eval_result[key], current_eval_result[key])

  return _compare_fn


text:
import logging
import os
import sys
import markdown
import jinja2
import re
import yaml
import json
import codecs
import shutil
import time
import math
import scss

def set_jinja_filters(env):
    """ Used to add custom Jinja2 filters to the env """
    import pykwiki.jinja_filters as jinja_filters
    env.filters['idsafe'] = jinja_filters.idsafe
    return env

def render_theme_template(f, **kwargs):
    """ Renders a theme template using a cascading FilySystemLoader

    All theme templates include sopt, topt, ctrl, and conf
    """
    env = jinja2.Environment(loader=jinja2.FileSystemLoader(
        [conf.theme_path, conf.default_theme_path]))
    env = set_jinja_filters(env)
    tpl = env.get_template(f)
    return tpl.render(conf=conf, ctrl=ctrl,
        topt=ctrl.theme_options, sopt=ctrl.social_options,
        **kwargs)

def render_text(text, **kwargs):
    """ Jinja-fies plain text """
    env = set_jinja_filters(jinja2.Environment())
    tpl = env.from_string(text)
    return tpl.render(**kwargs)

def u_read(fname):
    """ Unicode read

    @param fname [str] - full path to the file to read
    """
    fh = codecs.open(fname, encoding='utf-8')
    txt = fh.read()
    fh.close()
    return txt

def u_write(fname, data):
    """ Unicode write

    @param fname [str] - full path to the file to write to
    @param data [str] - data to write to fname
    """
    fh = codecs.open(fname, encoding='utf-8', mode='w')
    fh.write(data)
    fh.close()

class Config(object):

    web_prefix = ''
    base_path = None
    source_dir = 'source'
    upload_dir = 'uploads'
    target_dir = 'docroot'
    link_file = 'links.yaml'
    style_file = 'style.scss'
    link_json_file = 'links.json'
    theme_dir = 'themes'
    style_dir = 'styles'
    theme = None
    style = 'default'
    logger = None
    site = {
        'title':'Example Site',
        'description':'Example Site Description',
        'author':'Example Author',
        'keywords':'example, pykwiki',
        'base_url':''
    }
    postlist = {
        'per_page':2,
        'post_type':'preview', #blurb, full, preview
        'max_pages':5,
        'order_field':'mtime',
        'order_type':'descending',
    }
    post_tpl = 'post.html'
    docroot_index = 'index.html'
    search_tpl = 'search.html'
    postlist_tpl = 'postlist.html'
    posts_tpl = 'posts.html'
    e404_tpl = '404.html'
    menu_tpl = 'menu.html'
    rss_tpl = 'rss.xml'
    rss_max_entries = 20
    source_ext = '.md'
    target_ext = '.html'
    template_ext = '.tpl'
    time_format = '%H:%M:%S'
    timestamp_format = '%Y-%m-%d %H:%M'
    date_format = '%Y-%m-%d'
    index_file = 'idx.json'
    post_data_file = 'posts.json'
    stop_words = ['the','and']
    upload_exts = ['.gif','.jpg','.jpeg','.png','.tiff','.pdf']
    markdown_exts = [
        'codehilite','toc',
        'pykwiki.ext.tpl',
        'pykwiki.ext.post',
    ]
    # The regex to grab post data blocks
    post_conf_re = re.compile('^\[\[(.*?)\]\]', re.DOTALL)
    post_conf_re2 = re.compile('^---(.*?)\\n---', re.DOTALL)
    post_toc_re = re.compile('^\s{0,3}\[TOC\]', re.MULTILINE)
    blurb_max = 50
    home_page = 'index'
    version = None

    # These properties will be written to/read from
    # the config.yaml file.
    config_text_properties = [
        'web_prefix',
        'home_page',
        'theme',
        'blurb_max',
        'site',
        'base_path',
        'markdown_exts',
        'time_format',
        'date_format',
        'timestamp_format',
        'source_ext',
        'postlist',
        'rss_max_entries',
        'style',
        'upload_exts',
        'version',
        'target_dir',
        'source_dir'
    ]

    def __init__(self):
        if not self.logger:
            self.logger = logging.getLogger("pykwiki")
        if not self.logger.handlers:
            self.logger.addHandler(logging.StreamHandler())

    def __repr__(self):
        return '<Config() base: %s; source: %s; target: %s;>'\
            %(self.base_path, self.source_dir, self.target_dir)

    def check(self):
        """ Verify that the current configuration isn't broken """
        if not self.base_path:
            raise Exception('conf.base_path not set!')

        if not os.path.exists(self.base_path):
            raise Exception('Base path %s does not exist!'%(self.base_path))

        if not self.source_dir:
            raise Exception('conf.source_dir not set!')

        if not self.target_dir:
            raise Exception('conf.target_dir not set!')

        if not self.logger or not isinstance(self.logger, logging.Logger):
            raise Exception('conf.logger is invalid!')

        if self.web_prefix.endswith('/'):
            raise Exception('conf.web_prefix cannot end with "/"')

        if not self.version:
            raise Exception('conf.version does not exist')

        if self.version < 2 or self.version >= 3:
            raise Exception('conf.version is incorrect')

    @property
    def default_theme_path(self):
        self_dir = os.path.split(os.path.abspath(__file__))[0]
        return os.path.join(self_dir, 'data', 'default_theme')

    @property
    def link_path(self):
        return os.path.join(self.base_path, self.link_file)

    @property
    def link_json_path(self):
        return os.path.join(self.target_path, self.link_json_file)

    @property
    def theme_path(self):
        if self.theme:
            return os.path.join(self.base_path, self.theme_dir, self.theme.lower())
        return self.default_theme_path

    @property
    def style_head(self):
        hd_path = os.path.join(self.style_path, 'head.html')
        if os.path.exists(hd_path):
            with open(hd_path, 'r') as fh:
                return fh.read()
        return "<!-- no style head.html found -->"

    @property
    def style_post(self):
        path = os.path.join(self.style_path, 'post.html')
        if os.path.exists(path):
            with open(path, 'r') as fh:
                return fh.read()
        return "<!-- no style post.html found -->"

    @property
    def style_footer(self):
        ft_path = os.path.join(self.style_path, 'footer.html')
        if os.path.exists(ft_path):
            with open(ft_path, 'r') as fh:
                return fh.read()
        return "<!-- no style footer.html found -->"

    @property
    def style_path(self):
        return os.path.join(self.base_path, self.style_dir, self.style.lower())

    @property
    def style_web_path(self):
        return self.web_prefix + "/" + os.path.join('static', self.style.lower())

    @property
    def style_file_path(self):
        return os.path.join(self.style_path, self.style_file)

    @property
    def source_path(self):
        return os.path.join(self.base_path, self.source_dir)

    @property
    def target_path(self):
        return os.path.join(self.base_path, self.target_dir)

    def save(self, cname='config.yaml'):
        """ Write the config to base_path.yaml

        @param cname [str] - The name of the config file
        """
        self.logger.info('Saving configuration to: %s'%(cname))
        self.check()
        export = {}
        for k in self.config_text_properties:
            export[k] = getattr(self, k)
        fh = open(os.path.join(self.base_path, cname), 'w')
        yml = yaml.dump(export, default_flow_style=False)
        fh.write(yml)
        fh.close()

    def load(self, cpath):
        """ Load the config file into the object

        @param cpath [str] - The full path to the config to read
        """
        self.logger.debug('Reading configuration from %s...'%(cpath))
        if not os.path.exists(cpath):
            raise Exception('Config file: %s not found!'%(cpath))

        fh = open(cpath, 'r')
        txt = fh.read()
        fh.close()
        data = yaml.load(txt)
        for k in data:
            if k in self.config_text_properties:
                setattr(self, k, data[k])
        #self.check()

class PostController(object):

    _posts = []
    _source_files = []
    _theme_menu = None
    _theme_options = None
    _social_options = None
    _post_info = None
    index_data = {}

    def __init__(self):
        pass

    def cache_posts(self, plist=None, force=False):
        """ Cache posts and return the number of changes

        @param plist [list] - A list of source filenames to cache
        @param force [bool] - Cache regardless of file mtimes
        @returns [int] - Number of files changed
        """

        cached = 0;
        if not plist:
            plist = self.source_files

        for pf in plist:
            pg = Post(pf)
            if not force:
                if pg.source_mtime <= pg.target_mtime:
                    continue
            cached += 1
            pg.save()

        return cached

    def cache_uploads(self):
        """ Find file differences (mtime) and copy them to docroot

        @returns [bool] - True if successful, false otherwise
        """

        conf.logger.info('Caching uploads')
        cnt = 0
        for root, dirs, files in os.walk(conf.source_path):
            for f in files:
                fnoe, ext = os.path.splitext(f)
                if ext not in conf.upload_exts:
                    continue
                src_path = os.path.join(root, f)
                tgt_path = src_path.replace(conf.source_path, conf.target_path)
                if os.path.exists(tgt_path):
                    src_mt = os.path.getmtime(src_path)
                    tgt_mt = os.path.getmtime(tgt_path)
                    if src_mt < tgt_mt:
                        continue
                tgt_dir, tgt_fname = os.path.split(tgt_path)
                if not os.path.exists(tgt_dir):
                    os.makedirs(tgt_dir)
                conf.logger.debug('Copying %s to %s'%(src_path, tgt_path))
                shutil.copy(src_path, tgt_path)
                cnt += 1

        conf.logger.info('Copied %s new uploads'%(cnt))
        return True

    def cache_postlist(self):

        order_field = conf.postlist['order_field']
        order_type = conf.postlist['order_type']
        max_pages = conf.postlist['max_pages']
        per_page = conf.postlist['per_page']
        post_type = conf.postlist['post_type']

        posts = self.get_posts(sort_key=order_field, private=False,
            direction=order_type, filters=None, limit=(max_pages*per_page))

        post_count = len(posts)
        if post_count > max_pages * per_page:
            post_count = max_pages * per_page
        page_count = int(math.ceil(float(post_count) / float(per_page)))

        for page_num in range(1, max_pages+1):
            if len(posts) < 1:
                break
            this_posts = []
            for i in range(per_page):
                if len(posts) < 1:
                    break
                this_posts.append(posts.pop(0))

            html = render_theme_template(conf.postlist_tpl,
                this_page_num=page_num, posts=this_posts,
                page_count=page_count, post_count=post_count,
                post_type=post_type, per_page=per_page)
            out = os.path.join(conf.target_path, 'postlist-%s.html'%(page_num))
            u_write(out, html)

        html = render_theme_template(conf.posts_tpl,
            post_count=post_count, page_count=page_count,
            post_type=post_type, per_page=per_page)
        out = os.path.join(conf.target_path, conf.posts_tpl)
        u_write(out, html)

    def cache_theme(self):
        """ Cache specific theme files """

        conf.logger.info("Caching theme files")
        conf.logger.debug('Using theme files from %s'%(conf.theme_path))

        # Post 404
        html = render_theme_template(conf.e404_tpl)
        out = os.path.join(conf.target_path, conf.e404_tpl)
        u_write(out, html)

        # Post Search
        html = render_theme_template(conf.search_tpl)
        out = os.path.join(conf.target_path, conf.search_tpl)
        u_write(out, html)

        # Post Listing
        self.cache_postlist()

        # Home post
        home_page = conf.home_page
        if home_page in [conf.posts_tpl, conf.search_tpl]:
            html = render_theme_template(home_page)
            out = os.path.join(conf.target_path, conf.docroot_index)
            u_write(out, html)
        else:
            if not home_page.endswith(conf.source_ext):
                home_page = home_page + conf.source_ext
            home_page_path = os.path.join(conf.source_path, home_page)
            if os.path.exists(home_page_path):
                conf.logger.info('Writing %s as home post (%s)'\
                    %(home_page, conf.docroot_index))
                pg = Post(home_page)
                html = render_theme_template(conf.post_tpl, post=pg)
                out = os.path.join(conf.target_path, conf.docroot_index)
                u_write(out, html)

        # Static files
        stat_src = os.path.join(conf.theme_path, 'static/')
        stat_dest = os.path.join(conf.target_path, 'static/')
        style_src = os.path.join(conf.style_path, 'static')
        style_dest = os.path.join(conf.target_path, 'static', 'style')
        if os.path.exists(stat_src):
            if os.path.exists(stat_dest):
                shutil.rmtree(stat_dest)
            shutil.copytree(stat_src, stat_dest)
            if os.path.exists(style_src):
                shutil.copytree(style_src, style_dest)

        # In case source doesn't have a static/ directory
        if not os.path.exists(style_dest):
            os.makedirs(style_dest)

        # Cache scss
        conf.logger.debug("Compiling scss to static/styles/style.css")
        style_data = scss.Compiler().compile(conf.style_file_path)
        with open(os.path.join(style_dest, 'style.css'), 'w') as fh:
            fh.write(style_data)

    def cache_rss_feed(self):

        """ Builds an RSS feed for the posts """

        conf.logger.info('Caching RSS feed')
        rss_tpl = os.path.join(conf.theme_path, conf.rss_tpl)
        if not os.path.exists(rss_tpl):
            conf.logger.warning('No rss template found in theme')
            return False

        posts = self.get_posts(limit=conf.rss_max_entries)
        xml = render_theme_template(conf.rss_tpl, posts=posts)
        outf = os.path.join(conf.target_path, conf.rss_tpl)
        u_write(outf, xml)

    @property
    def social_options(self):
        if self._social_options:
            return self._social_options
        opt_path = os.path.join(conf.base_path, 'social.yaml')
        if os.path.exists(opt_path):
            data = u_read(opt_path)
            self._social_options = yaml.load(data)
            return self._social_options
        return {}

    @property
    def theme_options(self):
        if self._theme_options:
            return self._theme_options
        opt_path = os.path.join(conf.theme_path, 'options.yaml')
        if os.path.exists(opt_path):
            data = u_read(opt_path)
            self._theme_options = yaml.load(data)
            return self._theme_options

        return {}

    @property
    def theme_menu(self):
        if self._theme_menu:
            return self._theme_menu

        if os.path.exists(conf.link_json_path):
            if os.path.getmtime(conf.link_path) \
                    <= os.path.getmtime(conf.link_json_path):
                js = u_read(conf.link_json_path)
                links = json.loads(js)
                self._theme_menu = render_theme_template(conf.menu_tpl,
                    links=links)
                return self._theme_menu

        conf.logger.info('Generating theme menu from %s'%(conf.link_file))
        link_path = conf.link_path
        if not os.path.exists(link_path):
            return 'Unable to locate %s'%(link_path)

        def get_clean_links(dlist):
            ll = []
            for d in dlist:
                if not isinstance(d, dict):
                    ll.append({'label':d})
                    continue
                key = list(d.keys())[0]
                new_d = d[key]
                new_d['label'] = key
                new_d['rel'] = 'internal'
                if new_d.get('post'):
                    if not new_d['post'].endswith(conf.source_ext):
                        new_d['post'] = new_d['post'] + conf.source_ext
                    new_d['href'] = conf.web_prefix + '/' +\
                        new_d['post'].replace(conf.source_ext, conf.target_ext)
                else:
                    if new_d.get('href') and '://' in new_d['href']:
                        new_d['rel'] = 'external'

                if new_d.get('children'):
                    new_d['children'] = get_clean_links(new_d['children'])
                ll.append(new_d)
            return ll

        ltxt = u_read(link_path)
        links = yaml.load(ltxt)
        menu_links = get_clean_links(links)
        self._theme_menu = render_theme_template(conf.menu_tpl, links=menu_links)
        u_write(conf.link_json_path, json.dumps(menu_links))
        return self._theme_menu

    @property
    def post_info(self, plist=None):

        if self._post_info:
            return self._post_info

        if not plist:
            plist = self.source_files

    def index_posts(self, plist=None, search_index=True):
        """ Build a search index and post data JSON file

        @param plist [list] - A list of source file names
        @search_index [bool] - Whether or not to build a search index
        """
        if not plist:
            plist = self.source_files

        if self.index_data:
            return

        conf.logger.info("Building search index")
        conf.logger.debug('Indexing posts: %s'%(plist))

        data = {}
        tags = {}
        ids = {}
        info = {}
        this_id = 1
        for pf in plist:
            pg = Post(pf)
            if pg.conf.get('private'):
                continue
            ids[this_id] = pg.target_fname
            info[pg.target_fname] = dict(
                title=pg.title,
                blurb=pg.blurb,
                url=pg.url,
                mtime=int(pg.mtime),
                mtimestamp=pg.mtimestamp,
                mtime_string=pg.mtime_string,
                mdate_string=pg.mdate_string,
                author=pg.author,
            )

            # Tags
            for tag in pg.conf.get('tags',[]):
                if tag in tags:
                    if this_id in tags[tag]:
                        continue
                    tags[tag].append(this_id)
                else:
                    tags[tag] = [this_id]

            # Search Index
            if search_index:
                s_text = '%s %s'%((pg.conf.get('title','')+' ')*10, pg.source_text)
                s_text = s_text.replace("'",'').lower()
                s_text = re.sub('[^a-z0-9\-\ ]', ' ', s_text)
                for word in s_text.split():
                    if len(word) < 3:
                        continue
                    if word in conf.stop_words:
                        continue
                    if word in data:
                        if this_id in data[word]:
                            data[word][this_id] += 1
                        else:
                            data[word][this_id] = 1
                    else:
                        data[word] = {this_id: 1}

            this_id += 1

        for k in data:
            data[k] = sorted(data[k], key=data[k].get, reverse=True)

        self.index_data = {
            'ids':ids,
            'index':data,
            'tags':tags,
            'updated':int(time.time()),
        }

        post_info = {
            'info':info,
            'ids':ids,
        }

        post_info_path = os.path.join(conf.target_path, conf.post_data_file)
        conf.logger.debug('Writing post info to %s'%(post_info_path))
        u_write(post_info_path, json.dumps(post_info))

        index_path = os.path.join(conf.target_path, conf.index_file)
        conf.logger.debug('Writing search index to %s'%(index_path))
        u_write(index_path, json.dumps(self.index_data))

    def get_posts(self, sort_key='mtime', private=False, direction='desc',
            filters=None, limit=0):
        """ Get a sorted and filtered list of posts

        @param sort_key [str] - The post property to sort on, default: mtime
        @param private [bool] - Include private posts if true
        @param direction [str] - Either ascending or descending
        @param filters [list] - A multi-dimensional list of filters like
            [ ['field', 'op', 'value'], ['mtime', '>', '1234567890']]
        @param limit [int] - Maximum number of posts to return
        """
        direction = direction.lower()
        rev = False
        if direction == 'desc' or direction == 'descending':
            rev = True

        # Set the limit to something crazy, like 10K
        if not limit:
            limit = 10000

        plist = self.posts
        if not private:
            plist = [p for p in plist if not p.conf.get('private')]

        if filters:
            for f in filters:
                try:
                    k, o, v = f
                except:
                    raise Exception('Invalid filter found: %s'%(f))

                if o == '==':
                    plist = [p for p in plist if getattr(p, k) == v]
                elif o == '>':
                    plist = [p for p in plist if getattr(p, k) > v]
                elif o == '>=':
                    plist = [p for p in plist if getattr(p, k) >= v]
                elif o == '<':
                    plist = [p for p in plist if getattr(p, k) < v]
                elif o == '<=':
                    plist = [p for p in plist if getattr(p, k) <= v]
                elif o == '!=':
                    plist = [p for p in plist if getattr(p, k) != v]
                else:
                    raise Exception('Invalid filter operator found: %s'%(o))

        if sort_key:
            plist.sort(key=lambda x: getattr(x, sort_key), reverse=rev)

        if len(plist) > limit:
            plist = plist[0:limit]

        return plist

    def get_post_dates(self, private=False, direction='desc', limit=0):
        """ Return a list of post dates in mdate_string format

        @param private [bool] - Include private posts if True
        @param direction [str] - Either ascending or descending
        @param limit [int] - Maximum number of post dates to return
        """
        direction = direction.lower()
        rev = False
        if direction == 'desc' or direction == 'descending':
            rev = True

        if not limit:
            limit = 10000

        plist = self.posts

        if not private:
            plist = [p for p in plist if not p.conf.get('private')]

        plist.sort(key=lambda x: x.mtime, reverse=rev)
        dlist = []
        for p in plist:
            if p.mdate_string not in dlist and len(dlist) < limit:
                dlist.append(p.mdate_string)
        return dlist

    @property
    def post_dates(self):
        return self.get_post_dates(direction='desc')

    @property
    def posts(self):
        if self._posts:
            return self._posts
        plist = self.source_files
        posts = []
        for pf in plist:
            posts.append(Post(pf))
        self._posts = posts
        return self._posts

    @property
    def tags(self):
        tags = []
        for p in self.posts:
            for t in p.tags:
                tags.append(t)
        return sorted(list(set(tags)))

    @property
    def source_files(self):

        if self._source_files:
            return self._source_files

        for root, dirs, files in os.walk(conf.source_path):
            for f in files:
                if not f.endswith(conf.source_ext):
                    continue
                fullpath = os.path.join(root, f)
                fname = fullpath.replace(conf.source_path+'/', '')
                self._source_files.append(fname)

        return self._source_files

class Post(object):
    """ Post object loads, saves, and caches posts """

    source_fname = None
    toc = None
    _source_text = None
    _target_text = None
    _conf = {}
    _author = None
    _tags = []
    _blurb = None
    _description = None
    _keywords = None
    _title = None
    _mtime = None
    _has_toc = None
    _hide_author = None

    def __init__(self, source_fname):
        conf.check()
        self.source_fname = source_fname

    def save(self):

        full_dir = os.path.split(self.target_path)[0]

        if not os.path.exists(full_dir):
            try:
                conf.logger.info('Making directory: %s'%(full_dir))
                os.makedirs(full_dir)
            except Exception as e:
                raise Exception('Cannot create directory: %s'%(full_dir))

        pc = self.conf
        tt = self.target_text
        conf.logger.debug('Saving to: %s'%(self.target_path))
        html = render_theme_template(conf.post_tpl, post=self)
        u_write(self.target_path, html)

    def get_section(self, name, raw=True):
        """ Load a post section """

        pat = re.compile(r'\{section:%s\}(.*?)\{endsection\}'%(name), re.DOTALL|re.M)
        m = pat.search(self.source_text)
        if not m:
            return None
        # By default, return raw text
        if raw:
            return m.group(1)
        return markdown.markdown(m.group(1),
            extensions=conf.markdown_exts)

    @property
    def author(self):
        if self._author:
            return self._author
        if self.conf.get('author'):
            self._author = self.conf['author']
            return self._author
        self._author = conf.site.get('author', 'Nobody')
        return self._author

    @property
    def hide_author(self):
        if self._hide_author != None:
            return self._hide_author
        self._hide_author = self.conf.get('hide_author', False)
        return self._hide_author

    @property
    def conf(self):

        if self._conf:
            return self._conf

        m = (
            conf.post_conf_re.search(self.source_text) or
            conf.post_conf_re2.search(self.source_text)
        )
        if not m:
            conf.logger.warning('No post config specified for %s'%(self.source_fname))
            self._conf = {'foo':True}
            return {}

        try:
            self._conf = yaml.load(m.group(1))
            self._source_text = self.source_text.replace(m.group(0), '')
        except Exception as e:
            conf.logger.exception('Invalid post configuration found!')

        return self._conf

    @property
    def source_path(self):
        return os.path.join(conf.source_path, self.source_fname)

    @property
    def target_path(self):
        return os.path.join(conf.target_path, self.target_fname)

    @property
    def target_fname(self):
        return self.source_fname.replace(
            conf.source_ext, conf.target_ext)

    @property
    def url(self):
        url = []
        if conf.site.get('base_url'):
            url.append(conf.site['base_url'])
        if conf.web_prefix:
            url.append(conf.web_prefix)
        url.append(self.target_fname)
        return '/'.join(url)

    @property
    def source_text(self):
        if self._source_text:
            return self._source_text

        if not os.path.exists(self.source_path):
            raise Exception('Cannot locate file: %s'%(self.source_path))

        self._source_text = u_read(self.source_path)

        # Check for table of contents, and extract for theme
        tocm = conf.post_toc_re.search(self._source_text)
        if tocm:
            self._has_toc = True
            self._source_text = conf.post_toc_re.sub('', self._source_text)
            md = markdown.Markdown(extensions=conf.markdown_exts)
            md.convert(self.source_text)
            self.toc = md.toc

        return self._source_text

    @property
    def tags(self):
        if self._tags:
            return self._tags
        self._tags = self.conf.get('tags',[])
        return self._tags

    @property
    def title(self):
        if self._title:
            return self._title
        if self.conf.get('title'):
            self._title = self.conf['title']
            return self._title
        self._title = self.target_fname.replace(conf.target_ext, '')
        return self._title

    @property
    def target_text(self):
        if self._target_text:
            return self._target_text
        conf.logger.debug('Caching: %s'%(self.source_fname))
        md = markdown.Markdown(extensions=conf.markdown_exts)
        html = md.convert(self.source_text)
        #self._toc = md.toc
        #conf.logger.info("Got TOC:%s"%(self._toc))
        self._target_text = html
        return self._target_text

    @property
    def mtime(self):
        if self._mtime:
            return self._mtime

        if self.conf.get('timestamp'):
            self._mtime = time.mktime(time.strptime(self.conf['timestamp'], conf.timestamp_format))
            return self._mtime

        self._mtime = self.source_mtime
        return self._mtime

    @property
    def mtime_tuple(self):
        return time.localtime(self.mtime)

    @property
    def mtimestamp(self):
        return time.strftime(conf.timestamp_format, self.mtime_tuple)

    @property
    def mtimestamp_rfc822(self):

        """ Gets RSS/Email Compatible, RFC 822 based, date """

        return time.strftime('%a, %d %b %Y %H:%M:%S %Z', self.mtime_tuple)

    @property
    def mtime_string(self):
        return time.strftime(conf.time_format, self.mtime_tuple)

    @property
    def mdate_string(self):
        return time.strftime(conf.date_format, self.mtime_tuple)

    @property
    def mtime_hour(self):
        return time.strftime('%H', self.mtime_tuple)

    @property
    def mtime_minute(self):
        return time.strftime('%M', self.mtime_tuple)

    @property
    def mdate_day(self):
        return time.strftime('%d', self.mtime_tuple)

    @property
    def mdate_month(self):
        return time.strftime('%m', self.mtime_tuple)

    @property
    def mdate_year(self):
        return time.strftime('%Y', self.mtime_tuple)

    @property
    def target_mtime(self):
        if os.path.exists(self.target_path):
            return os.path.getmtime(self.target_path)
        return 0

    @property
    def source_mtime(self):
        return os.path.getmtime(self.source_path)

    @property
    def nowrap(self):
        return self.conf.get('nowrap');

    @property
    def blurb(self):
        if self._blurb:
            return self._blurb
        elif self.conf.get('blurb'):
            if self.conf['blurb'].lower() != 'auto':
                self._blurb = self.conf['blurb']
                return self._blurb
        elif self.conf.get('description'):
            self._blurb = self.conf['description']
            return self._blurb

        words = self.source_text.split()
        blurb = ' '.join(words[0:conf.blurb_max]) + '...'
        # @todo clean to blurb to remove HTML and MarkDown stuff
        blurb = re.sub(r'[<#>]','', blurb)

        self._blurb = blurb
        return self._blurb

    @property
    def keywords(self):
        if self._keywords:
            return self._keywords
        if self.conf.get('keywords'):
            self._keywords = self.conf['keywords']
            return self._keywords
        if self.conf.get('tags'):
            self._keywords = ','.join(self.conf['tags'])
            return self._keywords
        if conf.site.get('keywords'):
            self._keywords = conf.site['keywords']
            return self._keywords
        return ''

    @property
    def description(self):
        if self._description:
            return self._description
        if self.conf.get('description'):
            self._description = self.conf['description']
            return self._description
        if conf.site.get('description'):
            self._description = conf.site['description']
            return self._description
        return ''

conf = Config()
ctrl = PostController()


text:
package pl.asie.charset.lib.audio.codec;

public interface ICodec {
    void compress(byte[] dest, byte[] src, int destoffs, int srcoffs, int len);
    void decompress(byte[] dest, byte[] src, int destoffs, int srcoffs, int len);
}


text:
using System;
using System.Collections.Generic;
using System.Linq;

namespace Avalonia.Controls.Generators
{
    /// <summary>
    /// Maintains an index of all item containers currently materialized by a <see cref="TreeView"/>.
    /// </summary>
    /// <remarks>
    /// Each <see cref="TreeViewItem"/> has its own <see cref="TreeItemContainerGenerator{T}"/>
    /// that maintains the list of its direct children, but they also share an instance of this
    /// class in their <see cref="TreeItemContainerGenerator{T}.Index"/> property which tracks
    /// the containers materialized for the entire tree.
    /// </remarks>
    public class TreeContainerIndex
    {
        private readonly Dictionary<object, HashSet<IControl>> _itemToContainerSet = new Dictionary<object, HashSet<IControl>>();
        private readonly Dictionary<object, IControl> _itemToContainer = new Dictionary<object, IControl>();
        private readonly Dictionary<IControl, object> _containerToItem = new Dictionary<IControl, object>();

        /// <summary>
        /// Signaled whenever new containers are materialized.
        /// </summary>
        public event EventHandler<ItemContainerEventArgs>? Materialized;

        /// <summary>
        /// Event raised whenever containers are dematerialized.
        /// </summary>
        public event EventHandler<ItemContainerEventArgs>? Dematerialized;

        /// <summary>
        /// Gets the currently materialized containers.
        /// </summary>
        public IEnumerable<IControl> Containers => _containerToItem.Keys;

        /// <summary>
        /// Gets the items of currently materialized containers.
        /// </summary>
        public IEnumerable<object> Items => _containerToItem.Values;

        /// <summary>
        /// Adds an entry to the index.
        /// </summary>
        /// <param name="item">The item.</param>
        /// <param name="container">The item container.</param>
        public void Add(object item, IControl container)
        {
            _itemToContainer[item] = container;
            if (_itemToContainerSet.TryGetValue(item, out var set))
            {
                set.Add(container);
            }
            else
            {
                _itemToContainerSet.Add(item, new HashSet<IControl> { container });
            }

            _containerToItem.Add(container, item);

            Materialized?.Invoke(
                this,
                new ItemContainerEventArgs(new ItemContainerInfo(container, item, 0)));
        }

        /// <summary>
        /// Removes a container from private collections.
        /// </summary>
        /// <param name="container">The item container.</param>
        /// <param name="item">The DataContext object</param>
        private void RemoveContainer(IControl container, object item)
        {
            if (_itemToContainerSet.TryGetValue(item, out var set))
            {
                set.Remove(container);
                if (set.Count == 0)
                {
                    _itemToContainerSet.Remove(item);
                    _itemToContainer.Remove(item);
                }
                else
                {
                    _itemToContainer[item] = set.First();
                }
            }
        }

        /// <summary>
        /// Removes a container from the index.
        /// </summary>
        /// <param name="container">The item container.</param>
        public void Remove(IControl container)
        {
            var item = _containerToItem[container];
            _containerToItem.Remove(container);
            RemoveContainer(container, item);

            Dematerialized?.Invoke(
                this,
                new ItemContainerEventArgs(new ItemContainerInfo(container, item, 0)));
        }

        /// <summary>
        /// Removes a set of containers from the index.
        /// </summary>
        /// <param name="startingIndex">The index of the first item.</param>
        /// <param name="containers">The item containers.</param>
        public void Remove(int startingIndex, IEnumerable<ItemContainerInfo> containers)
        {
            foreach (var container in containers)
            {
                var item = _containerToItem[container.ContainerControl];
                _containerToItem.Remove(container.ContainerControl);
                RemoveContainer(container.ContainerControl, item);
            }

            Dematerialized?.Invoke(
                this,
                new ItemContainerEventArgs(startingIndex, containers.ToList()));
        }

        /// <summary>
        /// Gets the container for an item.
        /// </summary>
        /// <param name="item">The item.</param>
        /// <returns>The container, or null of not found.</returns>
        public IControl? ContainerFromItem(object item)
        {
            if (item != null)
            {
                _itemToContainer.TryGetValue(item, out var result);
                if (result == null)
                {
                    _itemToContainerSet.TryGetValue(item, out var set);
                    if (set?.Count > 0)
                    {
                        return set.FirstOrDefault();
                    }
                }
                return result;
            }

            return null;
        }

        /// <summary>
        /// Gets the item for a container.
        /// </summary>
        /// <param name="container">The container.</param>
        /// <returns>The item, or null of not found.</returns>
        public object? ItemFromContainer(IControl? container)
        {
            if (container != null)
            {
                _containerToItem.TryGetValue(container, out var result);
                if (result != null)
                {
                    _itemToContainer[result] = container;
                }
                return result;
            }

            return null;
        }
    }
}


text:
PLATFORM := $(shell uname -s)
BASH_EXISTS := $(shell which bash)
SHELL := $(shell which bash)

ifneq ($(MAKECMDGOALS),deps)
GIT_EXISTS := $(shell which git)
endif

MAKE = make
ifeq ($(PLATFORM),FreeBSD)
	MAKE = gmake
endif

DISTRO := $(shell . ./tools/lib.sh; _platform)
DISTRO_VERSION := $(shell . ./tools/lib.sh; _distro $(DISTRO))
ifeq ($(DISTRO),darwin)
	ifeq ($(DISTRO_VERSION), 10.11)
		BUILD_DIR = darwin
	else
		BUILD_DIR = darwin$(DISTRO_VERSION)
	endif
else ifeq ($(DISTRO),freebsd)
	BUILD_DIR = freebsd$(DISTRO_VERSION)
else
	BUILD_DIR = $(DISTRO_VERSION)
endif

DEFINES := CTEST_OUTPUT_ON_FAILURE=1
.PHONY: docs build

all: .setup
	cd build/$(BUILD_DIR) && cmake ../../ && \
		$(DEFINES) $(MAKE) --no-print-directory $(MAKEFLAGS)

docs: .setup
	cd build && cmake ../ && \
		$(DEFINES) $(MAKE) docs --no-print-directory $(MAKEFLAGS)

debug: .setup
	cd build/debug_$(BUILD_DIR) && DEBUG=True cmake ../../ && \
		$(DEFINES) $(MAKE) --no-print-directory $(MAKEFLAGS)

test_debug: .setup
	cd build/debug_$(BUILD_DIR) && DEBUG=True cmake ../../ && \
		$(DEFINES) $(MAKE) test --no-print-directory $(MAKEFLAGS)

analyze: .setup
	cd build/$(BUILD_DIR) && ANALYZE=True cmake ../../ && \
		$(DEFINES) $(MAKE) --no-print-directory $(MAKEFLAGS)

sanitize: .setup
	cd build/$(BUILD_DIR) && SANITIZE=True cmake ../../ && \
		$(DEFINES) $(MAKE) --no-print-directory $(MAKEFLAGS)

sdk: .setup
	cd build/$(BUILD_DIR) && SDK=True cmake ../../ && \
		$(DEFINES) $(MAKE) --no-print-directory $(MAKEFLAGS)

test_sdk: .setup
	cd build/$(BUILD_DIR) && SDK=True cmake ../../ && \
		$(DEFINES) $(MAKE) test --no-print-directory $(MAKEFLAGS)

debug_sdk: .setup
	cd build/debug_$(BUILD_DIR) && SDK=True DEBUG=True cmake ../../ && \
		$(DEFINES) $(MAKE) --no-print-directory $(MAKEFLAGS)

test_debug_sdk: .setup
	cd build/debug_$(BUILD_DIR) && SDK=True DEBUG=True cmake ../../ && \
		$(DEFINES) $(MAKE) test --no-print-directory $(MAKEFLAGS)

build:
	cd build/$(BUILD_DIR) && \
		$(DEFINES) $(MAKE) --no-print-directory $(MAKEFLAGS)

debug_build:
	cd build/debug_$(BUILD_DIR) && \
		$(DEFINES) $(MAKE) --no-print-directory $(MAKEFLAGS)

test_build:
	cd build/$(BUILD_DIR) && \
		$(DEFINES) $(MAKE) test --no-print-directory $(MAKEFLAGS)

test_debug_build:
	cd build/debug_$(BUILD_DIR) && \
		$(DEFINES) $(MAKE) test --no-print-directory $(MAKEFLAGS)

deps: .setup
	./tools/provision.sh build build/$(BUILD_DIR)

clean: .setup
	cd build/$(BUILD_DIR) && cmake ../../ && \
		$(DEFINES) $(MAKE) clean --no-print-directory $(MAKEFLAGS)

strip: .setup
	cd build/$(BUILD_DIR) && find ./osquery -executable -type f | xargs strip

distclean:
	rm -rf .sources build/$(BUILD_DIR) build/debug_$(BUILD_DIR) build/docs
ifeq ($(PLATFORM),Linux)
		rm -rf build/linux
endif

.setup:
ifneq ($(MAKECMDGOALS),deps)
ifeq ($(GIT_EXISTS),)
	@echo "Problem: cannot find 'git'"
	@false
endif
endif
ifeq ($(BASH_EXISTS),)
	@echo "Problem: cannot find 'bash'"
	@false
endif

ifeq ($(DISTRO),unknown_version)
	@echo Unknown, non-Redhat, non-Ubuntu based Linux distro
	@false
endif
	@mkdir -p build/docs
	@mkdir -p build/$(BUILD_DIR)
	@mkdir -p build/debug_$(BUILD_DIR)
ifeq ($(PLATFORM),Linux)
	@ln -snf $(BUILD_DIR) build/linux
	@ln -snf debug_$(BUILD_DIR) build/debug_linux
endif

package: .setup
	# Alias for packages (do not use CPack)
	cd build/$(BUILD_DIR) && PACKAGE=True cmake ../../ && \
		$(DEFINES) $(MAKE) packages --no-print-directory $(MAKEFLAGS)

debug_package: .setup
	cd build/debug_$(BUILD_DIR) && DEBUG=True PACKAGE=True cmake ../../ && \
		$(DEFINES) $(MAKE) packages --no-print-directory $(MAKEFLAGS)

packages: .setup
	cd build/$(BUILD_DIR) && PACKAGE=True cmake ../../ && \
		$(DEFINES) $(MAKE) packages --no-print-directory $(MAKEFLAGS)

debug_packages:
	cd build/debug_$(BUILD_DIR) && DEBUG=True PACKAGE=True cmake ../../ && \
		$(DEFINES) $(MAKE) packages --no-print-directory $(MAKEFLAGS)

sync: .setup
	cd build/$(BUILD_DIR) && PACKAGE=True cmake ../../ && \
		$(DEFINES) $(MAKE) sync --no-print-directory $(MAKEFLAGS)

%::
	cd build/$(BUILD_DIR) && cmake ../../ && \
		$(DEFINES) $(MAKE) --no-print-directory $@


text:
<!DOCTYPE html>

  <meta charset="UTF-8">

  <title>CSS Background and Borders Test: background-clip - content-box with background-color</title>

  <link rel="author" title="Gérard Talbot" href="http://www.gtalbot.org/BrowserBugsSection/css21testsuite/">
  <link rel="help" href="http://www.w3.org/TR/css3-background/#the-background-clip" title="3.7. Painting Area: the 'background-clip' property">
  <link rel="match" href="reference/ref-if-there-is-no-red.xht">

  <meta content="When 'background-clip' is set to 'content-box', then the background painting area is clipped at the edges of the content of the element. In this test, height is 'auto', therefore its used value is '0px'; width is 'auto', therefore its used value is as wide as the body element. So, the content box is 0px tall and as wide as the body element; therefore, red should not be visible." name="assert">

Test passes if there is no red.


text:
#ifndef CHECKHEADER_SLIB_UI_ADAPTER
#define CHECKHEADER_SLIB_UI_ADAPTER

#include "definition.h"

#include "view.h"

namespace slib
{

	class SLIB_EXPORT ViewAdapter : public Object
	{
		SLIB_DECLARE_OBJECT

	public:
		ViewAdapter();

		~ViewAdapter();

	public:
		virtual sl_uint64 getItemsCount() = 0;

		virtual Ref<View> getView(sl_uint64 index, View* original, View* parent) = 0;

		virtual sl_object_type getViewType(sl_uint64 index, View* parent);

		virtual sl_ui_len getAverageItemHeight(View* parent);

		virtual sl_ui_len getItemHeight(sl_uint64 index, View* parent);

		virtual sl_uint32 getMaximumItemsCountPerPage(View* parent);

		void populateInto(View* parent, UIUpdateMode mode = UIUpdateMode::UpdateLayout);

		void populateInto(const Ref<View>& parent, UIUpdateMode mode = UIUpdateMode::UpdateLayout);

	};

	template <class ViewType>
	class SLIB_EXPORT ViewAdapterT : public ViewAdapter
	{
	public:
		virtual void onBindView(sl_uint64 index, ViewType* view, View* parent) = 0;

	public:
		Ref<View> getView(sl_uint64 index, View* original, View* parent) override
		{
			Ref<ViewType> view;
			if (original) {
				view = (ViewType*)original;
			} else {
				view = new ViewType;
			}
			onBindView(index, view.get(), parent);
			return view;
		}

	};

	template <class DataType, class ViewType>
	class SLIB_EXPORT ListViewAdapter : public ViewAdapterT<ViewType>
	{
	public:
		ListViewAdapter() {}

		ListViewAdapter(const List<DataType>& list): m_list(list) {}

		ListViewAdapter(const List<DataType>& list, const Function<void(DataType& data, ViewType* view, View* parent)>& onBindView): m_list(list), m_onBindView(onBindView) {}

	public:
		static Ref<ListViewAdapter> create(const List<DataType>& list, const Function<void(DataType& data, ViewType* view, View* parent)>& onBindView)
		{
			return new ListViewAdapter(list, onBindView);
		}

		List<DataType> getList()
		{
			return m_list;
		}

		void setList(const List<DataType>& list)
		{
			m_list = list;
		}

		Function<void(DataType& data, ViewType* view, View* parent)> getOnBindView()
		{
			return m_onBindView;
		}

		void setOnBindView(const Function<void(DataType& data, ViewType* view, View* parent)>& onBindView)
		{
			m_onBindView = onBindView;
		}

		sl_uint64 getItemsCount() override
		{
			return m_list.getCount();
		}

	public:
		virtual void onBindView(DataType& data, ViewType* view, View* parent)
		{
			m_onBindView(data, view, parent);
		}

		void onBindView(sl_uint64 index, ViewType* view, View* parent) override
		{
			ListLocker<DataType> list(m_list);
			if (index < list.count) {
				onBindView(list[(sl_size)index], view, parent);
			}
		}

	protected:
		AtomicList<DataType> m_list;
		AtomicFunction<void(DataType& data, ViewType* view, View* parent)> m_onBindView;

	};

	class ViewListAdapter : public ViewAdapter
	{
		SLIB_DECLARE_OBJECT

	public:
		ViewListAdapter();

		~ViewListAdapter();

		ViewListAdapter(const List< Ref<View> >& list);

	public:
		static Ref<ViewListAdapter> create(const List< Ref<View> >& list);

		List< Ref<View> > getList();

		void setList(const List< Ref<View> >& list);

		void addView(const Ref<View>& view);

	public:
		sl_uint64 getItemsCount() override;

		Ref<View> getView(sl_uint64 index, View* original, View* parent) override;

	protected:
		AtomicList< Ref<View> > m_list;

	};

	class ViewRowAdapter : public ViewAdapter
	{
		SLIB_DECLARE_OBJECT

	public:
		ViewRowAdapter();

		~ViewRowAdapter();

	public:
		static Ref<ViewRowAdapter> create(sl_uint32 nColumns, const Ref<ViewAdapter>& itemAdapter);

	public:
		sl_uint32 getColumnsCount();

		void setColumnsCount(sl_uint32 nColumns);

		Ref<ViewAdapter> getItemAdapter();

		void setItemAdapter(const Ref<ViewAdapter>& adapter);

	public:
		sl_uint64 getItemsCount() override;

		Ref<View> getView(sl_uint64 index, View* original, View* parent) override;

		sl_ui_len getAverageItemHeight(View* parent) override;

		sl_ui_len getItemHeight(sl_uint64 index, View* parent) override;

		sl_uint32 getMaximumItemsCountPerPage(View* parent) override;

	protected:
		sl_uint32 m_nColumns;
		AtomicRef<ViewAdapter> m_itemAdapter;

	};

}

#endif


text:
import React from 'react';
import Layout from '../../components/Layout';
import Page from '../../components/Page';

export default {

  path: '/about',

  async action({ locale }) {
    const data = await new Promise((resolve) => {
      require.ensure([], (require) => {
        try {
          resolve(require(`./about.${locale}.md`)); // eslint-disable-line import/no-dynamic-require
        } catch (e) {
          resolve(require('./about.md'));
        }
      }, 'about');
    });

    return {
      title: data.title,
      chunk: 'about',
      component: <Layout><Page {...data} /></Layout>,
    };
  },

};


text:
cask 'qownnotes' do
  version '18.06.6,b3652-080019'
  sha256 '66f56b287eabff8531c3bcc93a6bbc1d12167ea9cedbd12ac6b2dc1e1a03e400'

  # github.com/pbek/QOwnNotes was verified as official when first introduced to the cask
  url "https://github.com/pbek/QOwnNotes/releases/download/macosx-#{version.after_comma}/QOwnNotes-#{version.before_comma}.dmg"
  appcast 'https://github.com/pbek/QOwnNotes/releases.atom'
  name 'QOwnNotes'
  homepage 'https://www.qownnotes.org/'

  app 'QOwnNotes.app'
end


text:
#ifndef CINEMA_H
#define CINEMA_H 1

#include <sthread.h>

/*estrutura de coordenadas*/
typedef struct Coords_t {
	int row;
	int column;
} Coords_t;

/*estrura Pedido*/
typedef struct pedido{

	Coords_t*   	lugares;
	int 			dimensao;
	int 			tipo; // 0 - cancelar,  1 - reserva, 2 - prereserva, 3 - confirma prereserva, 4 - mostra
	int 			id;
	int 			resultado;
	time_t 			tempo_vida;
	time_t 			tempo_entrada;
	sthread_sem_t	semaforo;

} Pedido;

/* Functions exposed by the cinema management system to the Clients */

/* Used to initialize the server side of the application. It takes as input parameter the size of the cinema, the number of threads to be concurrently activated on the server side, and the size of the internal buffer used to enqueue user client requests.

This function returns:
   *   0, if the initialization was successful;
   *  -1 if the initialization failed;
   *  -2 if the cinema has already been initialized. In this case the function has no effect.*/
int init_cinema(unsigned int num_rows, unsigned int num_cols, int num_server_threads, int buf_size);

/* returns -1 if not all seats are atomically reservable. Otherwise it returns the reservation id, encoded as an integer larger than 0.*/
int reserva(Coords_t* lugares, int dim);

/* returns -1 if not all seats are atomically reservable. Otherwise it reserves the seats for the specified number of seconds, and returns the reservation id, encoded as an integer larger than 0. */
int prereserva(Coords_t* lugares, int dim, int segundos);

/* returns -1 if the reservation does not currently exist; 0 if the pre-booking was confirmed and updated into a booking. */
int confirma_prereserva(int reservation_id);

/* returns -1 if the reservation does not currently exist; 0 if the reservation was removed and the correspondig seats made available */
int cancela(int reservation_id);

/* displays the status of the cinema. The format of the ouput must be compliant with the one generated by the file cinema/show.c */
void mostra_cinema();

#endif