<commit_before>import { h } from 'hyperapp'
import { toKebabCase } from '../utils'
import Code from './Code'

import APPEND_TO from '../../snippets/append-to'
import WAIT from '../../snippets/wait'

const Prop = (props, children) => {
  const id = toKebabCase(props.name) + '-option'
  return (

          {props.name}

        {props.default}

        {Array.isArray(props.value) ? (
          props.value.map(i => (

              {i}

          ))
        ) : (
          {props.value}
        )}

      {children}

  )
}

export default () => (

          Prop
          Default
          Value
          Description

        <Prop name="a11y" default="true" value="Boolean">
          If true, ensures the reference element can receive focus
          by adding tabindex="0" if the element is not natively
          focusable like a button.
        </Prop>
        <Prop name="allowHTML" default="true" value="Boolean">
          Determines if HTML can be rendered in the tippy.
        </Prop>
        <Prop name="animateFill" default="true" value="Boolean">
          Determines if the tippy's background fill should be animated. Disabled
          if arrow: true.
        </Prop>
        <Prop
          name="animation"
          default=""shift-away""
          value={[
            '"fade"',
            '"scale"',
            '"shift-toward"',
            '"perspective"',
            '"shift-away"'
          ]}
        >
          The type of transition animation.
        </Prop>
        <Prop
          name="appendTo"
          default="document.body"
          value={['Element', 'Function']}
        >
          The element to append the tippy to. Use a function that returns an
          element to dynamically append the tippy relative to the reference
          element.

        </Prop>
        <Prop name="arrow" default="false" value="Boolean">
          Determines if an arrow should be added to the tippy pointing toward
          the reference element.
        </Prop>
        <Prop
          name="arrowType"
          default=""sharp""
          value={['"sharp"', '"round"']}
        >
          The type of arrow. "sharp" is a CSS triangle using the
          border method, while "round" is a custom SVG shape.
        </Prop>
        <Prop
          name="arrowTransform"
          default=""""
          value={['"scaleX(2)"', '"scale(0.8)"']}
        >
          CSS transform to apply to the arrow. Only scale and{' '}
          translate are supported. It is dynamic. Apply the
          transform that you would normally give to a "top"{' '}
          placement, even if the placement is different.
        </Prop>
        <Prop
          name="content"
          default=""""
          value={['String', 'Element']}
        >
          The content of the tippy.
        </Prop>
        <Prop name="delay" default="[0, 20]" value={['Number', '[show, hide]']}>
          Delay in ms once a trigger event is fired before a tippy shows or
          hides. Use an array of numbers such as [100, 500] to
          specify a different value for show and hide. Use null in
          the array to use the default value, e.g. [null, 50].
        </Prop>
        <Prop
          name="duration"
          default="[275, 250]"
          value={['Number', '[show, hide]']}
        >
          Duration of the CSS transition animation in ms. Use an array of
          numbers such as [100, 500] to specify a different value
          for show and hide. Add null in the array to use the
          default value, e.g. [null, 50].
        </Prop>
        <Prop name="distance" default="10" value="Number">
          How far in pixels the tippy element is from the reference element.
          Only applies to a single axis and not to the parent popper element,
          see{' '}

            offset

          .
        </Prop>
        <Prop name="flip" default="true" value="Boolean">
          Determines if the tippy flips so that it is placed within the viewport
          as best it can be if there is not enough room.
        </Prop>
        <Prop
          name="flipBehavior"
          default=""flip""
          value={['"flip"', 'Array']}
        >
          Determines the order of flipping, i.e. which placements to prefer if a
          certain placement cannot be used. Use an array such as{' '}
          ["bottom", "left"] to prefer the "left" placement if
          "bottom" is unavailable. By default, it chooses the opposite axis,
          i.e. "top".
        </Prop>
        <Prop
          name="followCursor"
          default="false"
          value={['Boolean', '"vertical"', '"horizontal"']}
        >
          Determines if the tippy follows the user's mouse cursor while showing.
          Use the strings "vertical" or "horizontal"
          to only follow the cursor on a single axis.
        </Prop>
        <Prop name="hideOnClick" default="true" value={['Boolean', '"toggle"']}>
          Determines if the tippy should hide if its reference element was
          clicked. For click-triggered tippys, using false will
          prevent the tippy from ever hiding once it is showing. To prevent
          clicks outside of the tippy from hiding it but still allow it to be
          toggled, use the string "toggle".
        </Prop>
        <Prop name="inertia" default="false" value="Boolean">
          Adds an attribute to the tippy element that changes the CSS transition
          timing function to add an inertial "slingshot" effect to the
          animation.
        </Prop>
        <Prop name="interactive" default="false" value="Boolean">
          Determines if a tippy should be interactive, i.e. able to be hovered
          over or clicked without hiding.
        </Prop>
        <Prop name="interactiveBorder" default="false" value="Boolean">
          Determines the size of the invisible border around a tippy that will
          prevent it from hiding (only relevant for the hover trigger). Useful
          to prevent the tippy from accidentally hiding from clumsy cursor
          movements.
        </Prop>
        <Prop name="interactiveDebounce" default="0" value="Number">
          A number in ms that debounces the onMouseMove handler
          which determines when the tippy should hide.
        </Prop>
        <Prop name="lazy" default="true" value="Boolean">
          By default, the popperInstance (the positioning engine
          for the tippy) is lazily created. That is, it's only created when
          necessary (i.e. triggering the tippy for the first time). Setting this
          prop to false allows you to access the instance synchronously without
          needing to show the tippy.
        </Prop>
        <Prop name="livePlacement" default="true" value="Boolean">
          Determines if the popper instance should listen to scroll events. This
          means it will update the position on scroll. If you don't want the
          tippy to flip around when scrolling, and the tippy's reference is not
          in a scrollable container, you can set this to false.
        </Prop>
        <Prop name="multiple" default="false" value="Boolean">
          Determines if the reference can have multiple tippy instances.
        </Prop>
        <Prop name="offset" default="false" value="Boolean">
          An offset that Popper.js uses to offset the popper element. Can work
          with both the x and y axis, distinct from{' '}
          distance.
        </Prop>
        <Prop name="onHidden" default="noop" value="Function">
          Lifecycle function invoked when a tippy has fully transitioned out.
        </Prop>
        <Prop name="onHide" default="noop" value="Function">
          Lifecycle function invoked when a tippy begins to transition out.
        </Prop>
        <Prop name="onShow" default="noop" value="Function">
          Lifecycle function invoked when a tippy begins to transition in.
        </Prop>
        <Prop name="onShown" default="noop" value="Function">
          Lifecycle function invoked when a tippy has fully transitioned in.
        </Prop>
        <Prop name="performance" default="false" value="Boolean">
          If true, disables data-tippy-* attributes which reduces
          init execution by half.
        </Prop>
        <Prop
          name="placement"
          default=""top""
          value={['"top"', '"bottom"', '"left"', '"right"']}
          type="string"
        >
          Positions the tippy relative to its reference element. Use the suffix{' '}
          -start or -end to shift the tippy to the
          start or end of the reference element, instead of centering it. For
          example, top-start or left-end.
        </Prop>
        <Prop name="popperOptions" default="{}" value="Object">
          Specify custom Popper.js options. See the{' '}

            Popper.js documentation
          {' '}
          for more.
        </Prop>
        <Prop
          name="shouldPopperHideOnBlur"
          default="(FocusOutEvent) => true"
          value="Function"
        >
          A function that returns a boolean to determine if the popper element
          should hide if it's blurred (applies only if interactive).{' '}

          If the popper element is blurred (i.e. no elements within it are in
          focus), the popper is hidden. However, there are cases in which you
          may need to keep it visible even when not in focus.
        </Prop>
        <Prop name="showOnInit" default="false" value="Boolean">
          If true, the tooltip will be shown immediately once the
          instance is created.
        </Prop>
        <Prop
          name="size"
          default=""regular""
          value={['"small"', '"regular"', '"large"']}
        >
          The size of the tippy.
        </Prop>
        <Prop name="sticky" default="false" value="Boolean">
          Ensures the tippy stays stuck to its reference element if it moves
          around while showing.
        </Prop>
        <Prop name="target" default="""" value="String">
          CSS selector used for event delegation.
        </Prop>
        <Prop name="theme" default=""dark"" value="String">
          Themes added as classes (each separated by a space) to the tippy's
          class list, which adds a -theme suffix, i.e.{' '}
          "dark-theme".
        </Prop>
        <Prop name="touch" default="true" value="Boolean">
          Determines if the tippy displays on touch devices.
        </Prop>
        <Prop name="touchHold" default="false" value="Boolean">
          Determines trigger behavior on touch devices. If true, instead of a
          tap on the reference and a tap elsewhere to hide the tippy, the
          reference must be pressed and held for the tippy to show. Letting go
          from the screen will hide it.
        </Prop>
        <Prop
          name="trigger"
          default=""mouseenter focus""
          value={['"mouseenter"', '"focus"', '"click"', '"manual"']}
        >
          The events (each separated by a space) which cause a tippy to show.
          Use manual to only trigger the tippy programmatically.
        </Prop>
        <Prop name="updateDuration" default="200" value="Number">
          The transition duration between position updates for the{' '}
          sticky option. Set to 0 to prevent flips
          from transitioning.
        </Prop>
        <Prop name="wait" default="null" value="Function">
          A function that, when defined, will wait until you manually invoke{' '}
          show() when a tippy is triggered. It takes the tippy
          instance and the trigger event as arguments.

        </Prop>
        <Prop name="zIndex" default="9999" value="Number">
          The z-index of the popper element.
        </Prop>

)
<commit_msg>OptionsTable tweaks<commit_after>import { h } from 'hyperapp'
import { toKebabCase } from '../utils'
import Code from './Code'
import ExternalLink from './ExternalLink'

import APPEND_TO from '../../snippets/append-to'
import WAIT from '../../snippets/wait'

const Prop = (props, children) => {
  const id = toKebabCase(props.name) + '-option'
  return (

          {props.name}

        {props.default}

        {Array.isArray(props.value) ? (
          props.value.map(i => (

              {i}

          ))
        ) : (
          {props.value}
        )}

      {children}

  )
}

export default () => (

          Prop
          Default
          Value
          Description

        <Prop name="a11y" default="true" value="Boolean">
          If true, ensures the reference element can receive focus
          by adding tabindex="0" if the element is not natively
          focusable like a button.
        </Prop>
        <Prop name="allowHTML" default="true" value="Boolean">
          Determines if HTML can be rendered in the tippy.
        </Prop>
        <Prop name="animateFill" default="true" value="Boolean">
          Determines if the tippy's background fill should be animated. Disabled
          if arrow: true.
        </Prop>
        <Prop
          name="animation"
          default=""shift-away""
          value={[
            '"fade"',
            '"scale"',
            '"shift-toward"',
            '"perspective"',
            '"shift-away"'
          ]}
        >
          The type of transition animation.
        </Prop>
        <Prop
          name="appendTo"
          default="document.body"
          value={['Element', 'Function']}
        >
          The element to append the tippy to. Use a function that returns an
          element to dynamically append the tippy relative to the reference
          element.

        </Prop>
        <Prop name="arrow" default="false" value="Boolean">
          Determines if an arrow should be added to the tippy pointing toward
          the reference element.
        </Prop>
        <Prop
          name="arrowType"
          default=""sharp""
          value={['"sharp"', '"round"']}
        >
          The type of arrow. "sharp" is a CSS triangle using the
          border method, while "round" is a custom SVG shape.
        </Prop>
        <Prop
          name="arrowTransform"
          default=""""
          value={['"scaleX(2)"', '"scale(0.8)"']}
        >
          CSS transform to apply to the arrow. Only scale and{' '}
          translate are supported. It is dynamic. Apply the
          transform that you would normally give to a "top"{' '}
          placement, even if the placement is different.
        </Prop>
        <Prop
          name="content"
          default=""""
          value={['String', 'Element']}
        >
          The content of the tippy.
        </Prop>
        <Prop name="delay" default="[0, 20]" value={['Number', '[show, hide]']}>
          Delay in ms once a trigger event is fired before a tippy shows or
          hides. Use an array of numbers such as [100, 500] to
          specify a different value for show and hide. Use null in
          the array to use the default value, e.g. [null, 50].
        </Prop>
        <Prop
          name="duration"
          default="[275, 250]"
          value={['Number', '[show, hide]']}
        >
          Duration of the CSS transition animation in ms. Use an array of
          numbers such as [100, 500] to specify a different value
          for show and hide. Add null in the array to use the
          default value, e.g. [null, 50].
        </Prop>
        <Prop name="distance" default="10" value="Number">
          How far in pixels the tippy element is from the reference element.
          Only applies to a single axis and not to the parent popper element,
          see{' '}

            offset

          .
        </Prop>
        <Prop name="flip" default="true" value="Boolean">
          Determines if the tippy flips so that it is placed within the viewport
          as best it can be if there is not enough room.
        </Prop>
        <Prop
          name="flipBehavior"
          default=""flip""
          value={['"flip"', 'Array']}
        >
          Determines the order of flipping, i.e. which placements to prefer if a
          certain placement cannot be used. Use an array such as{' '}
          ["bottom", "left"] to prefer the "left" placement if
          "bottom" is unavailable. By default, it chooses the opposite axis,
          i.e. "top".
        </Prop>
        <Prop
          name="followCursor"
          default="false"
          value={['Boolean', '"vertical"', '"horizontal"']}
        >
          Determines if the tippy follows the user's mouse cursor while showing.
          Use the strings "vertical" or "horizontal"
          to only follow the cursor on a single axis.
        </Prop>
        <Prop name="hideOnClick" default="true" value={['Boolean', '"toggle"']}>
          Determines if the tippy should hide if its reference element was
          clicked. For click-triggered tippys, using false will
          prevent the tippy from ever hiding once it is showing. To prevent
          clicks outside of the tippy from hiding it but still allow it to be
          toggled, use the string "toggle".
        </Prop>
        <Prop name="inertia" default="false" value="Boolean">
          Adds an attribute to the tippy element that changes the CSS transition
          timing function to add an inertial "slingshot" effect to the
          animation.
        </Prop>
        <Prop name="interactive" default="false" value="Boolean">
          Determines if a tippy should be interactive, i.e. able to be hovered
          over or clicked without hiding.
        </Prop>
        <Prop name="interactiveBorder" default="false" value="Boolean">
          Determines the size of the invisible border around a tippy that will
          prevent it from hiding (only relevant for the hover trigger). Useful
          to prevent the tippy from accidentally hiding from clumsy cursor
          movements.
        </Prop>
        <Prop name="interactiveDebounce" default="0" value="Number">
          A number in ms that debounces the onMouseMove handler
          which determines when the tippy should hide.
        </Prop>
        <Prop name="lazy" default="true" value="Boolean">
          By default, the popperInstance (the positioning engine
          for the tippy) is lazily created. That is, it's only created when
          necessary (i.e. triggering the tippy for the first time). Setting this
          prop to false allows you to access the instance
          synchronously without needing to show the tippy first.
        </Prop>
        <Prop name="livePlacement" default="true" value="Boolean">
          Determines if the popper instance should listen to scroll events. This
          means it will update the position on scroll. If you don't want the
          tippy to flip around when scrolling, and the tippy's reference is not
          in a scrollable container, you can set this to false.
        </Prop>
        <Prop name="multiple" default="false" value="Boolean">
          Determines if the reference can have multiple tippy instances.
        </Prop>
        <Prop name="offset" default="false" value="Boolean">
          An offset that Popper.js uses to offset the popper element. Can work
          with both the x and y axis, distinct from{' '}
          distance.
        </Prop>
        <Prop name="onHidden" default="noop" value="Function">
          Lifecycle function invoked when a tippy has fully transitioned out.
        </Prop>
        <Prop name="onHide" default="noop" value="Function">
          Lifecycle function invoked when a tippy begins to transition out.
        </Prop>
        <Prop name="onShow" default="noop" value="Function">
          Lifecycle function invoked when a tippy begins to transition in.
        </Prop>
        <Prop name="onShown" default="noop" value="Function">
          Lifecycle function invoked when a tippy has fully transitioned in.
        </Prop>
        <Prop name="performance" default="false" value="Boolean">
          If true, disables data-tippy-* attributes which reduces
          init execution by half.
        </Prop>
        <Prop
          name="placement"
          default=""top""
          value={['"top"', '"bottom"', '"left"', '"right"']}
          type="string"
        >
          Positions the tippy relative to its reference element. Use the suffix{' '}
          -start or -end to shift the tippy to the
          start or end of the reference element, instead of centering it. For
          example, top-start or left-end.
        </Prop>
        <Prop name="popperOptions" default="{}" value="Object">
          Specify custom Popper.js options. See the{' '}
          <ExternalLink to="https://popper.js.org/popper-documentation.html">
            Popper.js documentation
          </ExternalLink>{' '}
          for more.
        </Prop>
        <Prop
          name="shouldPopperHideOnBlur"
          default="(FocusOutEvent) => true"
          value="Function"
        >
          A function that returns a boolean to determine if the popper element
          should hide if it's blurred (applies only if interactive).{' '}

          If the popper element is blurred (i.e. no elements within it are in
          focus), the popper is hidden. However, there are cases in which you
          may need to keep it visible even when not in focus.
        </Prop>
        <Prop name="showOnInit" default="false" value="Boolean">
          If true, the tooltip will be shown immediately once the
          instance is created.
        </Prop>
        <Prop
          name="size"
          default=""regular""
          value={['"small"', '"regular"', '"large"']}
        >
          The size of the tippy.
        </Prop>
        <Prop name="sticky" default="false" value="Boolean">
          Ensures the tippy stays stuck to its reference element if it moves
          around while showing.
        </Prop>
        <Prop name="target" default="""" value="String">
          CSS selector used for event delegation.
        </Prop>
        <Prop name="theme" default=""dark"" value="String">
          Themes added as classes (each separated by a space) to the tippy's
          class list, which adds a -theme suffix, i.e.{' '}
          "dark-theme".
        </Prop>
        <Prop name="touch" default="true" value="Boolean">
          Determines if the tippy displays on touch devices.
        </Prop>
        <Prop name="touchHold" default="false" value="Boolean">
          Determines trigger behavior on touch devices. If true, instead of a
          tap on the reference and a tap elsewhere to hide the tippy, the
          reference must be pressed and held for the tippy to show. Letting go
          from the screen will hide it.
        </Prop>
        <Prop
          name="trigger"
          default=""mouseenter focus""
          value={['"mouseenter"', '"focus"', '"click"', '"manual"']}
        >
          The events (each separated by a space) which cause a tippy to show.
          Use manual to only trigger the tippy programmatically.
        </Prop>
        <Prop name="updateDuration" default="200" value="Number">
          The transition duration between position updates for the{' '}
          sticky option. Set to 0 to prevent flips
          from transitioning.
        </Prop>
        <Prop name="wait" default="null" value="Function">
          A function that, when defined, will wait until you manually invoke{' '}
          show() when a tippy is triggered. It takes the tippy
          instance and the trigger event as arguments.

        </Prop>
        <Prop name="zIndex" default="9999" value="Number">
          The z-index of the popper element.
        </Prop>

)
<|endoftext|>


