text:
package org.itas.core.dbpool.hooks;

import java.util.Map;

import org.itas.core.dbpool.ConnectionHandle;

/**
 * Interface to the hooking mechanism of a connection lifecycle. Applications
 * will generally want to extend {@link com.jolbox.bonecp.hooks.AbstractConnectionHook} instead to provide a
 * default implementation. Applications might also want to make use of
 * connection.setDebugHandle(...) to keep track of additional information on
 * each connection.
 *
 *  Since the class is eventually loaded via reflection, the implementation must
 *  provide a public, no argument constructor.
 *
 *  Warning: Be careful to make sure that the hook methods are re-entrant and
 *  thread-safe; do not rely on external state without appropriate locking, use appropriate
 *  synchronization!
 *
 * @author wallacew
 *
 */
public interface ConnectionHook {

	/** Called upon getting a new connection from the JDBC driver (and prior to
	 * inserting into the pool). You may call connection.getInternalConnection() to obtain
	 * a handle to the actual (unwrapped) connection obtained from the driver.
	 * @param connection Handle to the new connection
	 */
	void onAcquire(ConnectionHandle connection);

	/**  Called when the connection is about to be returned to the pool.
	 * @param connection being returned to pool.
	 */
	void onCheckIn(ConnectionHandle connection);

	/**  Called when the connection is extracted from the pool and about to be
	 * given to the application.
	 * @param connection about to given to the app.
	 */
	void onCheckOut(ConnectionHandle connection);

	/** Called when the connection is about to be completely removed from the
	 * pool. Careful with this hook; the connection might be marked as being
	 * broken. Use connection.isPossiblyBroken() to determine if the connection
	 * has triggered an exception at some point.
	 * @param connection
	 */
	void onDestroy(ConnectionHandle connection);

	/** Called on attempting (and failing) to acquire a connection. Note that implementing this means that acquireRetry/delay logic
	 * will be overridden by this code.
	 * @param t Exception that occurred.
	 * @param acquireConfig handle containing retry delay, retry attempts etc.
	 * @return Return true to attempt the connection again.
	 */
	boolean onAcquireFail(Throwable t, AcquireFailConfig acquireConfig);

	/** Called when a query execute time limit has been set and an executing query took longer
	 * to than the limit to return control to the application.
	 * @param sql SQL statement that was used.
	 * @param logParams Parameters used in this statement.
	 */
	void onQueryExecuteTimeLimitExceeded(String sql, Map<Object, Object> logParams);

	/** Called whenever an exception on a connection occurs. This exception may be a connection failure, a DB failure or a
	 * non-fatal logical failure (eg Duplicate key exception).
	 *
	 *
SQLSTATE Value
	 *
Value	Meaning
	 *
08001	The application requester is unable to establish the connection.
	 *
08002	The connection already exists.
	 *
08003	The connection does not exist.
	 *
08004	The application server rejected establishment of the connection.
	 *
08007	Transaction resolution unknown.
	 *
08502	The CONNECT statement issued by an application process running with a SYNCPOINT of TWOPHASE has failed, because no transaction manager is available.
	 *
08504	An error was encountered while processing the specified path rename configuration file.
	 *
SQL Failure codes 08001 & 08007 indicate that the database is broken/died (and thus all remaining connections are killed off).
	 *
Anything else will be taken as the connection (not the db) being broken.
	 *

	 *
	 * Note: You may use pool.isConnectionHandleAlive(connection) to verify if the connection is in a usable state again.
	 * Note 2: As in all interceptor hooks, this method may be called concurrently so any implementation must be thread-safe.
	 *
	 * @param connection The handle that triggered this error
	 * @param state the SQLState error code.
	 * @param t Exception that caused this failure.
	 * @return Returning true means: when you eventually close off this connection, test to see if the connection is still
	 * alive and discard it if not (this is the normal behaviour). Returning false pretends that the connection is still ok
	 * when the connection is closed (your application will still receive the original exception that was thrown).
	 */
	boolean onConnectionException(ConnectionHandle connection, String state, Throwable t);
}


text:
from __future__ import absolute_import, division, print_function
__metaclass__ = type

ANSIBLE_METADATA = {'metadata_version': '1.1',
                    'status': ['preview'],
                    'supported_by': 'community'}

DOCUMENTATION = '''
---
module: docker_image

short_description: Manage docker images.

version_added: "1.5"

description:
     - Build, load or pull an image, making the image available for creating containers. Also supports tagging an
       image into a repository and archiving an image to a .tar file.

options:
  archive_path:
    description:
      - Use with state C(present) to archive an image to a .tar file.
    required: false
    version_added: "2.1"
  load_path:
    description:
      - Use with state C(present) to load an image from a .tar file.
    required: false
    version_added: "2.2"
  dockerfile:
    description:
      - Use with state C(present) to provide an alternate name for the Dockerfile to use when building an image.
    default: Dockerfile
    required: false
    version_added: "2.0"
  force:
    description:
      - Use with state I(absent) to un-tag and remove all images matching the specified name. Use with state
        C(present) to build, load or pull an image when the image already exists.
    default: false
    required: false
    version_added: "2.1"
    type: bool
  http_timeout:
    description:
      - Timeout for HTTP requests during the image build operation. Provide a positive integer value for the number of
        seconds.
    required: false
    version_added: "2.1"
  name:
    description:
      - "Image name. Name format will be one of: name, repository/name, registry_server:port/name.
        When pushing or pulling an image the name can optionally include the tag by appending ':tag_name'."
    required: true
  path:
    description:
      - Use with state 'present' to build an image. Will be the path to a directory containing the context and
        Dockerfile for building an image.
    aliases:
      - build_path
    required: false
  pull:
    description:
      - When building an image downloads any updates to the FROM image in Dockerfile.
    default: true
    required: false
    version_added: "2.1"
    type: bool
  push:
    description:
      - Push the image to the registry. Specify the registry as part of the I(name) or I(repository) parameter.
    default: false
    required: false
    version_added: "2.2"
    type: bool
  rm:
    description:
      - Remove intermediate containers after build.
    default: true
    required: false
    version_added: "2.1"
    type: bool
  nocache:
    description:
      - Do not use cache when building an image.
    default: false
    required: false
    type: bool
  repository:
    description:
      - Full path to a repository. Use with state C(present) to tag the image into the repository. Expects
        format I(repository:tag). If no tag is provided, will use the value of the C(tag) parameter or I(latest).
    required: false
    version_added: "2.1"
  state:
    description:
      - Make assertions about the state of an image.
      - When C(absent) an image will be removed. Use the force option to un-tag and remove all images
        matching the provided name.
      - When C(present) check if an image exists using the provided name and tag. If the image is not found or the
        force option is used, the image will either be pulled, built or loaded. By default the image will be pulled
        from Docker Hub. To build the image, provide a path value set to a directory containing a context and
        Dockerfile. To load an image, specify load_path to provide a path to an archive file. To tag an image to a
        repository, provide a repository path. If the name contains a repository path, it will be pushed.
      - "NOTE: C(build) is DEPRECATED and will be removed in release 2.3. Specifying C(build) will behave the
         same as C(present)."
    required: false
    default: present
    choices:
      - absent
      - present
      - build
  tag:
    description:
      - Used to select an image when pulling. Will be added to the image when pushing, tagging or building. Defaults to
        I(latest).
      - If C(name) parameter format is I(name:tag), then tag value from C(name) will take precedence.
    default: latest
    required: false
  buildargs:
    description:
      - Provide a dictionary of C(key:value) build arguments that map to Dockerfile ARG directive.
      - Docker expects the value to be a string. For convenience any non-string values will be converted to strings.
      - Requires Docker API >= 1.21 and docker-py >= 1.7.0.
    required: false
    version_added: "2.2"
  container_limits:
    description:
      - A dictionary of limits applied to each container created by the build process.
    required: false
    version_added: "2.1"
    suboptions:
      memory:
        description:
          - Set memory limit for build.
      memswap:
        description:
          - Total memory (memory + swap), -1 to disable swap.
      cpushares:
        description:
          - CPU shares (relative weight).
      cpusetcpus:
        description:
          - CPUs in which to allow execution, e.g., "0-3", "0,1".
  use_tls:
    description:
      - "DEPRECATED. Whether to use tls to connect to the docker server. Set to C(no) when TLS will not be used. Set to
        C(encrypt) to use TLS. And set to C(verify) to use TLS and verify that the server's certificate is valid for the
        server. NOTE: If you specify this option, it will set the value of the tls or tls_verify parameters."
    choices:
      - no
      - encrypt
      - verify
    default: no
    required: false
    version_added: "2.0"

extends_documentation_fragment:
    - docker

requirements:
  - "python >= 2.6"
  - "docker-py >= 1.7.0"
  - "Docker API >= 1.20"

author:
  - Pavel Antonov (@softzilla)
  - Chris Houseknecht (@chouseknecht)
  - James Tanner (@jctanner)

'''

EXAMPLES = '''

- name: pull an image
  docker_image:
    name: pacur/centos-7

- name: Tag and push to docker hub
  docker_image:
    name: pacur/centos-7
    repository: dcoppenhagan/myimage
    tag: 7.0
    push: yes

- name: Tag and push to local registry
  docker_image:
     name: centos
     repository: localhost:5000/centos
     tag: 7
     push: yes

- name: Remove image
  docker_image:
    state: absent
    name: registry.ansible.com/chouseknecht/sinatra
    tag: v1

- name: Build an image and push it to a private repo
  docker_image:
    path: ./sinatra
    name: registry.ansible.com/chouseknecht/sinatra
    tag: v1
    push: yes

- name: Archive image
  docker_image:
    name: registry.ansible.com/chouseknecht/sinatra
    tag: v1
    archive_path: my_sinatra.tar

- name: Load image from archive and push to a private registry
  docker_image:
    name: localhost:5000/myimages/sinatra
    tag: v1
    push: yes
    load_path: my_sinatra.tar

- name: Build image and with buildargs
  docker_image:
     path: /path/to/build/dir
     name: myimage
     buildargs:
       log_volume: /var/log/myapp
       listen_port: 8080
'''

RETURN = '''
image:
    description: Image inspection results for the affected image.
    returned: success
    type: dict
    sample: {}
'''
import os
import re