<commit_before>/**
 * KumbiaPHP web & app Framework
 *
 * LICENSE
 *
 * This source file is subject to the new BSD license that is bundled
 * with this package in the file LICENSE.txt.
 * It is also available through the world-wide-web at this URL:
 * http://wiki.kumbiaphp.com/Licencia
 * If you did not receive a copy of the license and are unable to
 * obtain it through the world-wide-web, please send an email
 * to license@kumbiaphp.com so we can send you a copy immediately.
 *
 * Plugin para jQuery que incluye los callbacks basicos para los Helpers
 *
 * @copyright  Copyright (c) 2005-2014 Kumbia Team (http://www.kumbiaphp.com)
 * @license	http://wiki.kumbiaphp.com/Licencia	 New BSD License
 */

(function($) {
	/**
	 * Objeto KumbiaPHP
	 *
	 */
	$.KumbiaPHP = {
		/**
		 * Ruta al directorio public en el servidor
		 *
		 * @var String
		 */
		publicPath : null,

		/**
		 * Plugins cargados
		 *
		 * @var Array
		 */
		plugin: [],

		/**
		 * Muestra mensaje de confirmacion
		 *
		 * @param Object event
		 */
		cConfirm: function(event) {
			var este=$(this);
			if(!confirm(este.data('msg'))) {
				event.preventDefault();
			}
		},

		/**
		 * Aplica un efecto a un elemento
		 *
		 * @param String fx
		 */
		cFx: function(fx) {
			return function(event) {
				event.preventDefault();
				var este=$(this),
					rel = $('#'+este.data('to'));
				rel[fx]();
			}
		},

		/**
		 * Carga con AJAX
		 *
		 * @param Object event
		 */
		cRemote: function(event) {
			var este=$(this), rel = $('#'+este.data('to'));
			event.preventDefault();
			rel.load(this.href);
		},

		/**
		 * Carga con AJAX y Confirmacion
		 *
		 * @param Object event
		 */
		cRemoteConfirm: function(event) {
			var este=$(this), rel = $('#'+este.data('to'));
			event.preventDefault();
			if(confirm(este.data('msg'))) {
				rel.load(this.href);
			}
		},

		/**
		 * Enviar formularios de manera asincronica, via POST
		 * Y los carga en un contenedor
		 */
		cFRemote: function(event){
			event.preventDefault();
			este = $(this);
			var button = $('[type=submit]', este);
			button.attr('disabled', 'disabled');
			var url = este.attr('action');
			var div = este.attr('data-to');
			$.post(url, este.serialize(), function(data, status){
				var capa = $('#'+div);
				capa.html(data);
				capa.hide();
				capa.show('slow');
				button.attr('disabled', null);
			});
		},

		/**
		 * Carga con AJAX al cambiar select
		 *
		 * @param Object event
		 */
		cUpdaterSelect: function(event) {
            var $t = $(this),$u= $('#' + $t.data('update'))
				url = $t.data('url');
            $u.empty();
            $.get(url, {'id':$t.val()}, function(d){
				for(i in d){
					var a = $('<option />').text(d[i]).val(i);
					$u.append(a);
				}
			}, 'json');
		},

		/**
		 * Enlaza a las clases por defecto
		 *
		 */
		bind : function() {
            // Enlace y boton con confirmacion
			$("a.js-confirm, input.js-confirm").on('click', this.cConfirm);

            // Enlace ajax
			$("a.js-remote").on('click', this.cRemote);

            // Enlace ajax con confirmacion
			$("a.js-remote-confirm").on('click', this.cRemoteConfirm);

            // Efecto show
			$("a.js-show").on('click', this.cFx('show'));

            // Efecto hide
			$("a.js-hide").on('click', this.cFx('hide'));

            // Efecto toggle
			$("a.js-toggle").on('click', this.cFx('toggle'));

            // Efecto fadeIn
			$("a.js-fade-in").on('click', this.cFx('fadeIn'));

            // Efecto fadeOut
			$("a.js-fade-out").on('click', this.cFx('fadeOut'));

            // Formulario ajax
			$("form.js-remote").on('submit', this.cFRemote);

            // Lista desplegable que actualiza con ajax
            $("select.js-remote").on('change', this.cUpdaterSelect);

            // Enlazar DatePicker
			$.KumbiaPHP.bindDatePicker();

		},

        /**
         * Implementa la autocarga de plugins, estos deben seguir
         * una convención para que pueda funcionar correctamente
         */
        autoload: function(){
            var elem = $("[class*='jp-']");
            $.each(elem, function(i, val){
                var este = $(this); //apunta al elemento con clase jp-*
                var classes = este.attr('class').split(' ');
                for (i in classes){
                    if(classes[i].substr(0, 3) == 'jp-'){
                        if($.inArray(classes[i].substr(3),$.KumbiaPHP.plugin) != -1)
                            continue;
                        $.KumbiaPHP.plugin.push(classes[i].substr(3))
                    }
                }
            });
            var head = $('head');
            for(i in $.KumbiaPHP.plugin){
                $.ajaxSetup({ cache: true});
                head.append('<link href="' + $.KumbiaPHP.publicPath + 'css/' + $.KumbiaPHP.plugin[i] + '.css" type="text/css" rel="stylesheet"/>');
				$.getScript($.KumbiaPHP.publicPath + 'javascript/jquery/jquery.' + $.KumbiaPHP.plugin[i] + '.js', function(data, text){});
            }
		},

		/**
		 * Carga y Enlaza Unobstrusive DatePicker en caso de ser necesario
		 *
		 */
		bindDatePicker: function() {

			// Selecciona los campos input
			var inputs = $('input.js-datepicker');
			/**
			 * Funcion encargada de enlazar el DatePicker a los Input
			 *
			 */
			var bindInputs = function() {
				inputs.each(function() {
					var opts = {monthSelector: true,yearSelector:true};
					var input = $(this);
					// Verifica si hay mínimo
					if(input.attr('min') != undefined) {
						opts.dateMin = input.attr('min').split('-');
					}
					// Verifica si ha máximo
					if(input.attr('max') != undefined) {
						opts.dateMax = input.attr('max').split('-');
					}

					// Crea el calendario
					input.pickadate(opts);
				});
			}

			// Si ya esta cargado Unobstrusive DatePicker, lo integra de una vez
			if(typeof($.pickadate) != "undefined") {
				return bindInputs();
			}

			// Carga la hoja de estilos
			$('head').append('<link href="' + this.publicPath + 'css/pickadate.css" type="text/css" rel="stylesheet"/>');

			// Carga Unobstrusive DatePicker, para poder usar cache
			jQuery.ajax({ dataType: "script",cache: true, url: this.publicPath + 'javascript/jquery/pickadate.js'}).done(function(){
				bindInputs();
			});
		},

		/**
		 * Inicializa el plugin
		 *
		 */
		initialize: function() {
			// Obtiene el publicPath, restando los caracteres que sobran
			// de la ruta, respecto a la ruta de ubicacion del plugin de KumbiaPHP
			// "javascript/jquery/jquery.kumbiaphp.js"
			var src = $('script:last').attr('src');
			this.publicPath = src.substr(0, src.length - 37);

			// Enlaza a las clases por defecto
			$(function(){
				$.KumbiaPHP.bind();
				$.KumbiaPHP.autoload();

			});
		}
	}

	// Inicializa el plugin
	$.KumbiaPHP.initialize();
})(jQuery);
<commit_msg>DELEGAR EVENTOS<commit_after>/**
 * KumbiaPHP web & app Framework
 *
 * LICENSE
 *
 * This source file is subject to the new BSD license that is bundled
 * with this package in the file LICENSE.txt.
 * It is also available through the world-wide-web at this URL:
 * http://wiki.kumbiaphp.com/Licencia
 * If you did not receive a copy of the license and are unable to
 * obtain it through the world-wide-web, please send an email
 * to license@kumbiaphp.com so we can send you a copy immediately.
 *
 * Plugin para jQuery que incluye los callbacks basicos para los Helpers
 *
 * @copyright  Copyright (c) 2005-2014 Kumbia Team (http://www.kumbiaphp.com)
 * @license	http://wiki.kumbiaphp.com/Licencia	 New BSD License
 */

(function($) {
	/**
	 * Objeto KumbiaPHP
	 *
	 */
	$.KumbiaPHP = {
		/**
		 * Ruta al directorio public en el servidor
		 *
		 * @var String
		 */
		publicPath : null,

		/**
		 * Plugins cargados
		 *
		 * @var Array
		 */
		plugin: [],

		/**
		 * Muestra mensaje de confirmacion
		 *
		 * @param Object event
		 */
		cConfirm: function(event) {
			var este=$(this);
			if(!confirm(este.data('msg'))) {
				event.preventDefault();
			}
		},

		/**
		 * Aplica un efecto a un elemento
		 *
		 * @param String fx
		 */
		cFx: function(fx) {
			return function(event) {
				event.preventDefault();
				var este=$(this),
					rel = $('#'+este.data('to'));
				rel[fx]();
			}
		},

		/**
		 * Carga con AJAX
		 *
		 * @param Object event
		 */
		cRemote: function(event) {
			var este=$(this), rel = $('#'+este.data('to'));
			event.preventDefault();
			rel.load(this.href);
		},

		/**
		 * Carga con AJAX y Confirmacion
		 *
		 * @param Object event
		 */
		cRemoteConfirm: function(event) {
			var este=$(this), rel = $('#'+este.data('to'));
			event.preventDefault();
			if(confirm(este.data('msg'))) {
				rel.load(this.href);
			}
		},

		/**
		 * Enviar formularios de manera asincronica, via POST
		 * Y los carga en un contenedor
		 */
		cFRemote: function(event){
			event.preventDefault();
			este = $(this);
			var button = $('[type=submit]', este);
			button.attr('disabled', 'disabled');
			var url = este.attr('action');
			var div = este.attr('data-to');
			$.post(url, este.serialize(), function(data, status){
				var capa = $('#'+div);
				capa.html(data);
				capa.hide();
				capa.show('slow');
				button.attr('disabled', null);
			});
		},

		/**
		 * Carga con AJAX al cambiar select
		 *
		 * @param Object event
		 */
		cUpdaterSelect: function(event) {
            var $t = $(this),$u= $('#' + $t.data('update'))
				url = $t.data('url');
            $u.empty();
            $.get(url, {'id':$t.val()}, function(d){
				for(i in d){
					var a = $('<option />').text(d[i]).val(i);
					$u.append(a);
				}
			}, 'json');
		},

		/**
		 * Enlaza a las clases por defecto
		 *
		 */
		bind : function() {
            // Enlace y boton con confirmacion
			$("body").on('click','a.js-confirm, input.js-confirm', this.cConfirm);

            // Enlace ajax
			$("body").on('click','a.js-remote', this.cRemote);

            // Enlace ajax con confirmacion
			$("body").on('click','a.js-remote-confirm', this.cRemoteConfirm);

            // Efecto show
			$("body").on('click','a.js-show', this.cFx('show'));

            // Efecto hide
			$("body").on('click','a.js-hide', this.cFx('hide'));

            // Efecto toggle
			$("body").on('click','a.js-toggle', this.cFx('toggle'));

            // Efecto fadeIn
			$("body").on('click','a.js-fade-in', this.cFx('fadeIn'));

            // Efecto fadeOut
			$("body").on('click','a.js-fade-out', this.cFx('fadeOut'));

            // Formulario ajax
			$("body").on('submit','form.js-remote', this.cFRemote);

            // Lista desplegable que actualiza con ajax
            $("body").on('change','select.js-remote', this.cUpdaterSelect);

            // Enlazar DatePicker
			$.KumbiaPHP.bindDatePicker();

		},

        /**
         * Implementa la autocarga de plugins, estos deben seguir
         * una convención para que pueda funcionar correctamente
         */
        autoload: function(){
            var elem = $("[class*='jp-']");
            $.each(elem, function(i, val){
                var este = $(this); //apunta al elemento con clase jp-*
                var classes = este.attr('class').split(' ');
                for (i in classes){
                    if(classes[i].substr(0, 3) == 'jp-'){
                        if($.inArray(classes[i].substr(3),$.KumbiaPHP.plugin) != -1)
                            continue;
                        $.KumbiaPHP.plugin.push(classes[i].substr(3))
                    }
                }
            });
            var head = $('head');
            for(i in $.KumbiaPHP.plugin){
                $.ajaxSetup({ cache: true});
                head.append('<link href="' + $.KumbiaPHP.publicPath + 'css/' + $.KumbiaPHP.plugin[i] + '.css" type="text/css" rel="stylesheet"/>');
				$.getScript($.KumbiaPHP.publicPath + 'javascript/jquery/jquery.' + $.KumbiaPHP.plugin[i] + '.js', function(data, text){});
            }
		},

		/**
		 * Carga y Enlaza Unobstrusive DatePicker en caso de ser necesario
		 *
		 */
		bindDatePicker: function() {

			// Selecciona los campos input
			var inputs = $('input.js-datepicker');
			/**
			 * Funcion encargada de enlazar el DatePicker a los Input
			 *
			 */
			var bindInputs = function() {
				inputs.each(function() {
					var opts = {monthSelector: true,yearSelector:true};
					var input = $(this);
					// Verifica si hay mínimo
					if(input.attr('min') != undefined) {
						opts.dateMin = input.attr('min').split('-');
					}
					// Verifica si ha máximo
					if(input.attr('max') != undefined) {
						opts.dateMax = input.attr('max').split('-');
					}

					// Crea el calendario
					input.pickadate(opts);
				});
			}

			// Si ya esta cargado Unobstrusive DatePicker, lo integra de una vez
			if(typeof($.pickadate) != "undefined") {
				return bindInputs();
			}

			// Carga la hoja de estilos
			$('head').append('<link href="' + this.publicPath + 'css/pickadate.css" type="text/css" rel="stylesheet"/>');

			// Carga Unobstrusive DatePicker, para poder usar cache
			jQuery.ajax({ dataType: "script",cache: true, url: this.publicPath + 'javascript/jquery/pickadate.js'}).done(function(){
				bindInputs();
			});
		},

		/**
		 * Inicializa el plugin
		 *
		 */
		initialize: function() {
			// Obtiene el publicPath, restando los caracteres que sobran
			// de la ruta, respecto a la ruta de ubicacion del plugin de KumbiaPHP
			// "javascript/jquery/jquery.kumbiaphp.js"
			var src = $('script:last').attr('src');
			this.publicPath = src.substr(0, src.length - 37);

			// Enlaza a las clases por defecto
			$(function(){
				$.KumbiaPHP.bind();
				$.KumbiaPHP.autoload();

			});
		}
	}

	// Inicializa el plugin
	$.KumbiaPHP.initialize();
})(jQuery);
<|endoftext|>