from ansible.module_utils.docker_common import HAS_DOCKER_PY_2, AnsibleDockerClient, DockerBaseClass
from ansible.module_utils._text import to_native

try:
    if HAS_DOCKER_PY_2:
        from docker.auth import resolve_repository_name
    else:
        from docker.auth.auth import resolve_repository_name
    from docker.utils.utils import parse_repository_tag
except ImportError:
    # missing docker-py handled in docker_common
    pass

class ImageManager(DockerBaseClass):

    def __init__(self, client, results):

        super(ImageManager, self).__init__()

        self.client = client
        self.results = results
        parameters = self.client.module.params
        self.check_mode = self.client.check_mode

        self.archive_path = parameters.get('archive_path')
        self.container_limits = parameters.get('container_limits')
        self.dockerfile = parameters.get('dockerfile')
        self.force = parameters.get('force')
        self.load_path = parameters.get('load_path')
        self.name = parameters.get('name')
        self.nocache = parameters.get('nocache')
        self.path = parameters.get('path')
        self.pull = parameters.get('pull')
        self.repository = parameters.get('repository')
        self.rm = parameters.get('rm')
        self.state = parameters.get('state')
        self.tag = parameters.get('tag')
        self.http_timeout = parameters.get('http_timeout')
        self.push = parameters.get('push')
        self.buildargs = parameters.get('buildargs')

        # If name contains a tag, it takes precedence over tag parameter.
        repo, repo_tag = parse_repository_tag(self.name)
        if repo_tag:
            self.name = repo
            self.tag = repo_tag

        if self.state in ['present', 'build']:
            self.present()
        elif self.state == 'absent':
            self.absent()

    def fail(self, msg):
        self.client.fail(msg)

    def present(self):
        '''
        Handles state = 'present', which includes building, loading or pulling an image,
        depending on user provided parameters.

        :returns None
        '''
        image = self.client.find_image(name=self.name, tag=self.tag)

        if not image or self.force:
            if self.path:
                # Build the image
                if not os.path.isdir(self.path):
                    self.fail("Requested build path %s could not be found or you do not have access." % self.path)
                image_name = self.name
                if self.tag:
                    image_name = "%s:%s" % (self.name, self.tag)
                self.log("Building image %s" % image_name)
                self.results['actions'].append("Built image %s from %s" % (image_name, self.path))
                self.results['changed'] = True
                if not self.check_mode:
                    self.results['image'] = self.build_image()
            elif self.load_path:
                # Load the image from an archive
                if not os.path.isfile(self.load_path):
                    self.fail("Error loading image %s. Specified path %s does not exist." % (self.name,
                                                                                self.load_path))
                image_name = self.name
                if self.tag:
                    image_name = "%s:%s" % (self.name, self.tag)
                self.results['actions'].append("Loaded image %s from %s" % (image_name, self.load_path))
                self.results['changed'] = True
                if not self.check_mode:
                    self.results['image'] = self.load_image()
            else:
                # pull the image
                self.results['actions'].append('Pulled image %s:%s' % (self.name, self.tag))
                self.results['changed'] = True
                if not self.check_mode:
                    self.results['image'] = self.client.pull_image(self.name, tag=self.tag)
                    if image and image == self.results['image']:
                        self.results['changed'] = False

        if self.archive_path:
            self.archive_image(self.name, self.tag)

        if self.push and not self.repository:
            self.push_image(self.name, self.tag)
        elif self.repository:
            self.tag_image(self.name, self.tag, self.repository, force=self.force, push=self.push)

    def absent(self):
        '''
        Handles state = 'absent', which removes an image.

        :return None
        '''
        image = self.client.find_image(self.name, self.tag)
        if image:
            name = self.name
            if self.tag:
                name = "%s:%s" % (self.name, self.tag)
            if not self.check_mode:
                try:
                    self.client.remove_image(name, force=self.force)
                except Exception as exc:
                    self.fail("Error removing image %s - %s" % (name, str(exc)))

            self.results['changed'] = True
            self.results['actions'].append("Removed image %s" % (name))
            self.results['image']['state'] = 'Deleted'

    def archive_image(self, name, tag):
        '''
        Archive an image to a .tar file. Called when archive_path is passed.

        :param name - name of the image. Type: str
        :return None
        '''

        if not tag:
            tag = "latest"

        image = self.client.find_image(name=name, tag=tag)
        if not image:
            self.log("archive image: image %s:%s not found" % (name, tag))
            return

        image_name = "%s:%s" % (name, tag)
        self.results['actions'].append('Archived image %s to %s' % (image_name, self.archive_path))
        self.results['changed'] = True
        if not self.check_mode:
            self.log("Getting archive of image %s" % image_name)
            try:
                image = self.client.get_image(image_name)
            except Exception as exc:
                self.fail("Error getting image %s - %s" % (image_name, str(exc)))

            try:
                with open(self.archive_path, 'w') as fd:
                    for chunk in image.stream(2048, decode_content=False):
                        fd.write(chunk)
            except Exception as exc:
                self.fail("Error writing image archive %s - %s" % (self.archive_path, str(exc)))

        image = self.client.find_image(name=name, tag=tag)
        if image:
            self.results['image'] = image

    def push_image(self, name, tag=None):
        '''
        If the name of the image contains a repository path, then push the image.

        :param name Name of the image to push.
        :param tag Use a specific tag.
        :return: None
        '''

        repository = name
        if not tag:
            repository, tag = parse_repository_tag(name)
        registry, repo_name = resolve_repository_name(repository)

        self.log("push %s to %s/%s:%s" % (self.name, registry, repo_name, tag))

        if registry:
            self.results['actions'].append("Pushed image %s to %s/%s:%s" % (self.name, registry, repo_name, tag))
            self.results['changed'] = True
            if not self.check_mode:
                status = None
                try:
                    for line in self.client.push(repository, tag=tag, stream=True,  decode=True):
                        self.log(line, pretty_print=True)
                        if line.get('errorDetail'):
                            raise Exception(line['errorDetail']['message'])
                        status = line.get('status')
                except Exception as exc:
                    if re.search('unauthorized', str(exc)):
                        if re.search('authentication required', str(exc)):
                            self.fail("Error pushing image %s/%s:%s - %s. Try logging into %s first." %
                                      (registry, repo_name, tag, str(exc), registry))
                        else:
                            self.fail("Error pushing image %s/%s:%s - %s. Does the repository exist?" %
                                      (registry, repo_name, tag, str(exc)))
                    self.fail("Error pushing image %s: %s" % (repository, str(exc)))
                self.results['image'] = self.client.find_image(name=repository, tag=tag)
                if not self.results['image']:
                    self.results['image'] = dict()
                self.results['image']['push_status'] = status

    def tag_image(self, name, tag, repository, force=False, push=False):
        '''
        Tag an image into a repository.

        :param name: name of the image. required.
        :param tag: image tag.
        :param repository: path to the repository. required.
        :param force: bool. force tagging, even it image already exists with the repository path.
        :param push: bool. push the image once it's tagged.
        :return: None
        '''
        repo, repo_tag = parse_repository_tag(repository)
        if not repo_tag:
            repo_tag = "latest"
            if tag:
                repo_tag = tag
        image = self.client.find_image(name=repo, tag=repo_tag)
        found = 'found' if image else 'not found'
        self.log("image %s was %s" % (repo, found))

        if not image or force:
            self.log("tagging %s:%s to %s:%s" % (name, tag, repo, repo_tag))
            self.results['changed'] = True
            self.results['actions'].append("Tagged image %s:%s to %s:%s" % (name, tag, repo, repo_tag))
            if not self.check_mode:
                try:
                    # Finding the image does not always work, especially running a localhost registry. In those
                    # cases, if we don't set force=True, it errors.
                    image_name = name
                    if tag and not re.search(tag, name):
                        image_name = "%s:%s" % (name, tag)
                    tag_status = self.client.tag(image_name, repo, tag=repo_tag, force=True)
                    if not tag_status:
                        raise Exception("Tag operation failed.")
                except Exception as exc:
                    self.fail("Error: failed to tag image - %s" % str(exc))
                self.results['image'] = self.client.find_image(name=repo, tag=repo_tag)
                if push:
                    self.push_image(repo, repo_tag)

    def build_image(self):
        '''
        Build an image

        :return: image dict
        '''
        params = dict(
            path=self.path,
            tag=self.name,
            rm=self.rm,
            nocache=self.nocache,
            stream=True,
            timeout=self.http_timeout,
            pull=self.pull,
            forcerm=self.rm,
            dockerfile=self.dockerfile,
            decode=True
        )
        build_output = []
        if self.tag:
            params['tag'] = "%s:%s" % (self.name, self.tag)
        if self.container_limits:
            params['container_limits'] = self.container_limits
        if self.buildargs:
            for key, value in self.buildargs.items():
                self.buildargs[key] = to_native(value)
            params['buildargs'] = self.buildargs

        for line in self.client.build(**params):
            # line = json.loads(line)
            self.log(line, pretty_print=True)
            if "stream" in line:
                build_output.append(line["stream"])
            if line.get('error'):
                if line.get('errorDetail'):
                    errorDetail = line.get('errorDetail')
                    self.fail(
                        "Error building %s - code: %s, message: %s, logs: %s" % (
                            self.name,
                            errorDetail.get('code'),
                            errorDetail.get('message'),
                            build_output))
                else:
                    self.fail("Error building %s - message: %s, logs: %s" % (
                        self.name, line.get('error'), build_output))
        return self.client.find_image(name=self.name, tag=self.tag)

    def load_image(self):
        '''
        Load an image from a .tar archive

        :return: image dict
        '''
        try:
            self.log("Opening image %s" % self.load_path)
            image_tar = open(self.load_path, 'r')
        except Exception as exc:
            self.fail("Error opening image %s - %s" % (self.load_path, str(exc)))

        try:
            self.log("Loading image from %s" % self.load_path)
            self.client.load_image(image_tar)
        except Exception as exc:
            self.fail("Error loading image %s - %s" % (self.name, str(exc)))

        try:
            image_tar.close()
        except Exception as exc:
            self.fail("Error closing image %s - %s" % (self.name, str(exc)))

        return self.client.find_image(self.name, self.tag)