<commit_before>/*
Copyright 2006-2007, Open Source Applications Foundation

 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
*/

/* The following are assertion functions available in all browsers
Currently the dynamic ones are not built into the IDE UI, but will
in the very near future.
*/

windmill.controller.asserts.assertRegistry = {
  'assertTrue': {
  expr: function (a) {
      if (typeof a != 'boolean') {
        throw('Bad argument to assertTrue.');
      }
      return a === true;
    },
  errMsg: 'expected true but was false.'
  },

  'assertFalse': {
  expr: function (a) {
      if (typeof a != 'boolean') {
        throw('Bad argument to assertFalse.');
      }
      return a === false;
    },
  errMsg: 'expected false but was true.'
  },

  'assertEquals': {
  expr: function (a, b) { return a === b; },
  errMsg: 'expected $1 but was $2.'
  },

  'assertNotEquals': {
  expr: function (a, b) { return a !== b; },
  errMsg: 'expected one of the two values not to be $1.'
  },

  'assertGreaterThan': {
  expr: function (a, b) { return a > b; },
  errMsg: 'expected a value greater than $2 but was $1.'
  },

  'assertLessThan': {
  expr: function (a, b) { return a < b; },
  errMsg: 'expected a value less than $2 but was $1.'
  },

  'assertNull': {
  expr: function (a) { return a === null; },
  errMsg: 'expected to be null but was $1.'
  },

  'assertNotNull': {
  expr: function (a) { return a !== null; },
  errMsg: 'expected not to be null but was null.'
  },

  'assertUndefined': {
  expr: function (a) { return typeof a == 'undefined'; },
  errMsg: 'expected to be undefined but was $1.'
  },

  'assertNotUndefined': {
  expr: function (a) { return typeof a != 'undefined'; },
  errMsg: 'expected not to be undefined but was undefined.'
  },

  'assertNaN': {
  expr: function (a) { return isNaN(a); },
  errMsg: 'expected $1 to be NaN, but was not NaN.'
  },

  'assertNotNaN': {
  expr: function (a) { return !isNaN(a); },
  errMsg: 'expected $1 not to be NaN, but was NaN.'
  },

  'assertEvaluatesToTrue': {
  expr: function (a) { return !!a; },
  errMsg: 'value of $1 does not evaluate to true.'
  },

  'assertEvaluatesToFalse': {
  expr: function (a) { return !a; },
  errMsg: 'value of $1 does not evaluate to false.'
  },

  'assertContains': {
  expr: function (a, b) {
      if (typeof a != 'string' || typeof b != 'string') {
        throw('Bad argument to assertContains.');
      }
      return (a.indexOf(b) > -1);
    },
  errMsg: 'value of $1 does not contain $2.'
  }
};

//Currently only does one level below the provided div
//To make it more thorough it needs recursion to be implemented later
windmill.controller.asserts.assertText = function (paramObject) {
  var n = lookupNode(paramObject, false);
  var validator = paramObject.validator;

  var inner = n.innerHTML;
  if (n.textContent){
    inner = n.textContent;
  }
  else if (n.innerText) {
    inner = n.innerText;
  }
  // new lines and spaces break a lot of these, so removing them makes sense
  var iHTML = inner.replace(/^\s*|\s*$/g,'');

  if (iHTML == validator){
    return true;
  }

  // var found = n.textContent;
  // if (found == undefined)
  //   found = n.innerText;
  throw "Text '" + validator +
        "' was not found in the provided node.  Found instead: " + iHTML;
};