def main():
    argument_spec = dict(
        archive_path=dict(type='path'),
        container_limits=dict(type='dict'),
        dockerfile=dict(type='str'),
        force=dict(type='bool', default=False),
        http_timeout=dict(type='int'),
        load_path=dict(type='path'),
        name=dict(type='str', required=True),
        nocache=dict(type='bool', default=False),
        path=dict(type='path', aliases=['build_path']),
        pull=dict(type='bool', default=True),
        push=dict(type='bool', default=False),
        repository=dict(type='str'),
        rm=dict(type='bool', default=True),
        state=dict(type='str', choices=['absent', 'present', 'build'], default='present'),
        tag=dict(type='str', default='latest'),
        use_tls=dict(type='str', default='no', choices=['no', 'encrypt', 'verify']),
        buildargs=dict(type='dict', default=None),
    )

    client = AnsibleDockerClient(
        argument_spec=argument_spec,
        supports_check_mode=True,
    )

    results = dict(
        changed=False,
        actions=[],
        image={}
    )

    ImageManager(client, results)
    client.module.exit_json(**results)

if __name__ == '__main__':
    main()

redpajama_set_name: RedPajamaGithub


text:
package cmd

import (
	"strconv"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"github.com/vespa-engine/vespa/client/go/mock"
)

func TestQuery(t *testing.T) {
	assertQuery(t,
		"?timeout=10s&yql=select+from+sources+%2A+where+title+contains+%27foo%27",
		"select from sources * where title contains 'foo'")
}

func TestQueryVerbose(t *testing.T) {
	client := &mock.HTTPClient{}
	client.NextResponseString(200, "{\"query\":\"result\"}")

	cli, stdout, stderr := newTestCLI(t)
	cli.httpClient = client

	assert.Nil(t, cli.Run("query", "-v", "select from sources * where title contains 'foo'"))
	assert.Equal(t, "curl http://127.0.0.1:8080/search/\\?timeout=10s\\&yql=select+from+sources+%2A+where+title+contains+%27foo%27\n", stderr.String())
	assert.Equal(t, "{\n    \"query\": \"result\"\n}\n", stdout.String())
}

func TestQueryNonJsonResult(t *testing.T) {
	assertQuery(t,
		"?timeout=10s&yql=select+from+sources+%2A+where+title+contains+%27foo%27",
		"select from sources * where title contains 'foo'")
}

func TestQueryWithMultipleParameters(t *testing.T) {
	assertQuery(t,
		"?hits=5&timeout=20s&yql=select+from+sources+%2A+where+title+contains+%27foo%27+and+year+%3D+2000",
		"select from sources * where title contains 'foo' and year = 2000", "hits=5", "timeout=20s")
}

func TestQueryWithEquals(t *testing.T) {
	assertQuery(t,
		"?timeout=10s&yql=SELECT+from+sources+%2A+where+title+contains+%27foo%27+and+year+%3D+2000",
		"SELECT from sources * where title contains 'foo' and year = 2000")
}

func TestQuerySelect(t *testing.T) {
	assertQuery(t,
		"?hits=5&select=%7B%22select%22%3A%7B%22where%22%3A%7B%22contains%22%3A%5B%22title%22%2C%22a%22%5D%7D%7D%7D&timeout=10s",
		`select={"select":{"where":{"contains":["title","a"]}}}`, "hits=5")
}

func TestQueryWithExplicitYqlParameter(t *testing.T) {
	assertQuery(t,
		"?timeout=10s&yql=select+from+sources+%2A+where+title+contains+%27foo%27",
		"yql=select from sources * where title contains 'foo'")
}

func TestIllegalQuery(t *testing.T) {
	assertQueryError(t, 401, "query error message")
}

func TestServerError(t *testing.T) {
	assertQueryServiceError(t, 501, "server error message")
}

func assertQuery(t *testing.T, expectedQuery string, query ...string) {
	client := &mock.HTTPClient{}
	client.NextResponseString(200, "{\"query\":\"result\"}")
	cli, stdout, _ := newTestCLI(t)
	cli.httpClient = client

	args := []string{"query"}
	assert.Nil(t, cli.Run(append(args, query...)...))
	assert.Equal(t,
		"{\n    \"query\": \"result\"\n}\n",
		stdout.String(),
		"query output")
	queryURL, err := queryServiceURL(client)
	require.Nil(t, err)
	assert.Equal(t, queryURL+"/search/"+expectedQuery, client.LastRequest.URL.String())
}

func assertQueryError(t *testing.T, status int, errorMessage string) {
	client := &mock.HTTPClient{}
	client.NextResponseString(status, errorMessage)
	cli, _, stderr := newTestCLI(t)
	cli.httpClient = client
	assert.NotNil(t, cli.Run("query", "yql=select from sources * where title contains 'foo'"))
	assert.Equal(t,
		"Error: invalid query: Status "+strconv.Itoa(status)+"\n"+errorMessage+"\n",
		stderr.String(),
		"error output")
}

func assertQueryServiceError(t *testing.T, status int, errorMessage string) {
	client := &mock.HTTPClient{}
	client.NextResponseString(status, errorMessage)
	cli, _, stderr := newTestCLI(t)
	cli.httpClient = client
	assert.NotNil(t, cli.Run("query", "yql=select from sources * where title contains 'foo'"))
	assert.Equal(t,
		"Error: Status "+strconv.Itoa(status)+" from container at 127.0.0.1:8080\n"+errorMessage+"\n",
		stderr.String(),
		"error output")
}

func queryServiceURL(client *mock.HTTPClient) (string, error) {
	return "http://127.0.0.1:8080", nil
}


text:
<!DOCTYPE html>

    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">

    <meta name="twitter:site" content="@metroui">
    <meta name="twitter:creator" content="@pimenov_sergey">
    <meta name="twitter:card" content="summary">
    <meta name="twitter:title" content="Metro 4 Components Library">
    <meta name="twitter:description" content="Metro 4 is an open source toolkit for developing with HTML, CSS, and JS. Quickly prototype your ideas or build your entire app with responsive grid system, extensive prebuilt components, and powerful plugins built on jQuery.">
    <meta name="twitter:image" content="https://metroui.org.ua/images/m4-logo-social.png">

    <meta property="og:url" content="https://metroui.org.ua/v4/index.html">
    <meta property="og:title" content="Metro 4 Components Library">
    <meta property="og:description" content="Metro 4 is an open source toolkit for developing with HTML, CSS, and JS. Quickly prototype your ideas or build your entire app with responsive grid system, extensive prebuilt components, and powerful plugins built on jQuery.">
    <meta property="og:type" content="website">
    <meta property="og:image" content="https://metroui.org.ua/images/m4-logo-social.png">
    <meta property="og:image:secure_url" content="https://metroui.org.ua/images/m4-logo-social.png">
    <meta property="og:image:type" content="image/png">
    <meta property="og:image:width" content="968">
    <meta property="og:image:height" content="504">

    <meta name="author" content="Sergey Pimenov">
    <meta name="description" content="Use a time picker to let a user pick a single time value. The most popular HTML, CSS, and JS library in Metro style.">
    <meta name="keywords" content="HTML, CSS, JS, Metro, CSS3, Javascript, HTML5, UI, Library, Web, Development, Framework">

    <link rel="shortcut icon" href="favicon.ico" type="image/x-icon">
    <link rel="icon" href="favicon.ico" type="image/x-icon">

    <link href="metro/css/metro-all.css" rel="stylesheet">
    <link href="highlight/styles/github.css" rel="stylesheet">
    <link href="docsearch/docsearch.min.css" rel="stylesheet">
    <link href="css/site.css" rel="stylesheet">

    <title>Date picker - Metro 4 :: Popular HTML, CSS and JS library</title>

Table of contents

Date picker

About

Picker parts

Locale

Min & max years

Value

Events

Methods

Customize

Date picker

                    Use a date picker to let a user pick a single date value.

About

                    The date picker gives you a standardized way to let users pick a date value.
                    To create picker you must add role with attribute data-role="datepicker" to input element.

                            <input data-role="datepicker">

                    <input data-role="datepicker">

Picker parts

                    The picker consists of three parts: month, day and year.
                    With attributes data-month, data-day and data-year you can on/off each part.
                    For disabled parts, the picker will get value from current system date.

                            <input data-role="datepicker" data-day="false" data-year="false">

                            <input data-role="datepicker" data-day="false">

                            <input data-role="datepicker" data-year="false">

                    <input data-role="datepicker" data-day="false" data-year="false">
                    <input data-role="datepicker" data-day="false">
                    <input data-role="datepicker" data-year="false">

Locale

                    You can set locale for picker with attribute data-locale.

                            <input data-role="datepicker" data-locale="uk-UA">

                            <input data-role="datepicker" data-locale="de-DE">

                            <input data-role="datepicker" data-locale="hu-HU">

                    <input data-role="datepicker" data-locale="uk-UA">

Min & max years

                    You can set min and max years for picker. To set min and max years use attributes
                        data-min-year and data-max-year.

                            <input data-role="datepicker" data-min-year="2014" data-max-year="2020">

                    <input data-role="datepicker"  data-min-year="2014" data-max-year="2020">

Picker value

                    By default picker get current date from system for init value.
                    But you can set init date with attribute data-value.
                    Also you can change attribute data-value at runtime to change real value for component.
                    To get value you can read input value attribute or use special component methods.
                    To format output value for input, you can use attribute data-format. More about date format you can read from this article.

                    Important! Value for attribute data-value must be a valid javascript datetime string.
                    Read about valid date format this and this.

                            <input data-role="datepicker" data-value="1972-12-21">

                            <input data-role="datepicker" data-value="Mon, 25 Dec 1995">

                            <input data-role="datepicker" data-value="2018-02">

                    <input data-role="datepicker" data-value="1972-12-21">
                    <input data-role="datepicker" data-value="Mon, 25 Dec 1995">
                    <input data-role="datepicker" data-value="2018-02">