windmill.controller.asserts.assertTextIn = function (paramObject) {
  var n = lookupNode(paramObject, false);
  var validator = paramObject.validator;

	//If no lookup was provided, search the whole document
	if (n == false){
		n = windmill.testWin().document.body;
	}

	var inner = n.innerHTML;
  if (n.textContent){
    inner = n.textContent;
  }
  else if (n.innerText){
    inner = n.innerText;
  }

  // new lines and spaces break a lot of these, so removing them makes sense
  var iHTML = inner.replace(/^\s*|\s*$/g,'');

  if (iHTML.indexOf(validator) != -1){
    return true;
  }
  else {
 	 // var found = n.textContent;
 	 // if (found == undefined)
  	//   found = n.innerText;
  	throw "Text '" + validator +
        "' was not found in the provided node.  Found instead: " + inner;
	}
};

//Assert that a specified node exists
windmill.controller.asserts.assertNode = function (paramObject) {
  var element = lookupNode(paramObject, false);
};

//Assert that a form element contains the expected value
windmill.controller.asserts.assertValue = function (paramObject) {
  //need to start moving test to use text instead of validator, its dumb
  var n = lookupNode(paramObject, false);
  var validator = paramObject.validator;

  if (n.value == undefined)
    throw "Element doesn't have a value";

  if (n.value != validator){
    throw "Found value \""+ n.value + "\" is not equal to \""+ validator+"\"";
  }
};

windmill.controller.asserts.assertValueIn = function (paramObject) {
  //need to start moving test to use text instead of validator, its dumb
  var n = lookupNode(paramObject, false);
  var validator = paramObject.validator;

  if (n.value == undefined)
    throw "Element doesn't have a value";

  if (n.value.indexOf(validator) == -1){
    throw "Found value \""+ n.value + "\" is not equal to \""+ validator+"\"";
  }
};

//Assert that a provided value is selected in a select element
windmill.controller.asserts.assertJS = function (paramObject) {
  var js = paramObject.js;
  var result = windmill.testWin().eval(js);
  if (result != true){
    throw "JavaScript did not return true."
  }
};

//Assert that a provided value is selected in a select element
windmill.controller.asserts.assertIDEJS = function (paramObject) {
  var js = paramObject.js;
  var result = eval(js);
  if (result != true){
    throw "JavaScript did not return true."
  }
};

//Asserting javascript with an element object available
windmill.controller.asserts.assertElemJS = function (paramObject) {
  var element = lookupNode(paramObject, false);
  var js = paramObject.js;
  var result = eval(js);
  if (result != true){
    throw "JavaScript did not return true."
  }
};

//Assert that a provided value is selected in a select element
windmill.controller.asserts.assertSelected = function (paramObject) {
  var n = lookupNode(paramObject, false);
  var validator = paramObject.validator;

  if ((n.options[n.selectedIndex].value != validator) && (n.options[n.selectedIndex].innerHTML != validator)){
    throw "Not selected, "+n.options[n.selectedIndex].value+" is not equal to " + validator;
  }
};

//Assert that a provided checkbox is checked
windmill.controller.asserts.assertChecked = function (paramObject) {
  var n = lookupNode(paramObject, false);

  if (!n.checked){
    throw "Checked property not true";
  }
};

// Assert that a an element's property is a particular value
windmill.controller.asserts.assertProperty = function (paramObject) {
  var element = lookupNode(paramObject, false);
  var vArray = paramObject.validator.split('|');
  if (vArray.length != 2)
    throw "Invalid validator '" + paramObject.validator + "'.  Use '|' to separate key from value.";

  var expected = "Expected property '" + vArray[0] + "' to have value '" + vArray[1] + "'. ";

  var value = eval ('element.' + vArray[0]+';');
  if (value == undefined)
    throw expected + "No '" + vArray[0] + "' property found.";

  if (value.indexOf(vArray[1]) != -1){
    return true;
  }
  if (String(value) == String(vArray[1])) {
    return true;
  }
  throw expected + "Found value '" + value + "' instead."
};

// Assert that a specified image has actually loaded
// The Safari workaround results in additional requests
// for broken images (in Safari only) but works reliably
windmill.controller.asserts.assertImageLoaded = function (paramObject) {
  var img = lookupNode(paramObject, false);
  if (!img || img.tagName != 'IMG') {
    throw "The node was not an image."
  }
  var comp = img.complete;
  var ret = null; // Return value

  // Workaround for Safari -- it only supports the
  // complete attrib on script-created images
  if (typeof comp == 'undefined') {
    test = new Image();
    // If the original image was successfully loaded,
    // src for new one should be pulled from cache
    test.src = img.src;
    comp = test.complete;
  }

  // Check the complete attrib. Note the strict
  // equality check -- we don't want undefined, null, etc.
  // --------------------------
  // False -- Img failed to load in IE/Safari, or is
  // still trying to load in FF
  if (comp === false) {
    throw "Image complete attrib false."
  }
  // True, but image has no size -- image failed to
  // load in FF
  else if (comp === true && img.naturalWidth == 0) {
    throw "Image has no size, failure to load."
  }
  // Otherwise all we can do is assume everything's
  // hunky-dory
  else {
    ret = true;
  }
  return ret;
};