Events

                    When picker works, it generated a number of events. You can use callbacks to these events to interact with it.

                        Events
                        Data-*
                        Desc

                        onSet(val, elem_val, elem, picker)
                        data-on-set
                        Fired when value sets to picker

                        onOpen(val, elem, picker)
                        data-on-open
                        Fired when picker is open

                        onClose(val, elem, picker)
                        data-on-close
                        Fired when picker is close

                        onScroll(target, list, picker)
                        data-on-scroll
                        Fired when picker is scroll

                        onTimePickerCreate(elem, picker)
                        data-on-time-picker-create
                        Fired when picker was created

                    html:

                    <input data-role="datepicker" data-on-set="console.log(arguments)">
                    <input data-role="datepicker" data-on-set="myLib.date.set">

                    javascript:

                    var myLib = {
                        date: {
                            set: function(val){
                                console.log(val);
                            }
                        }
                    }

Methods

                    To interact with component you can use picker methods.

                        Method
                        Desc

                        open()
                        Use this method to open picker scroller

                        close()
                        Use this method to close picker scroller

                        val()
                        Use this method to get picker value as string

                        val(t)
                        Use this method to set picker value from string

                        date()
                        Use this method to get picker value as datetime object

                        date(t)
                        Use this method to set picker value from datetime object

Customize

                    You can use attribute data-distance to set scroller size.

<input data-role="datepicker" data-distance="1">

<input data-role="datepicker" data-distance="2">

<input data-role="datepicker" data-distance="3">

                    <input data-role="datepicker" data-distance="1">
                    <input data-role="datepicker" data-distance="2">
                    <input data-role="datepicker" data-distance="3">

                    Also you can use attributes
                    data-cls-picker,
                    data-cls-part,
                    data-cls-month,
                    data-cls-day,
                    data-cls-year to set additional styles to picker. Values for these attributes must be valid css class.

                            <input data-role="datepicker"
                                   data-cls-picker="drop-shadow"
                                   data-cls-month="bg-red fg-white"
                                   data-cls-day="bg-green fg-white"
                                   data-cls-year="bg-cyan fg-white"
                            >


text:
<?php

namespace WSW\SiftScience\Services;

use WSW\SiftScience\Client\Client;
use WSW\SiftScience\Credentials;
use WSW\SiftScience\Events\UpdateAccount;
use WSW\SiftScience\TestCase;

/**
 * Class UpdateAccountServiceTest
 *
 * @package WSW\SiftScience\Services
 * @author Ronaldo Matos Rodrigues <ronaldo@whera.com.br>
 */
class UpdateAccountServiceTest extends TestCase
{
    /**
     * @var Credentials;
     */
    private $credentials;

    protected function setUp()
    {
        parent::setUp();

        $this->credentials = $this->createMock(Credentials::class);
    }

    public function testUpdateAccountBuilderInstance()
    {
        $service = new UpdateAccountService($this->credentials);

        $this->assertInstanceOf(UpdateAccount::class, $service->updateAccountBuilder());
    }

    /**
     * @expectedException \WSW\SiftScience\Exceptions\SiftScienceRequestException
     */
    public function testUpdateAccountException()
    {
        $chargeback = new UpdateAccountService(new Credentials(''));
        $chargeback->update(new UpdateAccount());
    }

    public function testUpdateAccount()
    {
        $client = $this->createMock(Client::class);
        $client->expects($this->once())->method('post')->willReturn(true);

        $chargeback = new UpdateAccountService($this->credentials, $client);

        $this->assertTrue($chargeback->update(new UpdateAccount()));
    }
}


text:
using System;
using System.Threading;
using System.Threading.Tasks;

namespace Ofl.Search
{
    public interface IIndex : IDisposable
    {
        string Name { get; }

        Type Type { get; }

        Task<bool> ExistsAsync(CancellationToken cancellationToken);

        Task<IndexStats> GetStatsAsync(CancellationToken cancellationToken);

        Task CreateAsync(CancellationToken cancellationToken);

        Task DestroyAsync(CancellationToken cancellationToken);
    }
}


text:
using System;

namespace IndexLetters
{
    public class Arrays
    {
        public static void Main(string[] args)
        {
            string charString = Console.ReadLine().ToLower();

            char[] parsedChars = new char[charString.Length];

            int charToIntValueCorrector = 97;

            for (int i = 0; i < charString.Length; i++)
            {
                parsedChars[i] = charString[i];
                Console.WriteLine("{0} -> {1}",parsedChars[i],parsedChars[i] - charToIntValueCorrector);
            }
        }
    }
}


text:
layout: blog.html.ejs
title: EPUBTools 0.8.3 -- Adopting latest xmldom, TypeScript types
publicationDate: January 22, 2022
blogtag: news
tags:
    - EPUBTools
    - Milestone
teaser: |
    EPUBTools version 0.8.3 has been released, eliminating a security warning for the xmldom dependency.  This release also includes TypeScript type definitions files.
---

Running `npm audit` for EPUBTools gave a warning that the `xmldom` package contains a security vulnerability in version 0.6.0 and prior.  Unfortunately 0.6.0 is the last release for this package.

It turns out that the `xmldom` package is stuck in time because of a dispute with GitHub over who owns that project.  Instead, current releases of this package are published under the name `@xmldom/xmldom`.

For documentation see: https://www.npmjs.com/package/@xmldom/xmldom

The bottom line for EPUBTools is that I switched to the new package, `@xmldom/xmldom`, reran the test suite, and all tests passed.

As for the TypeScript declaration files.  I'm updating a book, _Quick Start to using Typescript and TypeORM in Node.js applications_, and in a moment of enthusiasm for TypeScript type definitions, went ahead and used the TypeScript tools to generate type definition files.  I may also rewrite EPUBTools in TypeScript, but that's a longer term project.

The type files are declared in `package.json` and will be automatically picked up by the appropriate tools.


text:
var overlayVisFilter = false;
var filledRelatedIds = false;
var wsurl = location.host+"/VmAmG-portlet/api/jsonws/";
$(document).ready(function() {
	getStaticCatRegEnt();
	getAppDetails(appIdToLoad);
});

_.templateSettings = {
		interpolate : /\{\{(.+?)\}\}/g
};

/*
 * enable social media two-click solution
 */

$('#socialshareprivacy').socialSharePrivacy();