windmill.controller.asserts._AssertFactory = new function () {
  var _this = this;
  function validateArgs(count, args) {
    if (!(args.length == count ||
	  (args.length == count + 1 && typeof(args[0]) == 'string') )) {
      throw('Incorrect arguments passed to assert function');
    }
  }
  function createErrMsg(msg, arr) {
    var str = msg;
    for (var i = 0; i < arr.length; i++) {
      //When calling jum functions arr is an array with a null entry
      if (arr[i] != null){
        var val = arr[i];
        var display = '<' + val.toString().replace(/\n/g, '') +
          '> (' + getTypeDetails(val) + ')';
        str = str.replace('$' + (i + 1).toString(), display);
      }
    }
    return str;
  }
  function getTypeDetails(val) {
    var r = typeof val;
    try {
      if (r == 'object' || r == 'function') {
        var m = val.constructor.toString().match(/function\s*([^( ]+)\(/);
						 if (m) { r = m[1]; }
						 else { r = 'Unknown Data Type' }
						 }
      }
      finally {
        r = r.substr(0, 1).toUpperCase() + r.substr(1);
        return r;
      }
    }
    this.createAssert = function (meth) {
      return function () {
      var args = Array.prototype.slice.call(arguments);
      args.unshift(meth);
      return _this.doAssert.apply(_this, args);
      }
    }
    this.doAssert = function () {
      // Convert arguments to real Array
      var args = Array.prototype.slice.call(arguments);
      // The actual assert method, e.g, 'equals'
      var meth = args.shift();
      // The assert object
      var asrt = windmill.controller.asserts.assertRegistry[meth];
      // The assert expresion
      var expr = asrt.expr;
      // Validate the args passed
      var valid = validateArgs(expr.length, args);
      // Pull off additional comment which may be first arg
      var comment = args.length > expr.length ?
        args.shift() : null;
      // Run the assert
      var res = expr.apply(window, args);
      if (res) {
	      return true;
      }
      else {
        var message = meth + ' -- ' +
        createErrMsg(asrt.errMsg, args);

	      throw new windmill.controller.asserts._WindmillAssertException(comment, message);
      }
    };
  };

// Create all the assert methods on windmill.controller.asserts
// Using the items in the assertRegistry
for (var meth in windmill.controller.asserts.assertRegistry) {
  windmill.controller.asserts[meth] = windmill.controller.asserts._AssertFactory.createAssert(meth);
  windmill.controller.asserts[meth].jsUnitAssert = true;
}

windmill.controller.asserts._WindmillAssertException = function (comment, message) {
  this.comment = comment;
  this.message = message;
};

<commit_msg>Final assertTextIn that was reverted for jfry<commit_after>/*
Copyright 2006-2007, Open Source Applications Foundation

 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
*/

/* The following are assertion functions available in all browsers
Currently the dynamic ones are not built into the IDE UI, but will
in the very near future.
*/

windmill.controller.asserts.assertRegistry = {
  'assertTrue': {
  expr: function (a) {
      if (typeof a != 'boolean') {
        throw('Bad argument to assertTrue.');
      }
      return a === true;
    },
  errMsg: 'expected true but was false.'
  },

  'assertFalse': {
  expr: function (a) {
      if (typeof a != 'boolean') {
        throw('Bad argument to assertFalse.');
      }
      return a === false;
    },
  errMsg: 'expected false but was true.'
  },

  'assertEquals': {
  expr: function (a, b) { return a === b; },
  errMsg: 'expected $1 but was $2.'
  },

  'assertNotEquals': {
  expr: function (a, b) { return a !== b; },
  errMsg: 'expected one of the two values not to be $1.'
  },

  'assertGreaterThan': {
  expr: function (a, b) { return a > b; },
  errMsg: 'expected a value greater than $2 but was $1.'
  },

  'assertLessThan': {
  expr: function (a, b) { return a < b; },
  errMsg: 'expected a value less than $2 but was $1.'
  },

  'assertNull': {
  expr: function (a) { return a === null; },
  errMsg: 'expected to be null but was $1.'
  },

  'assertNotNull': {
  expr: function (a) { return a !== null; },
  errMsg: 'expected not to be null but was null.'
  },

  'assertUndefined': {
  expr: function (a) { return typeof a == 'undefined'; },
  errMsg: 'expected to be undefined but was $1.'
  },

  'assertNotUndefined': {
  expr: function (a) { return typeof a != 'undefined'; },
  errMsg: 'expected not to be undefined but was undefined.'
  },

  'assertNaN': {
  expr: function (a) { return isNaN(a); },
  errMsg: 'expected $1 to be NaN, but was not NaN.'
  },

  'assertNotNaN': {
  expr: function (a) { return !isNaN(a); },
  errMsg: 'expected $1 not to be NaN, but was NaN.'
  },

  'assertEvaluatesToTrue': {
  expr: function (a) { return !!a; },
  errMsg: 'value of $1 does not evaluate to true.'
  },

  'assertEvaluatesToFalse': {
  expr: function (a) { return !a; },
  errMsg: 'value of $1 does not evaluate to false.'
  },

  'assertContains': {
  expr: function (a, b) {
      if (typeof a != 'string' || typeof b != 'string') {
        throw('Bad argument to assertContains.');
      }
      return (a.indexOf(b) > -1);
    },
  errMsg: 'value of $1 does not contain $2.'
  }
};

//Currently only does one level below the provided div
//To make it more thorough it needs recursion to be implemented later
windmill.controller.asserts.assertText = function (paramObject) {
  var n = lookupNode(paramObject, false);
  var validator = paramObject.validator;

  var inner = n.innerHTML;
  if (n.textContent){
    inner = n.textContent;
  }
  else if (n.innerText) {
    inner = n.innerText;
  }
  // new lines and spaces break a lot of these, so removing them makes sense
  var iHTML = inner.replace(/^\s*|\s*$/g,'');

  if (iHTML == validator){
    return true;
  }

  // var found = n.textContent;
  // if (found == undefined)
  //   found = n.innerText;
  throw "Text '" + validator +
        "' was not found in the provided node.  Found instead: " + iHTML;
};

windmill.controller.asserts.assertTextIn = function (paramObject) {
  try { var n = lookupNode(paramObject, false); }
	catch(err){ n = false }

  var validator = paramObject.validator;

	//If no lookup was provided, search the whole document
	if (n == false || n == []){
		n = windmill.testWin().document.body;
	}
	var inner = n.innerHTML;
  if (n.textContent){
    inner = n.textContent;
  }
  else if (n.innerText){
    inner = n.innerText;
  }

  // new lines and spaces break a lot of these, so removing them makes sense
  var iHTML = inner.replace(/^\s*|\s*$/g,'');
  if (iHTML.indexOf(validator) != -1){
    return true;
  }
  else {
 	 // var found = n.textContent;
 	 // if (found == undefined)
  	//   found = n.innerText;
  	throw "Text '" + validator +
        "' was not found in the provided node.  Found instead: " + inner;
	}
};

//Assert that a specified node exists
windmill.controller.asserts.assertNode = function (paramObject) {
  var element = lookupNode(paramObject, false);
};

//Assert that a form element contains the expected value
windmill.controller.asserts.assertValue = function (paramObject) {
  //need to start moving test to use text instead of validator, its dumb
  var n = lookupNode(paramObject, false);
  var validator = paramObject.validator;

  if (n.value == undefined)
    throw "Element doesn't have a value";

  if (n.value != validator){
    throw "Found value \""+ n.value + "\" is not equal to \""+ validator+"\"";
  }
};

windmill.controller.asserts.assertValueIn = function (paramObject) {
  //need to start moving test to use text instead of validator, its dumb
  var n = lookupNode(paramObject, false);
  var validator = paramObject.validator;

  if (n.value == undefined)
    throw "Element doesn't have a value";

  if (n.value.indexOf(validator) == -1){
    throw "Found value \""+ n.value + "\" is not equal to \""+ validator+"\"";
  }
};

//Assert that a provided value is selected in a select element
windmill.controller.asserts.assertJS = function (paramObject) {
  var js = paramObject.js;
  var result = windmill.testWin().eval(js);
  if (result != true){
    throw "JavaScript did not return true."
  }
};

//Assert that a provided value is selected in a select element
windmill.controller.asserts.assertIDEJS = function (paramObject) {
  var js = paramObject.js;
  var result = eval(js);
  if (result != true){
    throw "JavaScript did not return true."
  }
};

//Asserting javascript with an element object available
windmill.controller.asserts.assertElemJS = function (paramObject) {
  var element = lookupNode(paramObject, false);
  var js = paramObject.js;
  var result = eval(js);
  if (result != true){
    throw "JavaScript did not return true."
  }
};

//Assert that a provided value is selected in a select element
windmill.controller.asserts.assertSelected = function (paramObject) {
  var n = lookupNode(paramObject, false);
  var validator = paramObject.validator;

  if ((n.options[n.selectedIndex].value != validator) && (n.options[n.selectedIndex].innerHTML != validator)){
    throw "Not selected, "+n.options[n.selectedIndex].value+" is not equal to " + validator;
  }
};

//Assert that a provided checkbox is checked
windmill.controller.asserts.assertChecked = function (paramObject) {
  var n = lookupNode(paramObject, false);

  if (!n.checked){
    throw "Checked property not true";
  }
};

// Assert that a an element's property is a particular value
windmill.controller.asserts.assertProperty = function (paramObject) {
  var element = lookupNode(paramObject, false);
  var vArray = paramObject.validator.split('|');
  if (vArray.length != 2)
    throw "Invalid validator '" + paramObject.validator + "'.  Use '|' to separate key from value.";

  var expected = "Expected property '" + vArray[0] + "' to have value '" + vArray[1] + "'. ";

  var value = eval ('element.' + vArray[0]+';');
  if (value == undefined)
    throw expected + "No '" + vArray[0] + "' property found.";

  if (value.indexOf(vArray[1]) != -1){
    return true;
  }
  if (String(value) == String(vArray[1])) {
    return true;
  }
  throw expected + "Found value '" + value + "' instead."
};

// Assert that a specified image has actually loaded
// The Safari workaround results in additional requests
// for broken images (in Safari only) but works reliably
windmill.controller.asserts.assertImageLoaded = function (paramObject) {
  var img = lookupNode(paramObject, false);
  if (!img || img.tagName != 'IMG') {
    throw "The node was not an image."
  }
  var comp = img.complete;
  var ret = null; // Return value

  // Workaround for Safari -- it only supports the
  // complete attrib on script-created images
  if (typeof comp == 'undefined') {
    test = new Image();
    // If the original image was successfully loaded,
    // src for new one should be pulled from cache
    test.src = img.src;
    comp = test.complete;
  }

  // Check the complete attrib. Note the strict
  // equality check -- we don't want undefined, null, etc.
  // --------------------------
  // False -- Img failed to load in IE/Safari, or is
  // still trying to load in FF
  if (comp === false) {
    throw "Image complete attrib false."
  }
  // True, but image has no size -- image failed to
  // load in FF
  else if (comp === true && img.naturalWidth == 0) {
    throw "Image has no size, failure to load."
  }
  // Otherwise all we can do is assume everything's
  // hunky-dory
  else {
    ret = true;
  }
  return ret;
};

windmill.controller.asserts._AssertFactory = new function () {
  var _this = this;
  function validateArgs(count, args) {
    if (!(args.length == count ||
	  (args.length == count + 1 && typeof(args[0]) == 'string') )) {
      throw('Incorrect arguments passed to assert function');
    }
  }
  function createErrMsg(msg, arr) {
    var str = msg;
    for (var i = 0; i < arr.length; i++) {
      //When calling jum functions arr is an array with a null entry
      if (arr[i] != null){
        var val = arr[i];
        var display = '<' + val.toString().replace(/\n/g, '') +
          '> (' + getTypeDetails(val) + ')';
        str = str.replace('$' + (i + 1).toString(), display);
      }
    }
    return str;
  }
  function getTypeDetails(val) {
    var r = typeof val;
    try {
      if (r == 'object' || r == 'function') {
        var m = val.constructor.toString().match(/function\s*([^( ]+)\(/);
						 if (m) { r = m[1]; }
						 else { r = 'Unknown Data Type' }
						 }
      }
      finally {
        r = r.substr(0, 1).toUpperCase() + r.substr(1);
        return r;
      }
    }
    this.createAssert = function (meth) {
      return function () {
      var args = Array.prototype.slice.call(arguments);
      args.unshift(meth);
      return _this.doAssert.apply(_this, args);
      }
    }
    this.doAssert = function () {
      // Convert arguments to real Array
      var args = Array.prototype.slice.call(arguments);
      // The actual assert method, e.g, 'equals'
      var meth = args.shift();
      // The assert object
      var asrt = windmill.controller.asserts.assertRegistry[meth];
      // The assert expresion
      var expr = asrt.expr;
      // Validate the args passed
      var valid = validateArgs(expr.length, args);
      // Pull off additional comment which may be first arg
      var comment = args.length > expr.length ?
        args.shift() : null;
      // Run the assert
      var res = expr.apply(window, args);
      if (res) {
	      return true;
      }
      else {
        var message = meth + ' -- ' +
        createErrMsg(asrt.errMsg, args);

	      throw new windmill.controller.asserts._WindmillAssertException(comment, message);
      }
    };
  };

// Create all the assert methods on windmill.controller.asserts
// Using the items in the assertRegistry
for (var meth in windmill.controller.asserts.assertRegistry) {
  windmill.controller.asserts[meth] = windmill.controller.asserts._AssertFactory.createAssert(meth);
  windmill.controller.asserts[meth].jsUnitAssert = true;
}

windmill.controller.asserts._WindmillAssertException = function (comment, message) {
  this.comment = comment;
  this.message = message;
};

<|endoftext|>


<commit_before>/**
 * # ServerChannel
 *
 * Copyright(c) 2012 Stefano Balietti
 * MIT Licensed
 *
 * Wrapper class for PlayerServer, AdminServer
 *
 * ---
 *
 */

// ## Global scope

module.exports = ServerChannel;

var AdminServer = require('./servers/AdminServer'),
	PlayerServer = require('./servers/PlayerServer'),
	GameServer = require('./GameServer'),
	Logger = require('./Logger');

//console.log(JSUS)

var J = require('JSUS').JSUS;

/**
 * ## ServerChannel Constructor
 *
 * Creates an instance of ServerChannel
 *
 * @param {object} options Configuration object
 * @param {object} sio The socket.io server
 *
 */
function ServerChannel (sio, options) {
	this.sio = sio;
	this.options = J.clone(options);

	this.name = options.name;

	this.admin = null;
	this.player = null;

	this.adminGames = {};

	this.subChannels = {};
	this.maxSubChannels = ('undefined' === typeof options.maxSubChannels) ? -1 : options.maxSubChannels;

	this.sysLogger = Logger.get('channel', {name: this.name});

	this.createServers();
}

// ServerChannel methods

/**
 * ### ServerChannel.createServers
 *
 * Creates the AdminServer and the PlayerServer
 *
 * Mixes in default options and user-defined options specified in
 * the constructor
 *
 */
ServerChannel.prototype.createServers = function() {

// AdminServer
	var adminOptions = {
	 	sio: this.sio,
	 	endpoint: this.options.admin,
	 	channel: this.name,
	 	name: 'admin',
	 	user_options: this.options
	};

	this.admin = new AdminServer(adminOptions);

// PlayerServer
	var playerOptions = {
	   sio: this.sio,
	   endpoint: this.options.player,
	   channel: this.name,
	   name: 'player',
	   user_options: this.options
	};

	this.player = new PlayerServer(playerOptions);

// The two servers are aware of each other
	this.admin.setPartner(this.player);
	this.player.setPartner(this.admin);
};

/**
 * ### ServerChannel.listen
 *
 * Puts the AdminServer and PlayerServer on listen mode
 *
 * @return {Boolean} TRUE, if execution is successful
 *
 */
ServerChannel.prototype.listen = function() {
	try {
		this.admin.listen();
		this.player.listen();
		return true;
	}
	catch(e) {
		this.sysLogger.log(e, 'error');
		return false;
	}
};

/**
 * ### ServerChannel.startGame
 *
 * Initializes and start a new game logic on the admin server
 *
 * @param game {string} The path to the game logic
 * @param options {object} Optional. A configuration object to pass to node.setup
 *
 * @see node.Game
 * @see node.setup
 */
ServerChannel.prototype.startGame = function(pathToGame, options) {
	options = options || {};

	var node = require('nodegame-client');
	var duplicateCounter = 1, game, gamename;

//	try {
		node.setup('nodegame', options);
		node.socket.setSocketType('SocketDirect', {
			socket: this.admin.socket_manager.sockets.direct
		});
		node.connect();

		game = require(pathToGame)(node, this);
		node.play(new game());

		gamename = J.uniqueKey(this.adminGames, options.name || game.name);

		this.adminGames[gamename] = node;

		return node;
//	}
//	catch(e) {
		this.sysLogger.log(e, 'error');
		return false;
//	}
};

ServerChannel.prototype.createSubChannel = function(options) {

	if (this.maxSubChannels !== -1 && J.size(this.subChannels) >= this.maxSubChannels) {
		var maxSubChannelsErr = 'Max number of subchannel for ' + this.name + ' reached: ' + this.maxSubChannels;
			maxSubChannelsErr += ' . Cannot create another subchannel.';
		this.sysLogger.log(maxSubChannelsErr, 'error');
		return false;
	}

	options = options || {};

	var name = J.uniqueKey(this.subChannels, options.name);

	options.name = name;
	options.admin = options.admin || options.name + '/admin';
	options.player = options.player || options.name + '/player';

	this.subChannels[name] = new ServerChannel(this.sio, options);
	return this.subChannels[name];
};

ServerChannel.prototype.destroySubChannel = function (name, options) {
	if (!name) return;

	if (!this.subChannels[name]) {
		var destroyChannelErr = 'Unexisting subchannel in channel ' + this.name + '. Cannot destroy: ' + name;
		this.sysLogger.log(destroyChannelErr, 'error');
		return false;
	}

	options = options || {};
	// TODO: how to propertly delete a subchannel? redirect, notify players?
	delete this.subChannels[name];
	return true;
};<commit_msg>validating input for ServerChannel.startGame<commit_after>/**
 * # ServerChannel
 *
 * Copyright(c) 2012 Stefano Balietti
 * MIT Licensed
 *
 * Wrapper class for PlayerServer, AdminServer
 *
 * ---
 *
 */

// ## Global scope

module.exports = ServerChannel;

var AdminServer = require('./servers/AdminServer'),
	PlayerServer = require('./servers/PlayerServer'),
	GameServer = require('./GameServer'),
	Logger = require('./Logger');

//console.log(JSUS)

var J = require('JSUS').JSUS;

/**
 * ## ServerChannel Constructor
 *
 * Creates an instance of ServerChannel
 *
 * @param {object} options Configuration object
 * @param {object} sio The socket.io server
 *
 */
function ServerChannel (sio, options) {
	this.sio = sio;
	this.options = J.clone(options);

	this.name = options.name;

	this.admin = null;
	this.player = null;

	this.adminGames = {};

	this.subChannels = {};
	this.maxSubChannels = ('undefined' === typeof options.maxSubChannels) ? -1 : options.maxSubChannels;

	this.sysLogger = Logger.get('channel', {name: this.name});

	this.createServers();
}

// ServerChannel methods

/**
 * ### ServerChannel.createServers
 *
 * Creates the AdminServer and the PlayerServer
 *
 * Mixes in default options and user-defined options specified in
 * the constructor
 *
 */
ServerChannel.prototype.createServers = function() {

// AdminServer
	var adminOptions = {
	 	sio: this.sio,
	 	endpoint: this.options.admin,
	 	channel: this.name,
	 	name: 'admin',
	 	user_options: this.options
	};

	this.admin = new AdminServer(adminOptions);

// PlayerServer
	var playerOptions = {
	   sio: this.sio,
	   endpoint: this.options.player,
	   channel: this.name,
	   name: 'player',
	   user_options: this.options
	};

	this.player = new PlayerServer(playerOptions);

// The two servers are aware of each other
	this.admin.setPartner(this.player);
	this.player.setPartner(this.admin);
};

/**
 * ### ServerChannel.listen
 *
 * Puts the AdminServer and PlayerServer on listen mode
 *
 * @return {Boolean} TRUE, if execution is successful
 *
 */
ServerChannel.prototype.listen = function() {
	try {
		this.admin.listen();
		this.player.listen();
		return true;
	}
	catch(e) {
		this.sysLogger.log(e, 'error');
		return false;
	}
};

/**
 * ### ServerChannel.startGame
 *
 * Initializes and start a new game logic on the admin server
 *
 * @param pathToGame {string} The path to the game logic
 * @param options {object} Optional. A configuration object to pass to node.setup
 *
 * @see node.Game
 * @see node.setup
 */
ServerChannel.prototype.startGame = function(pathToGame, options) {
	var errString;
	if (!pathToGame) {
		rrString = 'Cannot start an undefined game.';
		this.sysLogger.log(errString, 'error');
		return false;
	}
	var typeofPathToGame = typeof pathToGame;
	if ('string' !== typeofPathToGame) {
		errString = 'ServerChannel.startGame requires a string containing the path to the game as first parameter';
		errString += '. Found: ' + typeofPathToGame;
		this.sysLogger.log(errString, 'error');
		return false;
	}

	if (!J.existsSync(pathToGame)) {
		errString = PathToGame + ' does not exists or is it not readable.';
		this.sysLogger.log(errString, 'error');
		return false;
	}

	options = options || {};

	var node = require('nodegame-client');
	var duplicateCounter = 1, game, gamename;

//	try {
		node.setup('nodegame', options);
		node.socket.setSocketType('SocketDirect', {
			socket: this.admin.socket_manager.sockets.direct
		});
		node.connect();

		game = require(pathToGame)(node, this);
		node.play(new game());

		gamename = J.uniqueKey(this.adminGames, options.name || game.name);

		this.adminGames[gamename] = node;

		return node;
//	}
//	catch(e) {
		this.sysLogger.log(e, 'error');
		return false;
//	}
};

ServerChannel.prototype.createSubChannel = function(options) {

	if (this.maxSubChannels !== -1 && J.size(this.subChannels) >= this.maxSubChannels) {
		var maxSubChannelsErr = 'Max number of subchannel for ' + this.name + ' reached: ' + this.maxSubChannels;
			maxSubChannelsErr += ' . Cannot create another subchannel.';
		this.sysLogger.log(maxSubChannelsErr, 'error');
		return false;
	}

	options = options || {};

	var name = J.uniqueKey(this.subChannels, options.name);

	options.name = name;
	options.admin = options.admin || options.name + '/admin';
	options.player = options.player || options.name + '/player';

	this.subChannels[name] = new ServerChannel(this.sio, options);
	return this.subChannels[name];
};

ServerChannel.prototype.destroySubChannel = function (name, options) {
	if (!name) return;

	if (!this.subChannels[name]) {
		var destroyChannelErr = 'Unexisting subchannel in channel ' + this.name + '. Cannot destroy: ' + name;
		this.sysLogger.log(destroyChannelErr, 'error');
		return false;
	}

	options = options || {};
	// TODO: how to propertly delete a subchannel? redirect, notify players?
	delete this.subChannels[name];
	return true;
};<|endoftext|>


<commit_before>//{namespace name=backend/connect/view/main}

//{block name="backend/connect/view/import/remote_products"}
Ext.define('Shopware.apps.Connect.view.import.RemoteProducts', {
    extend: 'Ext.grid.Panel',
    alias: 'widget.connect-products',
    store: 'import.RemoteProducts',

    border: false,

    selModel: {
        selType: 'checkboxmodel',
        mode: 'MULTI'
    },

    viewConfig: {
        plugins: {
            ptype: 'gridviewdragdrop',
            appendOnly: true,
            dragGroup: 'local',
            dropGroup: 'remote'
        }
    },

    initComponent: function() {
        var me = this;

        Ext.applyIf(me, {
            height: 200,
            width: 450,

            dockedItems: [
                me.getPagingToolbar()

            ],
            columns: me.getColumns()
        });

        me.callParent(arguments);

    },

    getColumns: function() {
        return [
            {
                header: 'Aritkel Nr.',
                dataIndex: 'Detail_number',
                flex: 1
            }, {
                header: 'Name',
                dataIndex: 'Article_name',
                flex: 4
            }, {
                header: 'Hersteller',
                dataIndex: 'Supplier_name',
                flex: 3
            }, {
                header: 'Preis (brutto)',
                dataIndex: 'Price_basePrice',
                flex: 3
            }, {
                header: 'Steuersatz',
                dataIndex: 'Tax_name',
                flex: 1
            }
        ];
    },

    ///**
    // * Creates a paging toolbar with additional page size selector
    // *
    // * @returns Array
    // */
    getPagingToolbar: function() {
        var me = this;
        var pageSize = Ext.create('Ext.form.field.ComboBox', {
            cls: Ext.baseCSSPrefix + 'page-size',
            queryMode: 'local',
            width: 60,
            listeners: {
                scope: me,
                select: function(combo, records) {
                    var record = records[0],
                        me = this;

                    me.store.pageSize = record.get('value');
                    me.store.loadPage(1);
                }
            },
            store: Ext.create('Ext.data.Store', {
                fields: [ 'value' ],
                data: [
                    { value: '10' },
                    { value: '20' },
                    { value: '40' },
                    { value: '60' },
                    { value: '80' },
                    { value: '100' },
                    { value: '250' },
                    { value: '500' }
                ]
            }),
            displayField: 'value',
            valueField: 'value',
            editable: false,
            emptyText: '10'
        });
        pageSize.setValue(me.store.pageSize);

        var pagingBar = Ext.create('Ext.toolbar.Paging', {
            store: me.store,
            dock:'bottom',
            displayInfo:true,
            doRefresh : function(){
                var toolbar = this,
                    current = toolbar.store.currentPage;

                if (toolbar.fireEvent('beforechange', toolbar, current) !== false) {
                    toolbar.store.loadPage(current);
                }

                me.fireEvent('reloadRemoteCategories');
            }
        });
        pagingBar.insert(pagingBar.items.length - 2, [ { xtype: 'tbspacer', width: 6 }, pageSize ]);

        return pagingBar;
    }
});
//{/block}<commit_msg>[CON-3127] fix number format<commit_after>//{namespace name=backend/connect/view/main}

//{block name="backend/connect/view/import/remote_products"}
Ext.define('Shopware.apps.Connect.view.import.RemoteProducts', {
    extend: 'Ext.grid.Panel',
    alias: 'widget.connect-products',
    store: 'import.RemoteProducts',

    border: false,

    selModel: {
        selType: 'checkboxmodel',
        mode: 'MULTI'
    },

    viewConfig: {
        plugins: {
            ptype: 'gridviewdragdrop',
            appendOnly: true,
            dragGroup: 'local',
            dropGroup: 'remote'
        }
    },

    initComponent: function() {
        var me = this;

        Ext.applyIf(me, {
            height: 200,
            width: 450,

            dockedItems: [
                me.getPagingToolbar()

            ],
            columns: me.getColumns()
        });

        me.callParent(arguments);

    },

    getColumns: function() {
        return [
            {
                header: 'Aritkel Nr.',
                dataIndex: 'Detail_number',
                flex: 1
            }, {
                header: 'Name',
                dataIndex: 'Article_name',
                flex: 4
            }, {
                header: 'Hersteller',
                dataIndex: 'Supplier_name',
                flex: 3
            }, {
                header: 'Preis (brutto)',
                dataIndex: 'Price_basePrice',
                xtype: 'numbercolumn',
                format: '0.00',
                flex: 3
            }, {
                header: 'Steuersatz',
                dataIndex: 'Tax_name',
                flex: 1
            }
        ];
    },

    ///**
    // * Creates a paging toolbar with additional page size selector
    // *
    // * @returns Array
    // */
    getPagingToolbar: function() {
        var me = this;
        var pageSize = Ext.create('Ext.form.field.ComboBox', {
            cls: Ext.baseCSSPrefix + 'page-size',
            queryMode: 'local',
            width: 60,
            listeners: {
                scope: me,
                select: function(combo, records) {
                    var record = records[0],
                        me = this;

                    me.store.pageSize = record.get('value');
                    me.store.loadPage(1);
                }
            },
            store: Ext.create('Ext.data.Store', {
                fields: [ 'value' ],
                data: [
                    { value: '10' },
                    { value: '20' },
                    { value: '40' },
                    { value: '60' },
                    { value: '80' },
                    { value: '100' },
                    { value: '250' },
                    { value: '500' }
                ]
            }),
            displayField: 'value',
            valueField: 'value',
            editable: false,
            emptyText: '10'
        });
        pageSize.setValue(me.store.pageSize);

        var pagingBar = Ext.create('Ext.toolbar.Paging', {
            store: me.store,
            dock:'bottom',
            displayInfo:true,
            doRefresh : function(){
                var toolbar = this,
                    current = toolbar.store.currentPage;

                if (toolbar.fireEvent('beforechange', toolbar, current) !== false) {
                    toolbar.store.loadPage(current);
                }

                me.fireEvent('reloadRemoteCategories');
            }
        });
        pagingBar.insert(pagingBar.items.length - 2, [ { xtype: 'tbspacer', width: 6 }, pageSize ]);

        return pagingBar;
    }
});
//{/block}<|endoftext|>


<commit_before>$(function() {
    var data = textFromURL("ar1481484242-3993659735.hlt", $("#gameReplay"), function(data) {
        console.log(data)
        if(data != null) {
            showGame(data, $("#gameReplay"), null, 500, true, true);
        }
    });
})
<commit_msg>Put a random bot replay on intro halite<commit_after>$(function() {
    var data = textFromURL("ar1482413174-917235674.hlt", $("#gameReplay"), function(data) {
        console.log(data)
        if(data != null) {
            showGame(data, $("#gameReplay"), null, 500, true, true);
        }
    });
})
<|endoftext|>


<commit_before>/* global it */
/* global describe */

var should = require('should');
var sandboxPath = 'test/sandbox';

describe('Matching Reuters: ', function () {
  describe('Matches stuff in reuters-000.json', function () {
    it('should search on all fields and get results', function (done) {
      var si = require('../../')({indexPath: sandboxPath + '/si-reuters',
                                  logLevel: 'warn'});
      var str = 'lon';
      si.match(str, function (err, matches) {
        should.exist(matches);
        (err === null).should.be.exactly(true);
        matches.length.should.be.exactly(6);
        matches[0].should.be.exactly('long');
        matches[1].should.be.exactly('london');
        matches[2].should.be.exactly('longer');
        matches[3].should.be.exactly('longrange');
        matches[4].should.be.exactly('longstanding');
        matches[5].should.be.exactly('longtime');
        si.close(function (err) {
          if (err) false.should.eql(true);done();
        });
      });
    }),
    it('handles match strings that are below threshold', function (done) {
      var si = require('../../')({indexPath: sandboxPath + '/si-reuters',
                                  logLevel: 'warn'});
      var str = 'lo';
      si.match(str, function (err, matches) {
        should.exist(matches);
        matches.length.should.be.exactly(0);
        (err instanceof Error).should.be.exactly(true);
        err.toString().should.be.exactly('Error: string below threshold length (3)');
        si.close(function (err) {
          if (err) false.should.eql(true);done();
        });
      });
    });
  });
});
<commit_msg>Removes the test case in reuters-tests for matcher string length validation. As it's an exact duplication of the one in general-tests.<commit_after>/* global it */
/* global describe */

var should = require('should');
var sandboxPath = 'test/sandbox';

describe('Matching Reuters: ', function () {
  describe('Matches stuff in reuters-000.json', function () {
    it('should search on all fields and get results', function (done) {
      var si = require('../../')({indexPath: sandboxPath + '/si-reuters',
                                  logLevel: 'warn'});
      var str = 'lon';
      si.match(str, function (err, matches) {
        should.exist(matches);
        (err === null).should.be.exactly(true);
        matches.length.should.be.exactly(6);
        matches[0].should.be.exactly('long');
        matches[1].should.be.exactly('london');
        matches[2].should.be.exactly('longer');
        matches[3].should.be.exactly('longrange');
        matches[4].should.be.exactly('longstanding');
        matches[5].should.be.exactly('longtime');
        si.close(function (err) {
          if (err) false.should.eql(true);done();
        });
      });
    })
  });
});
<|endoftext|>