function getStaticCatRegEnt() {
			/*
			 * load categories, entitlements and regions
			 */

			console.log(isWin8mobDevice());

			var isToucheable = false;

			if(accessedByMobileTouchDevice()) {
				isToucheable = true;
			}

			//no matter of localstorage, initialize fee and platform checkbox / multiselect

			if(isToucheable) {
				//fee
				$('#accordion3').empty().removeClass("accordion").append('<select size="1" id="filter_fees" class="multiselect_touch_filter" multiple="multiple">'+
						'<option value="--null--" selected>Kosten</option>'+
						'<option id="no_fee" value="fee_0" class="filter-checkbox">Kostenlos</option>'+
						'<option id="with_fee" value="fee_1" class="filter-checkbox">Kostenpflichtig</option>'+
						'</select>');

				//plattform

				$('#accordion4').empty().removeClass("accordion").append('<select size="1" id="filter_platforms" class="multiselect_touch_filter" multiple="multiple">'+
						'<option value="--null--" selected>Plattform</option>'+
//						'<optgroup label="Betriebssystem">'+
						'<option class="filter-checkbox" id="os_webapp" value="platform_webapp">WebApp</option>'+
						'<option class="filter-checkbox" id="os_android" value="platform_android">Android</option>'+
						'<option class="filter-checkbox" id="os_apple" value="platform_ios">Apple iOS</option>'+
						'<option class="filter-checkbox" id="os_blackberry" value="platform_blackberry">Blackberry</option>'+
						'<option class="filter-checkbox" id="os_ubuntu" value="platform_ubuntu">Ubuntu iOS</option>'+
						'<option class="filter-checkbox" id="os_win" value="platform_windows">Windows</option>'+
//						'</optgroup>'+
//						'<optgroup label="Ger�t">'+
//						'<option class="filter-checkbox" id="device_Smartphone" value="device_Smartphone">Smartphone</option>'+
//						'<option class="filter-checkbox" id="device_Tablet" value="device_Tablet">Tablet</option>'+
//						'</optgroup>'+
						'</select>');

			}

			if(!noCookiesAllowed()) {
				if(localStorage["localStoragePrepared"]=="true") {
				/* getCategories */
					var obj = JSON.parse(localStorage["allCategories"]);
					if(isToucheable) {
						$('#accordion2').empty().removeClass("accordion").append('<select size="1" id="categories" class="multiselect_touch_filter" multiple="multiple"><option value="--null--" selected>Themen</option>');
					}

				    for (var i = 0; i < obj.length; i++) {
				    	if(isToucheable) {
				    		$('#categories').append("<option class='filter-checkbox' value='category_"+ obj[i].categoryId +"' id='category"+obj[i].categoryId+"'>" + obj[i].categoryName + "</option>");
				    	} else {

				    		var categoryIcon = '';

							switch(obj[i].categoryId) {
							case 1:
								categoryIcon+='';
								break;
							case 2:
								categoryIcon+='';
								break;
							case 3:
								categoryIcon+='';
								break;
							case 4:
								categoryIcon+='';
								break;
							case 5:
								categoryIcon+='';
								break;
							case 6:
								categoryIcon+='';
								break;
							case 7:
								categoryIcon+='';
								break;
							case 8:
								categoryIcon+='';
								break;
							case 9:
								categoryIcon+='';
								break;
							case 10:
								categoryIcon+='';
								break;
							case 11:
								categoryIcon+='';
								break;
							case 12:
								categoryIcon+='';
								break;
							}

				    		$('#categories').append("
<input type='checkbox' class='filter-checkbox' value='category_"+ obj[i].categoryId +"' id='category"+obj[i].categoryId+"'>"
				    				+categoryIcon+" "
				    				+ obj[i].categoryName + "
");
				    	}
				    }

				    if(isToucheable) {
				    	$('#accordion2').append('</select>');
					}

				    /* getRegions */
				    obj = JSON.parse(localStorage["allRegions"]);
					if(isToucheable) {
						$('#accordion6').empty().removeClass("accordion").append('<select size="1" id="regions" class="multiselect_touch_filter" multiple="multiple"><option value="--null--" selected>Regionen</option>');
					}
					for (var i = 0; i < obj.length; i++) {
						if(isToucheable) {
							$('#regions').append("<option class='filter-checkbox' value='region_"+ obj[i].regionId +"' id='region"+obj[i].regionId+"'>" + obj[i].name + "</option>");
						} else {
							var regionIcon = '';

							switch(obj[i].regionId) {

								case 1:
									regionIcon+='';
									break;
								case 2:
									regionIcon+='';
									break;
								case 3:
									regionIcon+='';
									break;
								case 4:
									regionIcon+='';
									break;
								case 5:
									regionIcon+='';
									break;
								case 6:
									regionIcon+='';
									break;
								case 7:
									regionIcon+='';
									break;
								case 8:
									regionIcon+='';
									break;
								case 9:
									regionIcon+=' ';
									break;
								case 10:
									regionIcon+=' ';
									break;
								case 11:
									regionIcon+=' ';
									break;
								case 12:
									regionIcon+=' ';
									break;
								case 13:
									regionIcon+=' ';
									break;
								case 14:
									regionIcon+=' ';
									break;
								case 15:
									regionIcon+=' ';
									break;
								case 16:
									regionIcon+=' ';
									break;
								case 17:
									regionIcon+=' ';
									break;
							}

							$('#regions').append("
<input type='checkbox' class='filter-checkbox' value='region_"+ obj[i].regionId +"' id='region"+obj[i].regionId+"'>" +
									regionIcon+" "+	obj[i].name + "
");
						}
					}

					if(isToucheable) {
						$('#accordion6').append('</select>');
					}

					/* get Entitlements */

					if(isToucheable) {
						$('#accordion5').empty().removeClass("accordion").append('<select size="1" id="filter_entitlements" class="multiselect_touch_filter" multiple="multiple"><option value="--null--" selected>Berechtigungen</option>');
					}
					obj = JSON.parse(localStorage["allEntitlements"]);
					for(var i=0; i<obj.length; i++) {
						var entitlementTag='';
						if(!isToucheable) {

							switch (obj[i].entitlementId) {
			            	   case 1:
									//network communication
									//entitlementTag+=obj[i].explanation+'">';
									entitlementTag+=' ';
									break;
								case 2:
									//access media (cam, mic, vid)
									//entitlementTag+=obj[i].explanation+'">';
									entitlementTag+='';
									break;
								case 3:
									//access /vepa-theme/images/vepa-icons/Plattform/Icon-Web-App-Aktiv.png
									//entitlementTag+=obj[i].explanation+'">'
									entitlementTag+=' ';
									break;
								case 4:
									//system settings
									//entitlementTag+=obj[i].explanation+'">';
									entitlementTag+='';
									break;
								case 5:
									//requires money
									//entitlementTag+=obj[i].explanation+'">';
									entitlementTag+=' ';
									break;
								case 6:
									//personal data
									//entitlementTag+=obj[i].explanation+'">';
									entitlementTag+='';
									break;
								default:
									//entitlementTag+=obj[i].explanation+'">';
									entitlementTag+=' ';
									break;
		         	   		}
		                   $('#filter_entitlements').append("
<input type='checkbox' checked='true' class='filter-checkbox' value='entitlement_"+ obj[i].entitlementId +"' id='entitlement"+obj[i].entitlementId+"'>" + entitlementTag + ' ' +obj[i].entitlementName + "
");
						} else {
						   $('#filter_entitlements').append("<option class='filter-checkbox' selected='true' value='entitlement_"+ obj[i].entitlementId +"' id='entitlement"+obj[i].entitlementId+"'>" + entitlementTag + ' ' +obj[i].entitlementName + "</option>");
						}
					}

					if(isToucheable) {
						$('#accordion5').append('</select>');
						$("#filter_entitlements option[value='--null--'],#regions option[value='--null--'],#categories option[value='--null--'],#filter_fees option[value='--null--'], #filter_platforms option[value='--null--']").attr("disabled", true);
					}

				} else {
					doAjaxForFilterBuild();
				}
			} else {
				doAjaxForFilterBuild();
			}

			function doAjaxForFilterBuild() {
				console.log("H");
			}

			$(".filter_for_fee input:checkbox").change(function() {
				if($(this).attr("checked")!="true") {
					$(".filter_for_fee input:checkbox").attr("checked", false);
				    $(this).attr("checked", true);
				}
			});

		}

function getAppDetails(related) {

	//show details tab

	loadingBarStartFilter();

	/*
	 * reset tab settings:
	 */

	$('#app_detail_tabs').empty();
	$('#app_detail_tabs').append('
 Webapp
'
			+'
 Android
'
			+'
 iOS
'
			+'
 Blackberry
'
			+'
 Ubuntu
'
			+'
 Windows
');

	scrollTo(0, 0);

	$('.back_to_results').show();
	//TODO - customize for mobile view
	var appIds = new Array();
	var relatedApps = related.split(';');
	//alert("related apps: \""+related+"\"");
	var foundSimilarApps = true;

	var screensAvailable = false, simAppsAvailable = false;

	for(var y=0; y<relatedApps.length; y++) {

		appIds.push(relatedApps[y]);

	}

	for (var i=0; i<appIds.length; i++) {

		//alert("round "+i);
		/*
		 * each additional app details will be added within a tab;
		 * on mobile platform only respective details for current OS will be loaded
		 */
		$.ajax({
			type: 'GET',
			url: "/VmAmG-portlet/api/jsonws/application/get-full-application-details-eiscrlla/applicationId/"+appIds[i],
			async: false,
			success: function(data) {

				/*
				 * 1. for tab content pane, fill template and put it into content pane
				 *
				 * [0] -> list of entitlements
				 * [1] -> icon-URL
				 * [2] -> Screen shots
				 * [3] -> categories
				 * [4] -> regions
				 * [5] -> languages
				 * [6] -> application links (store, developer)
				 * [7] -> app_details like description, costs, last mod etc.
				 * [8] -> email address of the person that added this app to GOVAPPs
				 */

				var app_details = jQuery.parseJSON(data);

				//1. add tab button and content pane
				/*
				 * tabs are pre assigned:
				 * 0: Web-App
				 * 1: Android
				 * 2: iOS
				 * 3: Windows
				 */

				var app_icon_url='', app_screenshots='', app_categories='',
				app_regions='', app_langs='', app_sim_apps='',
				app_fee='', app_last_mod, app_description='', app_name='', app_size='', app_developer='',
				app_store_link='',app_screenshots_nav='', app_developer_link='',
				 multiple_screens='s', multiple_apps='s', app_publisher_link='', app_sim_apps_mobile='',
				app_screenshots_swipe='', app_creator_email_address = '', uses_open_data='';

				//get app costs and store name and targetdevice

				/*
				 * if app was directly accessed, look for related applicationIDs
				 */

				if(!filledRelatedIds) {
					var relatedAppIds = app_details[7].relatedApplicationId.split(";");
					for(var e=0; e<relatedAppIds.length; e++) {
						if(relatedAppIds[e].length>0) {
							console.log("related app id for ("+appIds[i]+"): "+relatedAppIds[e]);
							appIds.push(relatedAppIds[e]);
						}
					}
					filledRelatedIds = true;
				}

				app_fee = app_details[7].fee;

				if(app_fee == "0") {
					app_fee = "kostenlos";
				} else {
					app_fee = "kostenpflichtig";
				}
				switch(app_details[7].targetOS) {

					case "Android":
						app_target_os_storename = "Google Play Store";
						app_store_link ="http://play.google.com";
						break;
					case "Windows":
						app_target_os_storename = "Windows Phone Marketplace";
						app_store_link ="http://www.windowsphone.com/store";
						break;
					case "iOS":
						app_target_os_storename = "Apple App Store";
						app_store_link ="http://itunes.apple.com/de/genre/ios/id36?mt=8";
						break;
					case "Ubuntu":
						app_target_os_storename = "Ubuntu Apps Directory";
						app_store_link ="https://apps.ubuntu.com/cat/";
						break;
					case "BlackBerry":
						app_target_os_storename = "Blackberry Apps World";
						app_store_link ="http://de.blackberry.com/apps/app-world.html";
						break;
					default:
						app_target_os_storename = "Visit Web App...";
						break;
				}

				for(var q=0; q<app_details[6].length;q++) {
					switch(app_details[6][q].type) {
					case 2:
						//store link
						app_store_link = app_details[6][q].url;
						break;
					case 4:
						//site of publisher
						app_publisher_link = app_details[6][q].url;
						break;
					case 5:
						//site of developers
						app_developer_link = app_details[6][q].url;
						break;
					}

				}

				if(app_store_link.length==0) {
					$('.app_store_link_button').addClass("disabled");
				}

				//integrate click counter

				$('.app_store_link_button').unbind("click").click(function(event) {
					var appId = $(this).attr("value");

					Liferay.Service(
					  '/VmAmG-portlet#application/click-application-link',
					  {
					    applicationId: appId
					  },
					  function(obj) {
					    console.log("logged click on appstore");
					  }
					);
				});

				//get last modified date

				app_last_mod = new Date(app_details[7].lastModifiedDate).toLocaleDateString();

				//get app description

				/*
				 * process description: if < 300 chars -> OK, else: take 300 chars and make rest collapseable (TODO)
				 */

				app_description ='
'+app_details[7].description+'
';

				//get app name

				app_name = app_details[7].name;

				//get app size

				app_size = (""+app_details[7].size/1024+"").replace('.', ',').substring(0,5);

				//get developer

				app_creator_email_address = app_details[8];

				//
				var app_publisherText = '';

				if(app_details[7].legalDetails!=undefined && app_details[7].legalDetails.length>0) {
					app_publisherText = app_details[7].legalDetails;
				} else {
					app_publisherText = 'Es liegen keine Informationen zum Herausgeber vor';
				}

				if(app_creator_email_address!=undefined && app_creator_email_address.length>0) {
					app_creator_email_address=app_creator_email_address.replace("@", "[at]");
					app_developer = app_publisherText+'

Feedback senden';
				} else {
					app_developer = app_publisherText+'

Feedback senden';
				}

				//assign icon url
				app_icon_url = app_details[1].substring(0, app_details[1].length).replace("http://localhost", "");

				//set icon and name

				if(accessedBySmartphone()) {
					$('#app_details_heading').html('
'+app_name+'
');
				} else {
					$('#app_details_heading').html('
'+app_name+'
');
				}

				//assign screenshot urls
				var count_screenshots = app_details[2].length;
				if(count_screenshots>0) {
					screensAvailable = true;
				}

				for(var l=0; l<app_details[2].length; l++) {

					var screenshot_url = app_details[2][l].replace("http://localhost", "");

					if(l==0) {
						app_screenshots_swipe += '
'+'
';
					} else {
						app_screenshots_swipe +='
'+'
';
					}
				}

				if(app_screenshots_swipe.length==0) {
					app_screenshots_swipe="
Zu dieser App sind keine Screenshots vorhanden
";
				}

				if(app_screenshots_swipe.length==1) {
					multiple_screens="";
				}
				//app categories

				for(var m=0; m<app_details[3].length; m++) {

					switch(app_details[3][m].categoryId) {

					case 1:
						app_categories+=' ';
						break;
					case 2:
						app_categories+=' ';
						break;
					case 3:
						app_categories+=' ';
						break;
					case 4:
						app_categories+=' ';
						break;
					case 5:
						app_categories+=' ';
						break;
					case 6:
						app_categories+=' ';
						break;
					case 7:
						app_categories+=' ';
						break;
					case 8:
						app_categories+=' ';
						break;
					case 9:
						app_categories+=' ';
						break;
					case 10:
						app_categories+=' ';
						break;
					case 11:
						app_categories+=' ';
						break;
					case 12:
						app_categories+=' ';
						break;
					}

					if(m==0) {
						app_categories+=app_details[3][m].categoryName;
					} else {
						app_categories+=' '+app_details[3][m].categoryName;
					}

				}

				//app regions

				for(var n=0; n<app_details[4].length; n++) {

					if(n!=0) {
						app_regions+=', ';
					}

					switch(app_details[4][n].regionId) {

					case 1:
						app_regions+='';
						break;
					case 2:
						app_regions+='';
						break;
					case 3:
						app_regions+='';
						break;
					case 4:
						app_regions+='';
						break;
					case 5:
						app_regions+='';
						break;
					case 6:
						app_regions+='';
						break;
					case 7:
						app_regions+='';
						break;
					case 8:
						app_regions+='';
						break;
					case 9:
						app_regions+='';
						break;
					case 10:
						app_regions+='';
						break;
					case 11:
						app_regions+='';
						break;
					case 12:
						app_regions+='';
						break;
					case 13:
						app_regions+='';
						break;
					case 14:
						app_regions+='';
						break;
					case 15:
						app_regions+='';
						break;
					case 16:
						app_regions+='';
						break;
					case 17:
						app_regions+='';
						break;

					}
					if(n==0) {
						app_regions+=' '+app_details[4][n].name+'';
					} else {
						app_regions+=' '+app_details[4][n].name+'';
					}

				}

				//app languages

				for(var o=0; o<app_details[5].length; o++) {
					app_langs+=app_details[5][o].languageName+' ';
				}

				//get similar apps
				var uniqueApps = [];
				//alert("get similar apps for: \""+appIds[i]+"\"; i="+i);
				$.ajax( {

					type: 'GET',
					//change to url for similar apps
					url: "/VmAmG-portlet/api/jsonws/application/get-similar-applications/companyId/10154/applicationId/"+appIds[i]+"/byCategory/false/byRegion/true",
					async:false,
					success: function(data) {

						sim_apps_response = jQuery.parseJSON(data);

						//alert('There are '+sim_apps_response.length+' similar apps');

						if(sim_apps_response.length>0) {

							var relatedAppsNotToScan = [];

							var lastLengthUniqueApps = 0;

							var numberOfSliderItems = 4;
							var span_size = "span12";

							for(var p=0;p<sim_apps_response.length;p++) {

								var OSs ='';
								var app_icon_url='';

								if(relatedAppsNotToScan.indexOf(''+sim_apps_response[p][0].applicationId+'') == -1) {

									//alert('App  with ID#: '+sim_apps_response[p].applicationId+' is root app!');
									var currentRelatedApps=sim_apps_response[p][0].relatedApplicationId.split(';').sort();
									var related_appIds = ''+sim_apps_response[p][0].applicationId+'';
									if(sim_apps_response[p][0].relatedApplicationId.length>0) {
										related_appIds+=';'+''+sim_apps_response[p][0].relatedApplicationId+'';
									}

									//alert('Related apps from ID#: '+sim_apps_response[p].applicationId+':'+currentRelatedApps);
									var relatedAppsToScanForOs=[];
									uniqueApps.push(sim_apps_response[p][0].applicationId);

									/*
									 * if current app is related to other, add their IDs so that they do not get
									 * inspected again
									 * Furthermore: keep relations of apps
									 */

									if(currentRelatedApps.length>0) {
										for(var q=0;q<currentRelatedApps.length;q++) {
											//add related app to list of apps that need no inspection anymore
											relatedAppsNotToScan.push(currentRelatedApps[q]);
											//add related app to list to scan for respective OS
											relatedAppsToScanForOs.push(currentRelatedApps[q]);
										}

									}

									//get app-icon

									app_icon_url = sim_apps_response[p][1];
									app_icon_url = app_icon_url.replace("http://localhost", "");

									//alert('successfully got app_icon');

									//get OSs
									var webapp='', android='', ios='',blackberry='',ubuntu='', windows='';
									var tooltip_webapp='', tooltip_and='', tooltip_ios='',tooltip_blackberry='',tooltip_ubuntu='', tooltip_win='';

									switch(sim_apps_response[p][0].targetOS) {
										case "Android":
											android = "-Aktiv";

											break;
										case "Windows":
											windows = "-Aktiv";

											break;
										case "iOS":
											ios = "-Aktiv";

											break;
										case "BlackBerry":
											blackberry = "-Aktiv";

											break;
										case "Ubuntu":
											ubuntu = "-Aktiv";

											break;
										case "Webapp":
											webapp = "-Aktiv";

											break;
											default:
											osTag='OS';
											break;
									}

									//alert('Related apps from ID#: '+sim_apps_response[p].applicationId+' : '+relatedAppsToScanForOs+', length: '+relatedAppsToScanForOs.length);

									var alreadyScannedApps =[];

									var osWhichExistAsApp='';

									for (var r=0; r<sim_apps_response.length;r++) {

										if(_.include(relatedAppsToScanForOs, ''+sim_apps_response[r][0].applicationId+'') && (_.include(alreadyScannedApps, ''+sim_apps_response[r][0].applicationId+'')==false)) {
										//	alert('App  with ID#: '+sim_apps_response[r].applicationId+' has to be scanned!');
											alreadyScannedApps.push(''+sim_apps_response[r][0].applicationId+'');

											var osTag='';

											switch(sim_apps_response[r][0].targetOS) {
											case "Android":
												android = "-Aktiv";

												break;
											case "Windows":
												windows = "-Aktiv";

												break;
											case "iOS":
												ios = "-Aktiv";

												break;
											case "BlackBerry":
												blackberry = "-Aktiv";

												break;
											case "Ubuntu":
												ubuntu = "-Aktiv";

												break;
											case "Webapp":
												webapp = "-Aktiv";

												break;
												default:
											osTag='OS';
													break;
											}
										} else {
									//		alert(sim_apps_response[r].applicationId+' in alreadyScanned: '+_.include(alreadyScannedApps, ''+sim_apps_response[r].applicationId+''));
									//		alert(sim_apps_response[r].applicationId+' in appsToScanForOS: '+_.include(relatedAppsToScanForOs, ''+sim_apps_response[r].applicationId+''));
										}
									}

									//alert('successfully got OSes');

									//build template and append to list

									span_icon="span4", span_details="span8";

									if(accessedBySmartphone()) {
//										span_size = 'small_app_details';
										//icon_class = 'width="100%"';
										span_icon='small_span_icon';
										span_details='small_span_details';
									}

									var template_variables = {webapp:webapp,
											android:android,
											apple:ios,
											ubuntu:ubuntu,
											blackberry:blackberry,
											windows:windows,
											tooltip_win:tooltip_win,
											tooltip_and:tooltip_and,
											tooltip_ios:tooltip_ios,
											tooltip_blackberry:tooltip_blackberry,
											tooltip_ubuntu:tooltip_ubuntu,
											tooltip_webapp:tooltip_webapp,
											app_id: "detail_"+appIds[i]};
									OSs = _.template( $("#os_template").html(), template_variables);

									var variables = {span_icon:span_icon, span_details:span_details, span_size:span_size, app_name:sim_apps_response[p][0].name,
											app_id: sim_apps_response[p][0].applicationId,
											app_icon: app_icon_url, app_target_oss: OSs, related_ids:related_appIds};

									var template = _.template( $("#similar_app_template").html(), variables);
									app_sim_apps_mobile += '
'+template+'
';
									app_sim_apps+=template;
									relatedAppsNotToScan.push(''+sim_apps_response[p][0].applicationId+'');
								}
							};

						} else {
							foundSimilarApps = false;
						}

					},
					error: function(jqXHR, textStatus, errorThrown) {
						console.log("details-portlet, error textStatus: "+textStatus);
						console.log("details-portlet, error errorThrown: "+errorThrown);
					//	location.href="/fehler";
					}
				});

				if(!foundSimilarApps) {
					app_sim_apps_mobile = "
Zur Zeit gibt es keine weiteren Apps aus der Region
";
				}

				if(app_description.length>=300) {
					$('#description_accordion').unbind("click").click(function(event){
						$('#app_description_container').css("height", "auto");
					}) ;
				}

				var count_similar_apps = uniqueApps.length;
				if(count_similar_apps>0) {
					simAppsAvailable = true;
					if(count_similar_apps==1) {
						multiple_apps = '';
					}
				}

				//check for open data

				uses_open_data = '';
				if(app_details[7].useOpenData="1") {
					//Look for license
					var licenseName = app_details[7].license;
					console.log("look for equivalent to: "+licenseName);
					switch (app_details[7].license) {
					case "bsd-license":
						licenseName = "BSD Lizenz";
						break;
					case "apache":
						licenseName = "Apache Lizenz";
						break;
					case "app_commercial":
						licenseName = "Andere kommerzielle Lizenz";
						break;
					case "app_freeware":
						licenseName = "Andere kostenfreie Lizenz";
						break;
					case "app_opensource":
						licenseName = "Andere Open Source Lizenz";
						break;
					case "gpl-3.0":
						licenseName = "GNU General Public License version 3.0 (GPLv3)";
						break;
					case "mozilla":
						licenseName = "Mozilla Public License 1.0 (MPL)";
						break;
					case "other-open":
						licenseName = "Andere freie Lizenz";
						break;
					case "other-closed":
						licenseName = "Andere eingeschränkte Lizenz";
						break;
					default:
						licenseName ="Nicht definierte Lizenz";
						break;
					}
					// uses_open_data = '
 Nutzt offene Daten
 '+licenseName+'
';
				}

				var template_variables = {app_name: app_name,
						count_similar_apps: count_similar_apps,
						count_screenshots: count_screenshots,
						app_screenshots_swipe : app_screenshots_swipe,
						app_screenshots_nav : app_screenshots_nav,
						app_sim_apps_mobile : app_sim_apps_mobile,
						app_id: appIds[i],
						app_icon: app_icon_url,
						app_screenshots: app_screenshots,
						app_fee: app_fee,
						app_description: app_description,
						app_target_os_storename: app_target_os_storename,
						app_category: app_categories,
						app_update: app_last_mod,
						app_size: app_size,
						app_languages: app_langs,
						app_regions:app_regions,
						app_developer: app_developer ,
						multiple_screens: multiple_screens,
						multiple_apps: multiple_apps,
						app_store_link: app_store_link,
						app_version : app_details[7].targetOS,
						uses_open_data : uses_open_data
				};

				var template = _.template( $("#search_result_template").html(), template_variables);

				//append fully loaded html and react on different versions
				switch(app_details[7].targetOS) {

					case "Webapp":
						$('#app_detail_tabs a[href="#tab0"]').removeClass("disabled_ent").addClass("enabled");
						$('#tab0').html(template);
						$('#app_detail_tabs a[href="#tab0"]').attr("value", appIds[i]);
						break;
					case "Android":

							$('#app_detail_tabs a[href="#tab1"]').removeClass("disabled_ent").addClass("enabled");
							$('#tab1').html(template);
							$('#app_detail_tabs a[href="#tab1"]').attr("value", appIds[i]);

						break;
					case "iOS":

							$('#app_detail_tabs a[href="#tab2"]').removeClass("disabled_ent").addClass("enabled");
							$('#tab2').html(template);
							$('#app_detail_tabs a[href="#tab2"]').attr("value", appIds[i]);

						break;
					case "BlackBerry":

						$('#app_detail_tabs a[href="#tab3"]').removeClass("disabled_ent").addClass("enabled");
						$('#tab3').html(template);
						$('#app_detail_tabs a[href="#tab3"]').attr("value", appIds[i]);

					break;
					case "Ubuntu":

						$('#app_detail_tabs a[href="#tab4"]').removeClass("disabled_ent").addClass("enabled");
						$('#tab4').html(template);
						$('#app_detail_tabs a[href="#tab4"]').attr("value", appIds[i]);

					break;
					case "Windows":
							$('#app_detail_tabs a[href="#tab5"]').removeClass("disabled_ent").addClass("enabled");
							$('#tab5').html(template);
							$('#app_detail_tabs a[href="#tab5"]').attr("value", appIds[i]);
						break;
				}

				if(app_details[2].length<=1) {
					$('#screenshot_navigation'+appIds[i]).empty();
				}

				$('.region_link').unbind("click").click(function(event) {
					filter = "entitlement_1;entitlement_2;entitlement_3;entitlement_4;entitlement_5;entitlement_6;"+event.target.id;
					console.log('clear previous search results');

					/*
					 * modify form declared in suche-portlet and submit!
					 */
					document.getElementById('filter').value=filter;
					document.setFilter.submit();

				});

				$('.safe_mail_button').unbind("click").click(function(event) {
					if(!$('.safe_mail_button').hasClass("disabled")) {
						var theMailAddress = $(this).attr("value");
						theMailAddress = theMailAddress.replace("[mail]", "mailto:").replace("[at]", "@");
						location.href = theMailAddress;
					}
				});

				//fill entitlements
				for (var k = 0; k < app_details[0].length; k++) {

					switch (app_details[0][k].entitlementId) {
						case 1:
							//alert('remove opacitiy from ent: '+app_details[0][k].entitlementId);
							$('#det_ent_network'+appIds[i]).removeClass('disabled_ent');
							$('#ent_net_tooltip'+appIds[i]).attr("title", app_details[0][k].motivation);
							break;
						case 2:
							//access media (cam, mic, vid)

							$('#det_ent_media_access'+appIds[i]).removeClass('disabled_ent');
							$('#ent_med_tooltip'+appIds[i]).attr("title", app_details[0][k].motivation);
							break;
						case 3:
							//access /vepa-theme/images/vepa-icons/Plattform/Icon-Web-App-Aktiv.png

							$('#det_ent_directions'+appIds[i]).removeClass('disabled_ent');
							$('#ent_pla_tooltip'+appIds[i]).attr("title", app_details[0][k].motivation);
							break;
						case 4:
							//system settings

							$('#det_ent_system_settings'+appIds[i]).removeClass('disabled_ent');
							$('#ent_sys_tooltip'+appIds[i]).attr("title", app_details[0][k].motivation);
							break;
						case 5:
							//requires money

							$('#det_ent_money'+appIds[i]).removeClass('disabled_ent');
							$('#ent_fee_tooltip'+appIds[i]).attr("title", app_details[0][k].motivation);
							break;
						case 6:
							//personal data

							$('#det_ent_personal'+appIds[i]).removeClass('disabled_ent');
							$('#ent_per_tooltip'+appIds[i]).attr("title", app_details[0][k].motivation);
							break;
						default:
							break;
					}
				}

				//alert('successfully filled template');
			},
			error: function(jqXHR, textStatus, errorThrown) {
				console.log("details-portlet, error textStatus: "+textStatus);
				console.log("details-portlet, error errorThrown: "+errorThrown);
				//location.href="/fehler";
			}
		});
	}

	//activate first tab and deactivate other tabs

	var chosenAppIdToShow = 0;

	if(!accessedByMobileTouchDevice() && !isIpad()) {
		$('.ent_tooltip').tooltip();

		$('#app_detail_tabs .enabled:first').tab('show');

		chosenAppIdToShow = $('#app_detail_tabs .enabled:first').attr("value");
	} else {

		$('.ent_tooltip').tooltip({
			trigger:'manual',
			delay: { show: 100, hide: 100 }
		});

		$('.ent_tooltip').each(function(index) {
			var currTooltip = $(this);
			currTooltip.toggle(function (){
				currTooltip.tooltip('show');
				$('body').on('touchend', function(e) {
					currTooltip.click();
					e.stopPropagation();
				});
			}, function() {
				currTooltip.tooltip('hide');
				$('body').unbind('touchend');
			});
		});

		var deviceList = ["iphone", "ipod", "ipad",
		                  	"android","windows ce","windows phone"];

		var i=0;
		var osFoundForTab = false;

		while(i<deviceList.length && !osFoundForTab) {

			var userAgentIndex = navigator.userAgent.toLowerCase().search(deviceList[i]);
			if(userAgentIndex!=-1) {
				switch(i) {
					case 0:
						if($('#app_detail_tabs a[href="#tab2"]').hasClass("enabled")) {

							$('#app_detail_tabs a[href="#tab2"]').tab('show');
							chosenAppIdToShow = $('#app_detail_tabs a[href="#tab2"]').attr("value");
							osFoundForTab = true;
						}
						break;
					case 1:
						if($('#app_detail_tabs a[href="#tab2"]').hasClass("enabled")) {

							$('#app_detail_tabs a[href="#tab2"]').tab('show');
							chosenAppIdToShow = $('#app_detail_tabs a[href="#tab2"]').attr("value");
							osFoundForTab = true;
						}
						break;
					case 2:
						if($('#app_detail_tabs a[href="#tab2"]').hasClass("enabled")) {
							$('#app_detail_tabs a[href="#tab2"]').tab('show');
							chosenAppIdToShow = $('#app_detail_tabs a[href="#tab2"]').attr("value");
							osFoundForTab = true;
						}
						break;
					case 3:
						if($('#app_detail_tabs a[href="#tab1"]').hasClass("enabled")) {
							$('#app_detail_tabs a[href="#tab1"]').tab('show');

							chosenAppIdToShow = $('#app_detail_tabs a[href="#tab1"]').attr("value");
							osFoundForTab = true;
						}
						break;
					case 4:
					if($('#app_detail_tabs a[href="#tab3"]').hasClass("enabled")) {
							$('#app_detail_tabs a[href="#tab3"]').tab('show');

							chosenAppIdToShow = $('#app_detail_tabs a[href="#tab3"]').attr("value");
							osFoundForTab = true;
						}
						break;
					case 5:
					if($('#app_detail_tabs a[href="#tab3"]').hasClass("enabled")) {
							$('#app_detail_tabs a[href="#tab3"]').tab('show');

							chosenAppIdToShow = $('#app_detail_tabs a[href="#tab3"]').attr("value");
							osFoundForTab = true;
						}
						break;
					default:
						$('#app_detail_tabs .enabled:first').tab('show');

						chosenAppIdToShow = $('#app_detail_tabs .enabled:first').attr("value");
						break;
				}
			} else {
				$('#app_detail_tabs .enabled:first').tab('show');

				chosenAppIdToShow = $('#app_detail_tabs .enabled:first').attr("value");
			}
			i++;
		}

	}

	if(simAppsAvailable) {
		$('#sim-apps-slider'+chosenAppIdToShow).ready(function() {
			$('#sim-apps-slider'+chosenAppIdToShow).flexslider({
				animation: "slide",
			//	useCSS:false,
	            itemWidth: 245,
	            itemMargin: 0,
	            minItems: 1,
	            maxItems: 4,
	            move:1,
	            slideshow:false,
	            controlNav:false
			});
		});

	}

	if(screensAvailable) {
		$('#screens-slider'+chosenAppIdToShow).ready(function() {
			$('#screens-slider'+chosenAppIdToShow).flexslider({
				animation: "slide",
				//useCSS:false,
	            itemWidth: 250,
	            itemMargin: 5,
	            minItems: 1,
	            maxItems: 2,
	            move:1,
	            slideshow:false
			});
		});

	}

	console.log("added screenshot/simapps slider");

	if(screensAvailable || simAppsAvailable) {
		if(isWin8mobDevice()) {
			$(".flex-direction-nav li").show();
			$(".flex-direction-nav li a").css('opacity','1');
		}
	}

	$('.osTab').unbind("click").click(function(event) {
		console.log("clicked tab for Id: "+ $(this).attr("value") );
		if(screensAvailable) {
				$('#screens-slider'+$(this).attr("value")).flexslider({
					animation: "slide",
					//useCSS:false,
		            itemWidth: 250,
		            itemMargin: 5,
		            minItems: 1,
		            maxItems: 2,
		            move:1,
		            slideshow:false
				});
		}
		if(simAppsAvailable) {
			$('#sim-apps-slider'+$(this).attr("value")).flexslider({
					animation: "slide",
					//useCSS:false,
		            itemWidth: 245,
		            itemMargin: 0,
		            minItems: 1,
		            maxItems: 4,
		            move:1,
		            slideshow:false,
		            controlNav:false
				});
		}

		if(isWin8mobDevice()) {
			$(".flex-direction-nav li").show();
			$(".flex-direction-nav li a").css('opacity','1');
		}

	});

	console.log("enabled sliders for all app version pages");

	$('#app_detail_tabs .disabled_ent').removeAttr("href");

	loadingBarStopFilter();

};

function loadingBarStartFilter() {
    if (overlayVisFilter == false) {
           console.log("SHOWING Filter SPINNER");
           $("#app-details").css("visibility","hidden");
           $("#app-details-container").append("

Inhalte werden geladen ...
");
           overlayVisFilter = true;
    }
}

function loadingBarStopFilter() {
    if (overlayVisFilter == true) {
 	   	console.log("REMOVING Filter SPINNER");
 	   	 $("#app-details").css("visibility","visible");
         $("#overlay_filter").remove();
         if($("#app-details").css("visibility")!="visible") {
        	 $("#app-details").css("visibility", "visible");
         }

         overlayVisFilter = false;
    }
}


text:
var panelsUtil = {

	/**
	 * Add click events to the template that was rendered
	 * @param  {String}      the rendered template for the modal
	 * @param  {String}      the pattern partial for the modal
	 */
	addClickEvents: function (templateRendered, patternPartial) {

		var els = templateRendered.querySelectorAll('.pl-js-tab-link');
		for (var i = 0; i < els.length; ++i) {
			els[i].onclick = function (e) {
				e.preventDefault();

				var patternPartial = this.getAttribute('data-patternpartial');
				var panelID = this.getAttribute('data-panelid');
				panelsUtil.show(patternPartial, panelID);
			};
		}

		return templateRendered;

	},

	/**
	 * Show a specific modal
	 * @param  {String}      the pattern partial for the modal
	 * @param  {String}      the ID of the panel to be shown
	 */
	show: function (patternPartial, panelID) {

		var els;

		// turn off all of the active tabs
		els = document.querySelectorAll('#pl-' + patternPartial + '-tabs .pl-js-tab-link');
		for (i = 0; i < els.length; ++i) {
			els[i].classList.remove('pl-is-active-tab');
		}

		// hide all of the panels
		els = document.querySelectorAll('#pl-' + patternPartial + '-panels .pl-js-tab-panel');
		for (i = 0; i < els.length; ++i) {
			els[i].classList.remove('pl-is-active-tab');
		}

		// add active tab class
		document.getElementById('pl-' + patternPartial + '-' + panelID + '-tab').classList.add('pl-is-active-tab');

		// show the panel
		document.getElementById('pl-' + patternPartial + '-' + panelID + '-panel').classList.add('pl-is-active-tab');

	}

};


text:
<?php

class TestApplication extends Nice\Application
{

}


text:
import {SafeObject} from '../Isolation/safe-object.js';

const {keys, entries, assign} = SafeObject;

export const RecipeBuilder = {
  defaultContainer: 'main#graph',
  idDelim: ':',
  connectorDelim: '$$',
  state: {
    storeMap: {}
  },

  constructId(id, name) {
    return `${id ? `${id}${this.idDelim}` : ''}${name}`;
  },

  construct({graph, nodeTypes, layout}) {
    return this.recipesForGraph({graph, nodeTypes, layout}, this.state)
  },

  recipesForGraph(inputs, state) {
    return entries(inputs.graph.nodes)
      .map(([n, v]) => ({id: n, ...v}))
      .map(node => this.recipeForNode(node, inputs, state))
      .filter(recipe => recipe)
      ;
  },

  recipeForNode(node, {graph, nodeTypes, layout}, state) {
    const nodeType = this.flattenNodeType(nodeTypes?.[node?.type]);
    return !nodeType ? null : {
      $stores: this.buildStoreSpecs(node, nodeType, state),
      $meta: {
        name: this.encodeFullNodeId(node, graph, this.connectorDelim),
        custom: nodeType.$meta?.custom
      },
      ...this.buildParticleSpecs(node, nodeType, layout, state)
    };
  },

  flattenNodeType(nodeType, $container) {
    const flattened = {};
    keys(nodeType).forEach(key => {
      if (key.startsWith('$')) {
        flattened[key] = nodeType[key];
      } else {
        assign(flattened, this.flattenParticleSpec(key, nodeType[key], $container));
      }
    });
    return flattened;
  },

  flattenParticleSpec(particleId, particleSpec, $container) {
    const flattened = {
      [particleId]: {
        ...particleSpec,
        $slots: {},
        ...($container && {$container})
      }
    };
    entries(particleSpec.$slots || {}).forEach(([slotId, slotRecipe]) => {
      assign(flattened, this.flattenNodeType(slotRecipe, `${particleId}#${slotId}`));
    });
    return flattened;
  },

  /* */

  buildStoreSpecs(node, nodeType, state) {
    const specs = {};
    entries(nodeType?.$stores).forEach(([name, store]) => {
      state.storeMap[name] = [];
      if (store.connection) {
        const connections = node.connections?.[name];
        connections?.forEach(connection => state.storeMap[name].push(connection));
      } else {
        const storeId = this.constructId(node.id, name);
        specs[storeId] = this.buildStoreSpec(store, node.props?.[name], node);
        state.storeMap[name].push(storeId);
      }
    });
    return specs;
  },

  buildStoreSpec(store, value, node) {
    return {
      $type: store.$type,
      $value: this.formatStoreValue(store, value, node)
    };
  },

  /* */

  buildParticleSpecs(node, nodeType, layout, {storeMap}) {
    const specs = {};
    const containerId = this.constructId(node.id, 'Container')
    const container = layout?.[containerId] || this.defaultContainer;
    const names = this.getParticleNames(nodeType) || [];
    for (const particleName of names) {
      const particleId = this.constructId(node.id, particleName);
      specs[particleId] = this.buildParticleSpec(node, nodeType, particleName, container, storeMap);
    }
    return specs;
  },

  getParticleNames(recipe) {
    const notKeyword = name => !name.startsWith('$');
    return recipe && keys(recipe).filter(notKeyword);
  },

  buildParticleSpec(node, nodeType, particleName, container, storeMap) {
    const particleSpec = nodeType[particleName];
    const $container = this.resolveContainer(node.id, particleSpec.$container, container);
    const bindings = this.resolveBindings(particleSpec, storeMap);
    const resolvedSpec = {
      $slots: {},
      ...particleSpec,
      ...bindings,
      ...($container && {$container})
    };
    return resolvedSpec;
  },

  resolveContainer(nodeName, containerName, defaultContainer) {
    return containerName ? this.constructId(nodeName, containerName) : defaultContainer;
  },

  resolveBindings(particleSpec, storeMap) {
    const {$inputs, $outputs} = particleSpec;
    return {
      $inputs: this.resolveIoGroup($inputs, storeMap),
      $outputs: this.resolveIoGroup($outputs, storeMap),
    };
  },

  resolveIoGroup(bindings, storeMap) {
    return bindings?.map(coded => {
      const {key, binding} = this.decodeBinding(coded);
      const task = (store, index) => ({[`${key}${index === 0 ? '' : index}`]: store});
      return storeMap[binding || key]?.map(task);
    }).flat().filter(i=>i);
  },

  decodeBinding(value) {
    if (typeof value === 'string') {
      return {key: value, binding: ''};
    } else {
      const [key, binding] = entries(value)[0];
      return {key, binding};
    }
  },

  formatStoreValue(store, value, node) {
    if (value !== undefined) {
      return value;
    }
    // TODO(mariakleiner): Revisit how to express special default values supported by RecipeBuilder.
    if (store.value === 'node.id') {
      return node.id;
    }
    return store.$value;
  },

  encodeFullNodeId({id}, {$meta}, delimiter) {
    return [$meta?.id, id].filter(i=>i).join(delimiter);
  }
};


text:
window.onload = () => {
	const text = ["Renewable energy? I'm a big fan",
	              'Keep an even kale',
	              'Herb your enthusiasm',
	              'Visualize whirled peas',
	              'These are tearable puns',
	              'Air pollution is a mist-demeanor',
	              'Wasting paper cups is high tree sin',
	              'Get green-shirtified',
	              "I'd like to spend more thyme with you",
	              'A power outage at school often leaves the students delighted!',
	              'All this talk about wind energy really blew me away!',
	              'Q: Why did the leaf go to the doctor? A: Because it was feeling green',
	              'Q: How can you tell the ocean is friendly? A: It waves',
	              'Q: How do trees access the internet? A: They log on',
	              'Q: How many tickles does it take to make a squid laugh? A: Ten tickles',
	              "Q: What's the name of the new Tom Cruise eco-thriller? A: Mission Compostable",
	              'Q: Why do potatoes make good detectives? A: Because they keep their eyes peeled.'];
	const x = Math.floor(Math.random() * text.length);
	$('#pun').html(text[x]);
};

$(function () {
	$('[data-toggle="tooltip"]').tooltip();
});